Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
authorJim Procter <j.procter@dundee.ac.uk>
Wed, 14 Dec 2022 16:01:09 +0000 (16:01 +0000)
committerJim Procter <j.procter@dundee.ac.uk>
Wed, 14 Dec 2022 16:01:09 +0000 (16:01 +0000)
 Conflicts:
THIRDPARTYLIBS
build.gradle
gradle.properties
help/help/html/features/preferences.html
help/help/html/releases.html
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/datamodel/SequenceI.java
src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureEditor.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/OptsAndParamsPage.java
src/jalview/gui/OverviewPanel.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/SplashScreen.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/UserQuestionnaireCheck.java
src/jalview/gui/VamsasApplication.java
src/jalview/gui/WsJobParameters.java
src/jalview/gui/WsParamSetManager.java
src/jalview/io/BackupFiles.java
src/jalview/io/ModellerDescription.java
src/jalview/io/NewickFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StockholmFile.java
src/jalview/javascript/log4j/Logger.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPreferences.java
src/jalview/project/Jalview2XML.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/urls/IdentifiersUrlProvider.java
src/jalview/util/HttpUtils.java
src/jalview/util/MappingUtils.java
src/jalview/util/MessageManager.java
src/jalview/util/Platform.java
src/jalview/util/StringUtils.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/ws/AWSThread.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/gui/MsaWSThread.java
src/jalview/ws/jws1/Discoverer.java
src/jalview/ws/jws1/JPredClient.java
src/jalview/ws/jws1/JPredThread.java
src/jalview/ws/jws2/AADisorderClient.java
src/jalview/ws/jws2/AbstractJabaCalcWorker.java
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/Jws2Discoverer.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/rest/HttpResultSet.java
src/jalview/ws/rest/RestClient.java
src/jalview/ws/rest/RestJobThread.java
src/jalview/ws/utils/UrlDownloadClient.java
src/jalview/xml/binding/jalview/DoubleVector.java
src/jalview/xml/binding/jalview/JalviewModel.java
test/jalview/bin/CommandLineOperations.java
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AlignmentPanelTest.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/AnnotationRowFilterTest.java
test/jalview/gui/JvSwingUtilsTest.java
test/jalview/gui/SeqCanvasTest.java
test/jalview/gui/SequenceRendererTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/io/SequenceAnnotationReportTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/renderer/OverviewResColourFinderTest.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
test/jalview/schemes/ClustalxColourSchemeTest.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/util/ColorUtilsTest.java
test/jalview/workers/AlignCalcManagerTest.java
test/jalview/ws/gui/Jws2ParamView.java
test/jalview/ws/sifts/SiftsClientTest.java

527 files changed:
THIRDPARTYLIBS
build-libjs.xml [new file with mode: 0644]
build.gradle
doc/JalviewJS-API.md [new file with mode: 0644]
doc/JalviewJS-startupParams.md [new file with mode: 0644]
doc/JalviewJS-startupParams.xlsx [new file with mode: 0644]
doc/addingWebClient.md [new file with mode: 0644]
doc/building.html
examples/groovy/hmmertestimport.groovy [new file with mode: 0644]
examples/testdata/hmmer3/alignment_frag.fa.gz [new file with mode: 0644]
examples/testdata/hmmer3/alignment_res.fa.gz [new file with mode: 0644]
examples/testdata/hmmer3/hit_fragment.json.gz [new file with mode: 0644]
examples/testdata/hmmer3/hmmeresult.json.gz [new file with mode: 0644]
examples/uniref50.hmm [new file with mode: 0644]
gradle.properties
help/help/help.jhm
help/help/helpTOC.xml
help/help/html/features/preferences.html
help/help/html/menus/alwhmmer.html [new file with mode: 0644]
j11lib/Jmol-15.1.3.jar [new file with mode: 0644]
j11lib/apache-mime4j-0.6.jar [deleted file]
j11lib/apache-mime4j-core-0.8.3.jar [new file with mode: 0644]
j11lib/apache-mime4j-dom-0.8.3.jar [new file with mode: 0644]
j11lib/httpclient-4.0.3.jar [deleted file]
j11lib/httpclient-4.5.6.jar [new file with mode: 0644]
j11lib/httpcore-4.0.1.jar [deleted file]
j11lib/httpcore-4.4.10.jar [new file with mode: 0644]
j11lib/httpmime-4.0.3.jar [deleted file]
j11lib/httpmime-4.5.6.jar [new file with mode: 0644]
j11lib/java-json.jar [deleted file]
j11lib/json-20180130.jar [new file with mode: 0644]
j11lib/slivka-client.jar [new file with mode: 0644]
j8lib/Jmol-15.1.3.jar [new file with mode: 0644]
j8lib/apache-mime4j-core-0.8.3.jar [new file with mode: 0644]
j8lib/apache-mime4j-dom-0.8.3.jar [new file with mode: 0644]
resources/ProbabilityOfMatch [new file with mode: 0644]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
schemas/jalview.xsd
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignmentAnnotationUtils.java
src/jalview/analysis/AlignmentSorter.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/CrossRef.java
src/jalview/analysis/Dna.java
src/jalview/analysis/Finder.java
src/jalview/analysis/ParseProperties.java
src/jalview/analysis/SeqsetUtils.java
src/jalview/analysis/scoremodels/ScoreModels.java
src/jalview/api/AlignCalcListener.java [new file with mode: 0644]
src/jalview/api/AlignCalcManagerI.java [deleted file]
src/jalview/api/AlignCalcManagerI2.java [new file with mode: 0644]
src/jalview/api/AlignCalcWorkerI.java
src/jalview/api/AlignViewportI.java
src/jalview/api/FeatureSettingsModelI.java
src/jalview/api/JalviewJSApi.java [new file with mode: 0644]
src/jalview/api/PollableAlignCalcWorkerI.java [new file with mode: 0644]
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/RedundancyPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/TitledPanel.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/appletgui/js/JSFunctionExec.java [moved from src/jalview/javascript/JSFunctionExec.java with 99% similarity]
src/jalview/appletgui/js/JalviewLiteJsApi.java [moved from src/jalview/javascript/JalviewLiteJsApi.java with 99% similarity]
src/jalview/appletgui/js/JsCallBack.java [moved from src/jalview/javascript/JsCallBack.java with 97% similarity]
src/jalview/appletgui/js/JsSelectionSender.java [moved from src/jalview/javascript/JsSelectionSender.java with 99% similarity]
src/jalview/appletgui/js/MouseOverListener.java [moved from src/jalview/javascript/MouseOverListener.java with 99% similarity]
src/jalview/appletgui/js/MouseOverStructureListener.java [moved from src/jalview/javascript/MouseOverStructureListener.java with 99% similarity]
src/jalview/bin/AppletParams.java [new file with mode: 0644]
src/jalview/bin/ApplicationSingletonProvider.java [new file with mode: 0644]
src/jalview/bin/ArgsParser.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewJS2.java
src/jalview/bin/JalviewJSApp.java [new file with mode: 0644]
src/jalview/bin/JalviewLite.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/AlignmentOrder.java
src/jalview/datamodel/AlignmentView.java
src/jalview/datamodel/AnnotatedCollectionI.java
src/jalview/datamodel/HMMNode.java [new file with mode: 0644]
src/jalview/datamodel/HiddenMarkovModel.java [new file with mode: 0644]
src/jalview/datamodel/PDBEntry.java
src/jalview/datamodel/ResidueCount.java
src/jalview/datamodel/SeqCigar.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceCollectionI.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/datamodel/SequenceI.java
src/jalview/datamodel/features/FeatureAttributes.java
src/jalview/datamodel/features/FeatureSources.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/ext/ensembl/EnsemblCdna.java
src/jalview/ext/ensembl/EnsemblCds.java
src/jalview/ext/ensembl/EnsemblFeatures.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblProtein.java
src/jalview/ext/ensembl/EnsemblSeqProxy.java
src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
src/jalview/ext/ensembl/EnsemblXref.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
src/jalview/gui/AlignExportOptions.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationChooser.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AnnotationRowFilter.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/AssociatePdbFileWithSeq.java
src/jalview/gui/CalculationChooser.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/ColourMenuHelper.java
src/jalview/gui/CrossRefAction.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureEditor.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Finder.java
src/jalview/gui/FontChooser.java
src/jalview/gui/IProgressIndicator.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/JalviewChangeSupport.java
src/jalview/gui/JalviewDialog.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/LineartOptions.java
src/jalview/gui/OOMWarning.java
src/jalview/gui/OptsAndParamsPage.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/PaintRefresher.java
src/jalview/gui/PairwiseAlignPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/ProgressBar.java
src/jalview/gui/PromptUserConfig.java
src/jalview/gui/PymolViewer.java
src/jalview/gui/RedundancyPanel.java
src/jalview/gui/RestInputParamEditDialog.java
src/jalview/gui/RestServiceEditorPane.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/SliderPanel.java
src/jalview/gui/SlivkaPreferences.java [new file with mode: 0644]
src/jalview/gui/SplashScreen.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/StructureViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/UserQuestionnaireCheck.java
src/jalview/gui/VamsasApplication.java
src/jalview/gui/WebserviceInfo.java
src/jalview/gui/WsJobParameters.java
src/jalview/gui/WsParamSetManager.java
src/jalview/gui/WsPreferences.java
src/jalview/hmmer/HMMAlign.java [new file with mode: 0644]
src/jalview/hmmer/HMMBuild.java [new file with mode: 0644]
src/jalview/hmmer/HMMERParamStore.java [new file with mode: 0644]
src/jalview/hmmer/HMMERPreset.java [new file with mode: 0644]
src/jalview/hmmer/HMMSearch.java [new file with mode: 0644]
src/jalview/hmmer/HmmerCommand.java [new file with mode: 0644]
src/jalview/hmmer/JackHMMER.java [new file with mode: 0644]
src/jalview/hmmer/Search.java [new file with mode: 0644]
src/jalview/httpserver/HttpServer.java
src/jalview/io/AlignFile.java
src/jalview/io/AlignmentFileReaderI.java
src/jalview/io/AlignmentFileWriterI.java
src/jalview/io/AnnotationFile.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BSMLFile.java [new file with mode: 0644]
src/jalview/io/BackupFiles.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/CountReader.java [new file with mode: 0644]
src/jalview/io/FileFormat.java
src/jalview/io/FileFormats.java
src/jalview/io/FileLoader.java
src/jalview/io/HMMFile.java [new file with mode: 0644]
src/jalview/io/IdentifyFile.java
src/jalview/io/ModellerDescription.java
src/jalview/io/NewickFile.java
src/jalview/io/RnamlFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StockholmFile.java
src/jalview/io/VamsasAppDatastore.java
src/jalview/io/WSWUBlastClient.java
src/jalview/io/cache/AppCache.java
src/jalview/io/gff/Gff3Helper.java
src/jalview/io/gff/InterProScanHelper.java
src/jalview/io/gff/SequenceOntologyFactory.java
src/jalview/io/packed/JalviewDataset.java
src/jalview/io/packed/ParsePackedSet.java
src/jalview/io/vamsas/Sequencemapping.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPCAPanel.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GRestInputParamEditDialog.java
src/jalview/jbgui/GRestServiceEditorPane.java
src/jalview/jbgui/GSequenceLink.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/jbgui/GTreePanel.java
src/jalview/jbgui/GUserDefinedColours.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/renderer/ResidueShaderI.java
src/jalview/rest/RestHandler.java
src/jalview/schemes/AnnotationColourGradient.java
src/jalview/schemes/ColourSchemes.java
src/jalview/schemes/FeatureSettingsAdapter.java
src/jalview/schemes/HMMMatchScoreColourScheme.java [new file with mode: 0644]
src/jalview/schemes/HmmerColourScheme.java [new file with mode: 0644]
src/jalview/schemes/HmmerGlobalBackground.java [new file with mode: 0644]
src/jalview/schemes/HmmerLocalBackground.java [new file with mode: 0644]
src/jalview/schemes/JalviewColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/structure/StructureImportSettings.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/urls/IdOrgSettings.java
src/jalview/urls/IdentifiersUrlProvider.java
src/jalview/util/BrowserLauncher.java
src/jalview/util/ColorUtils.java
src/jalview/util/Comparison.java
src/jalview/util/DBRefUtils.java
src/jalview/util/FileUtils.java [new file with mode: 0644]
src/jalview/util/HMMProbabilityDistributionAnalyser.java [new file with mode: 0644]
src/jalview/util/HttpUtils.java
src/jalview/util/MapList.java
src/jalview/util/MappingUtils.java
src/jalview/util/MessageManager.java
src/jalview/util/Platform.java
src/jalview/util/ProbabilityAnalyserKickstarter.java [new file with mode: 0644]
src/jalview/util/ShortcutKeyMaskExWrapper.java
src/jalview/util/ShortcutKeyMaskExWrapperI.java
src/jalview/util/StringUtils.java
src/jalview/util/jarInputStreamProvider.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java
src/jalview/viewmodel/OverviewDimensionsHideHidden.java
src/jalview/viewmodel/OverviewDimensionsShowHidden.java
src/jalview/viewmodel/ViewportRanges.java
src/jalview/workers/AlignCalcManager.java [deleted file]
src/jalview/workers/AlignCalcManager2.java [new file with mode: 0644]
src/jalview/workers/AlignCalcWorker.java
src/jalview/workers/AnnotationWorker.java
src/jalview/workers/ColumnCounterSetWorker.java
src/jalview/workers/ConsensusThread.java
src/jalview/workers/ConservationThread.java
src/jalview/workers/InformationThread.java [new file with mode: 0644]
src/jalview/workers/StrucConsensusThread.java
src/jalview/ws/AWSThread.java
src/jalview/ws/AWsJob.java
src/jalview/ws/JobStateSummary.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/SequenceFetcherFactory.java [deleted file]
src/jalview/ws/ServiceChangeListener.java [new file with mode: 0644]
src/jalview/ws/WSClient.java
src/jalview/ws/WSDiscovererI.java [new file with mode: 0644]
src/jalview/ws/api/CancellableI.java [new file with mode: 0644]
src/jalview/ws/api/DistanceMatrixResultI.java [new file with mode: 0644]
src/jalview/ws/api/JalviewServiceEndpointProviderI.java [new file with mode: 0644]
src/jalview/ws/api/JalviewWebServiceI.java [new file with mode: 0644]
src/jalview/ws/api/JobId.java [new file with mode: 0644]
src/jalview/ws/api/MsaI.java [new file with mode: 0644]
src/jalview/ws/api/MsaResultI.java [new file with mode: 0644]
src/jalview/ws/api/MsaWithGuideTreeI.java [new file with mode: 0644]
src/jalview/ws/api/MultipleSequenceAlignmentI.java [new file with mode: 0644]
src/jalview/ws/api/SequenceAnnotationServiceI.java [new file with mode: 0644]
src/jalview/ws/api/ServiceWithParameters.java [new file with mode: 0644]
src/jalview/ws/api/TreeResultI.java [new file with mode: 0644]
src/jalview/ws/api/UIinfo.java [new file with mode: 0644]
src/jalview/ws/api/WSAnnotationCalcManagerI.java [new file with mode: 0644]
src/jalview/ws/dbsources/EmblCdsSource.java
src/jalview/ws/dbsources/EmblFlatfileSource.java
src/jalview/ws/dbsources/EmblSource.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/ebi/HmmerJSONProcessor.java [new file with mode: 0644]
src/jalview/ws/ebi/hmmerClient.java [new file with mode: 0644]
src/jalview/ws/gui/AnnotationWsJob.java [new file with mode: 0644]
src/jalview/ws/gui/MsaWSJob.java [new file with mode: 0644]
src/jalview/ws/gui/MsaWSThread.java [moved from src/jalview/ws/jws2/MsaWSThread.java with 53% similarity]
src/jalview/ws/gui/WsJob.java [new file with mode: 0644]
src/jalview/ws/io/mime/JalviewMimeContentHandler.java
src/jalview/ws/jws1/Discoverer.java
src/jalview/ws/jws1/JPredClient.java
src/jalview/ws/jws1/JPredThread.java
src/jalview/ws/jws1/MsaWSClient.java
src/jalview/ws/jws1/MsaWSThread.java
src/jalview/ws/jws1/SeqSearchWSClient.java
src/jalview/ws/jws1/SeqSearchWSThread.java
src/jalview/ws/jws1/WS1Client.java
src/jalview/ws/jws2/AAConClient.java [deleted file]
src/jalview/ws/jws2/AADisorderClient.java [deleted file]
src/jalview/ws/jws2/AbstractJabaCalcWorker.java [deleted file]
src/jalview/ws/jws2/JabaParamStore.java
src/jalview/ws/jws2/JabaPreset.java
src/jalview/ws/jws2/JabaWsParamTest.java [new file with mode: 0644]
src/jalview/ws/jws2/JabawsCalcWorker.java [deleted file]
src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java [deleted file]
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/Jws2ClientFactory.java [new file with mode: 0644]
src/jalview/ws/jws2/Jws2Discoverer.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/ParameterUtils.java
src/jalview/ws/jws2/PreferredServiceChangeListener.java [new file with mode: 0644]
src/jalview/ws/jws2/PreferredServiceRegistry.java [new file with mode: 0644]
src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java [new file with mode: 0644]
src/jalview/ws/jws2/SequenceAnnotationWSClient.java
src/jalview/ws/jws2/dm/AAConSettings.java
src/jalview/ws/jws2/dm/JabaOption.java
src/jalview/ws/jws2/jabaws2/AAConClient.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/AADisorderClient.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/JabawsAnnotationInstance.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/JabawsMsaInterfaceAlignCalcWorker.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/Jws2Instance.java
src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
src/jalview/ws/jws2/jabaws2/RNAalifoldClient.java [moved from src/jalview/ws/jws2/RNAalifoldClient.java with 70% similarity]
src/jalview/ws/params/ArgumentI.java
src/jalview/ws/params/AutoCalcSetting.java
src/jalview/ws/params/OptionI.java
src/jalview/ws/params/ValueConstrainI.java
src/jalview/ws/params/simple/BooleanOption.java
src/jalview/ws/params/simple/DoubleParameter.java [new file with mode: 0644]
src/jalview/ws/params/simple/FileParameter.java [new file with mode: 0644]
src/jalview/ws/params/simple/IntegerParameter.java
src/jalview/ws/params/simple/LogarithmicParameter.java [new file with mode: 0644]
src/jalview/ws/params/simple/Option.java
src/jalview/ws/params/simple/Parameter.java [deleted file]
src/jalview/ws/params/simple/RadioChoiceParameter.java [moved from src/jalview/ws/params/simple/StringChoiceParameter.java with 65% similarity]
src/jalview/ws/params/simple/StringParameter.java [new file with mode: 0644]
src/jalview/ws/rest/HttpResultSet.java
src/jalview/ws/rest/InputType.java
src/jalview/ws/rest/RestClient.java
src/jalview/ws/rest/RestJob.java
src/jalview/ws/rest/RestJobThread.java
src/jalview/ws/rest/RestServiceDescription.java
src/jalview/ws/rest/clientdefs/ShmrRestClient.java [new file with mode: 0644]
src/jalview/ws/sifts/SiftsClient.java
src/jalview/ws/sifts/SiftsSettings.java
src/jalview/ws/slivkaws/RNAalifoldServiceInstance.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaAnnotationServiceInstance.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaDatastore.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaMsaServiceInstance.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaParamSet.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaWSDiscoverer.java [new file with mode: 0644]
src/jalview/ws/slivkaws/SlivkaWSInstance.java [new file with mode: 0644]
src/jalview/ws/uimodel/AlignAnalysisUIText.java
src/jalview/ws/utils/UrlDownloadClient.java
src/jalview/xml/binding/jalview/DoubleVector.java
src/jalview/xml/binding/jalview/JalviewModel.java
src/javajs/async/Assets.java [new file with mode: 0644]
src/javajs/async/Async.java [new file with mode: 0644]
src/javajs/async/AsyncColorChooser.java [new file with mode: 0644]
src/javajs/async/AsyncDialog.java [new file with mode: 0644]
src/javajs/async/AsyncFileChooser.java [new file with mode: 0644]
src/javajs/async/AsyncSwingWorker.java [new file with mode: 0644]
src/javajs/async/SwingJSUtils.java [new file with mode: 0644]
src/org/json/JSONObject.java
src/swingjs/api/Interface.java [new file with mode: 0644]
src/swingjs/api/JSFileHandler.java [new file with mode: 0644]
src/swingjs/api/JSUtilI.java
src/swingjs/api/js/DOMNode.java [new file with mode: 0644]
src/swingjs/api/js/HTML5AudioContext.java [new file with mode: 0644]
src/swingjs/api/js/HTML5Canvas.java [new file with mode: 0644]
src/swingjs/api/js/HTML5CanvasContext2D.java [new file with mode: 0644]
src/swingjs/api/js/HTML5DataTransfer.java [new file with mode: 0644]
src/swingjs/api/js/HTML5Video.java [new file with mode: 0644]
src/swingjs/api/js/J2SInterface.java [new file with mode: 0644]
src/swingjs/api/js/JQuery.java [new file with mode: 0644]
src/swingjs/api/js/JQueryObject.java [new file with mode: 0644]
src/swingjs/api/js/JSFunction.java [new file with mode: 0644]
src/swingjs/api/js/JSInterface.java [new file with mode: 0644]
src/swingjs/api/js/README.txt [new file with mode: 0644]
srcjar/fr/orsay/lri/varna/applications/fragseq/FragSeqTreeModel.java
swingjs/README-JALVIEW [new file with mode: 0644]
swingjs/SwingJS-site.zip
swingjs/timestamp
temp/bbb.dtd.pdf [new file with mode: 0644]
template.html [new file with mode: 0644]
test/jalview/analysis/AAFrequencyTest.java
test/jalview/analysis/AlignmentGenerator.java
test/jalview/analysis/AlignmentSorterTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/CrossRefTest.java
test/jalview/analysis/FinderTest.java
test/jalview/analysis/SeqsetUtilsTest.java
test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java
test/jalview/bin/CommandLineOperations.java
test/jalview/datamodel/AlignmentAnnotationTests.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/AlignmentViewTest.java
test/jalview/datamodel/HMMNodeTest.java [new file with mode: 0644]
test/jalview/datamodel/HiddenMarkovModelTest.java [new file with mode: 0644]
test/jalview/datamodel/HiddenSequencesTest.java
test/jalview/datamodel/ResidueCountTest.java
test/jalview/datamodel/SequenceGroupTest.java
test/jalview/datamodel/SequenceTest.java
test/jalview/datamodel/features/FeatureAttributesTest.java
test/jalview/ext/ensembl/EnsemblCdnaTest.java
test/jalview/ext/ensembl/EnsemblCdsTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/ext/ensembl/EnsemblGenomeTest.java
test/jalview/ext/ensembl/EnsemblSeqProxyTest.java
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/ext/jmol/JmolViewerTest.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/fts/service/pdb/PDBFTSPanelTest.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AlignmentPanelTest.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/AnnotationColumnChooserTest.java
test/jalview/gui/AnnotationRowFilterTest.java
test/jalview/gui/CalculationChooserTest.java
test/jalview/gui/FreeUpMemoryTest.java
test/jalview/gui/JvSwingUtilsTest.java
test/jalview/gui/PairwiseAlignmentPanelTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/gui/SeqCanvasTest.java
test/jalview/gui/SeqPanelTest.java
test/jalview/gui/SequenceRendererTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/hmmer/HMMERTest.java [new file with mode: 0644]
test/jalview/hmmer/testProps.jvprops [new file with mode: 0644]
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/BSMLFileTest.java [new file with mode: 0644]
test/jalview/io/BackupFilesTest.java
test/jalview/io/CrossRef2xmlTests.java
test/jalview/io/FeaturesFileTest.java
test/jalview/io/FileFormatsTest.java
test/jalview/io/FileLoaderTest.java
test/jalview/io/FormatAdapterTest.java
test/jalview/io/HMMAlignmentTest.sto [new file with mode: 0644]
test/jalview/io/HMMAlignmentTestHMM.hmm [new file with mode: 0644]
test/jalview/io/HMMFileTest.java [new file with mode: 0644]
test/jalview/io/Jalview2xmlBase.java
test/jalview/io/JalviewExportPropertiesTests.java
test/jalview/io/SequenceAnnotationReportTest.java
test/jalview/io/StockholmFileTest.java
test/jalview/io/test_MADE1_hmm.txt [new file with mode: 0644]
test/jalview/io/test_PKinase_hmm.txt [new file with mode: 0644]
test/jalview/io/test_fn3_hmm.txt [new file with mode: 0644]
test/jalview/project/Jalview2xmlTests.java
test/jalview/renderer/OverviewResColourFinderTest.java
test/jalview/renderer/ResidueColourFinderTest.java
test/jalview/renderer/ScaleRendererTest.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
test/jalview/renderer/seqfeatures/FeatureRendererTest.java
test/jalview/schemes/ClustalxColourSchemeTest.java
test/jalview/schemes/ColourSchemesTest.java
test/jalview/schemes/HmmerGlobalBackgroundTest.java [new file with mode: 0644]
test/jalview/schemes/HmmerLocalBackgroundTest.java [new file with mode: 0644]
test/jalview/schemes/PIDColourSchemeTest.java
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/util/Binned.csv [new file with mode: 0644]
test/jalview/util/ColorUtilsTest.java
test/jalview/util/FileUtilsTest.java [new file with mode: 0644]
test/jalview/util/HMMProbabilityDistributionAnalyserTest.java [new file with mode: 0644]
test/jalview/util/MapListTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/util/PlatformTest.java
test/jalview/util/Raw.csv [new file with mode: 0644]
test/jalview/util/StringUtilsTest.java
test/jalview/util/test_Fams_for_probability_analysis [new file with mode: 0644]
test/jalview/util/test_hmms_for_probability_analysis [new file with mode: 0644]
test/jalview/workers/AlignCaclManager2Test.java [new file with mode: 0644]
test/jalview/workers/AlignCalcManagerTest.java
test/jalview/ws/PDBSequenceFetcherTest.java
test/jalview/ws/dbsources/RemoteFormatTest.java
test/jalview/ws/ebi/HmmerJSONProcessTest.java [new file with mode: 0644]
test/jalview/ws/gui/Jws2ParamView.java
test/jalview/ws/jabaws/AAConAnnotAndSettingsIO.java [new file with mode: 0644]
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/JalviewJabawsTestUtils.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/jws2/ParameterUtilsTest.java
test/jalview/ws/rest/ShmmrRSBSService.java
test/jalview/ws/sifts/SiftsClientTest.java
test/jalview/ws/utils/UrlDownloadClientTest.java
test/mc_view/PDBfileTest.java
unused/JalviewAppLoader.java [new file with mode: 0644]
unused/JalviewJSApp.java [new file with mode: 0644]
utils/i18nAnt.xml
utils/jalviewjs/_j2sclasslist.txt [deleted file]
utils/jalviewjs/classlists/jalview.txt
utils/jalviewjs/classlists/jvjmol.txt [new file with mode: 0644]
utils/jalviewjs/classlists/stevesoft.txt [new file with mode: 0644]
utils/jalviewjs/coretemplate.html
utils/jalviewjs/libjs/jmol-app.zip
utils/jalviewjs/libjs/slivka-client-site.zip [new file with mode: 0644]
utils/jalviewjs/site-resources/___j2sflags.htm [new file with mode: 0644]
utils/jalviewjs/site-resources/_jalview_bin_JalviewJS_core.html [moved from utils/jalviewjs/site-resources/jalview_bin_JalviewJS_core.html with 100% similarity]
utils/jalviewjs/site-resources/_jalview_embedded_example1.html [moved from utils/jalviewjs/site-resources/jalview_embedded_example1.html with 79% similarity]
utils/jalviewjs/site-resources/_jalview_embedded_example2.html [new file with mode: 0644]
utils/jalviewjs/template.html
utils/testnglibs/testng-7.4.0-sources.jar [new file with mode: 0644]
utils/testnglibs/testng-7.4.0.jar [new file with mode: 0644]
utils/testnglibs/testng-sources.jar [deleted file]
utils/testnglibs/testng.jar [deleted file]

index 72ff49e..0a4b0d2 100644 (file)
@@ -28,7 +28,7 @@ htsjdk-2.12.0.jar     built from maven master at https://github.com/samtools/htsjdk
 httpclient-4.0.3.jar
 httpcore-4.0.1.jar
 httpmime-4.0.3.jar
-intervalstore-v1.0.jar
+intervalstore-v1.1.jar
 jabaws-min-client-NO_LOG4J-2.2.1.jar   Apache license - pre-release of JABAWS 2.2.1 client built from https://source.jalview.org/crucible/changelog/jabaws?cs=586260b9f877e0954513fcffb0aa27eaddc5d0ff 
 java-json.jar
 jaxrpc.jar
diff --git a/build-libjs.xml b/build-libjs.xml
new file mode 100644 (file)
index 0000000..3fb3cd3
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!--
+ * just a crude zip up of non-Jalview classes for development purposes -BH 2018
+ *
+ * external JAR class treatment for JavaScript: see src2/README_SWINGJS.txt
+ * 
+ -->
+
+<project name="jalviewX" default="zipall" basedir="."
+ xmlns:if="ant:if"
+    xmlns:unless="ant:unless">
+
+       <!-- inputs directories -->
+    <property name="resource.dir" value="resources" />         
+    <property name="swingjs.dir" value="swingjs"/>
+       <!-- output directories -->
+       <property name="site.dir" value="site"/>
+       <property name="j2s.dir" value="${site.dir}/swingjs/j2s"/>
+       <property name="libjs.dir" value="libjs"/>
+
+       <target name="zipall" depends="zipvarna,zipmig,zipintervalstore">
+               
+               
+  </target>
+
+  <target name="zipvarna">
+    <!-- VARNA -->
+           <property name="varna.zip" value="${libjs.dir}/VARNA-site.zip" />                   
+               <echo> Zipping up ${varna.zip} </echo>
+               <zip destfile="${varna.zip}" basedir="${site.dir}" includes="fr_*.html,swingjs/j2s/fr/**" />
+       </target>
+
+       <target name="zipmig">
+         <!-- net.miginfo.com MiGLayout -->
+                   <property name="mig.zip" value="${libjs.dir}/MiGLayout-site.zip" />                 
+                       <echo> Zipping up ${mig.zip} </echo>
+                       <zip destfile="${mig.zip}" basedir="${site.dir}" includes="swingjs/j2s/net/miginfocom/**" />
+       </target>
+
+       <target name="zipintervalstore">
+         <!-- intervalstore.impl NCList implementation -->
+                   <property name="intervalstore.zip" value="${libjs.dir}/intervalstore-site.zip" />                   
+                       <echo> Zipping up ${intervalstore.zip} </echo>
+                       <zip destfile="${intervalstore.zip}" basedir="${site.dir}" includes="swingjs/j2s/intervalstore/**" />
+       </target>
+
+       <!-- already in SwingJS
+       <target name="zipjson"  already in SwingJS>
+                   <property name="json.zip" value="${libjs.dir}/json-site.zip" />                     
+                       <echo> Zipping up ${json.zip} </echo>
+                       <zip destfile="${json.zip}" basedir="${site.dir}" includes="swingjs/j2s/org/json/**" />
+       </target>
+       -->
+
+       <!-- log4j minimal implementation is already in jalview/javascript
+             and is mapped from org.apache.log4j by the following .j2s line:
+             
+             j2s.class.replacements=org.apache.log4j.->jalview.javascript.log4j.
+              
+       <target name="ziplog4j">
+                 <!- org.apache.log4j ->
+                   <property name="log4j.zip" value="${libjs.dir}/log4j-site.zip" />                   
+                       <echo> Zipping up ${log4j.zip} </echo>
+                       <zip destfile="${log4j.zip}" basedir="${site.dir}" includes="swingjs/j2s/org/apache/log4j/**" />
+       </target>
+    -->
+</project>
index bde50b5..e7e6abb 100644 (file)
@@ -409,7 +409,6 @@ ext {
   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
   modules_runtimeClasspath = modules_compileClasspath
   */
-
   gitHash = "SOURCE"
   gitBranch = "Source"
   try {
@@ -1406,8 +1405,6 @@ def getMdSections(String content) {
   }
   return sections
 }
-
-
 task copyHelp(type: Copy) {
   def inputDir = helpSourceDir
   def outputDir = "${helpBuildDir}/${help_dir}"
@@ -1578,8 +1575,6 @@ task releasesTemplates {
   outputs.file(releasesHtmlFile)
   outputs.file(whatsnewHtmlFile)
 }
-
-
 task copyResources(type: Copy) {
   group = "build"
   description = "Copy (and make text substitutions in) the resources dir to the build area"
@@ -2252,7 +2247,6 @@ task getdownArchive() {
   dependsOn getdownArchiveBuild
   dependsOn getdownArchiveDigest
 }
-
 tasks.withType(JavaCompile) {
        options.encoding = 'UTF-8'
 }
@@ -2307,7 +2301,6 @@ task copyInstall4jTemplate {
       macosArchive.attributes().remove('executeSetupApp')
       macosArchive.attributes().remove('setupAppId')
     }
-
     // turn off checksum creation for LOCAL channel
     def e = install4jConfigXml.application[0]
     e.'@createChecksums' = string(install4jCheckSums)
@@ -2564,7 +2557,6 @@ task installers {
   dependsOn installerFiles
 }
 
-
 spotless {
   java {
     eclipse().configFile(eclipse_codestyle_file)
@@ -2739,7 +2731,6 @@ task dataInstallersJson {
 task helppages {
   group "help"
   description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
-
   dependsOn copyHelp
   dependsOn pubhtmlhelp
   
diff --git a/doc/JalviewJS-API.md b/doc/JalviewJS-API.md
new file mode 100644 (file)
index 0000000..64cae62
--- /dev/null
@@ -0,0 +1,123 @@
+# public interface JalviewJSApi
+
+## full list of available methods (from JalviewLiteJsApi):
+
+  public boolean addPdbFile(AlignFrame alFrame, String sequenceId, String pdbEntryString, String pdbFile);
+  public String getAlignment(String format);
+  public String getAlignment(String format, String suffix);
+  public String getAlignmentFrom(AlignFrame alf, String format);
+  public String getAlignmentFrom(AlignFrame alf, String format, String suffix);
+  public String getAlignmentOrder();
+  public String getAlignmentOrderFrom(AlignFrame alf);
+  public String getAlignmentOrderFrom(AlignFrame alf, String sep);
+  public String getAnnotation();
+  public String getAnnotationFrom(AlignFrame alf);
+  public Object getAppletParameter(String name, boolean asString);
+  public URL getCodeBase();
+  public URL getDocumentBase();
+  public String getFeatureGroups();
+  public String getFeatureGroupsOfState(boolean visible);
+  public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible);
+  public String getFeatureGroupsOn(AlignFrame alf);
+  public String getFeatures(String format);
+  public String getFeaturesFrom(AlignFrame alf, String format);
+  public Object getFrameForSource(VamsasSource source);
+  public String getParameter(String name);
+  public String getSelectedSequences();
+  public String getSelectedSequences(String sep);
+  public String getSelectedSequencesAsAlignment(String format, String suffix);
+  public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, String format, String suffix);
+  public String getSelectedSequencesFrom(AlignFrame alf);
+  public String getSelectedSequencesFrom(AlignFrame alf, String sep);
+  public String getSeparator();
+  public AlignViewportI getViewport();
+  public void highlight(String sequenceId, String position, String alignedPosition);
+  public void highlightIn(AlignFrame alf, String sequenceId, String position, String alignedPosition);
+  public AlignFrame loadAlignment(String text, String title);
+  public void loadAnnotation(String annotation);
+  public void loadAnnotationFrom(AlignFrame alf, String annotation);
+  public void loadFeatures(String features, boolean autoenabledisplay);
+  public boolean loadFeaturesFrom(AlignFrame alf, String features, boolean autoenabledisplay);
+  public boolean loadScoreFile(String sScoreFile) throws IOException;
+  public void newFeatureSettings();
+  public void newStructureView(PDBEntry pdb, SequenceI[] seqs, String[] chains, DataSourceType protocol);
+  public Object openPcaPanel(AlignFrame af, String modelName);
+  public Object openTreePanel(AlignFrame af, String treeType, String modelName);
+  public String orderAlignmentBy(AlignFrame alf, String order, String undoName, String sep);
+  public String orderBy(String order, String undoName);
+  public String orderBy(String order, String undoName, String sep);
+  public Object parseArguments(String[] args);
+  public boolean parseFeaturesFile(String param, DataSourceType protocol);
+  public void removeSelectionListener(AlignFrame af, String listener);
+  public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn);
+  public void scrollViewToIn(AlignFrame alf, String topRow, String leftHandColumn);
+  public void scrollViewToRowIn(AlignFrame alf, String topRow);
+  public void select(String sequenceIds, String columns);
+  public void select(String sequenceIds, String columns, String sep);
+  public void selectIn(AlignFrame alf, String sequenceIds, String columns);
+  public void selectIn(AlignFrame alf, String sequenceIds, String columns, String sep);
+  public void setFeatureGroupState(String groups, boolean state);
+  public void setFeatureGroupState(String[] groups, boolean state);
+  public void setFeatureGroupStateOn(AlignFrame alf, String groups, boolean state);
+  public void setSelectionListener(AlignFrame af, String listener);
+  public void setSelectionListener(String listener);
+  public void setSeparator(String separator);
+  public void showOverview();
+  public void updateForAnnotations();
+
+## addition available methods (from JalviewLite):
+
+  public static String getBuildDate()
+  public static String getInstallation()
+  public static String getVersion()
+
+
+
+## proposed alias list:
+- remove overloaded methods
+- indicate null options
+- use standard arrays; no need for special separators
+- possibly return more actual objects, not just strings
+- moves AlignFrame to last parameter, as it is likely to be unnecessary
+
+public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile, AlignFrame alFrame);
+public String getAlignment(String format, boolean addSuffix, AlignFrame alf);
+public String[] getAlignmentOrder(AlignFrame alf);
+public String getAnnotation(AlignFrame alf);
+public URL getCodeBase();
+public URL getDocumentBase();
+public String[] getFeatureGroups(AlignFrame alf);
+public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf);
+public String getFeatures(String format, AlignFrame alf);
+public String getParameter(String name);
+public Object getParameterAsObject(String name);
+public SequenceI[] getSelectedSequences(AlignFrame alf);
+public AlignFrame newView();
+public AlignFrame newView(String name);
+public AlignFrame newViewFrom(AlignFrame alf);
+public AlignFrame newViewFrom(AlignFrame alf, String name);
+public String getSelectedSequencesAsAlignment(String format, boolean addSuffix, AlignFrame alf);
+public void highlight(String sequenceId, String position, String alignedPosition, AlignFrame alf);
+public AlignFrame loadAlignment(String data, String title, int width, int height);
+public void loadAnnotation(String annotation, AlignFrame alf);
+public boolean loadFeatures(String features, boolean autoenabledisplay, AlignFrame alf);
+public boolean loadScoreFile(String sScoreFile, AlignFrame alf);
+public Object openPcaPanel(String modelName, AlignFrame alf);
+public Object openTreePanel(String treeType, String modelName, AlignFrame alf);
+public boolean orderAlignment(String[] ids, String undoName, AlignFrame alf);
+public Object parseArguments(String[] args);
+public void removeSelectionListener(String listener, AlignFrame af);
+public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf);
+public void select(String[] sequenceIds, String[] columns, AlignFrame alf);
+public void setFeatureGroupState(String[] groups, boolean state, AlignFrame alf);
+public void setSelectionListener(String listener, AlignFrame alf);
+public void showOverview();
+public void showStructure(String pdbID, String fileType, AlignFrame alf);
+
+# unknown methods/shouldn't be in interface?
+
+  public Object getFrameForSource(VamsasSource source);
+  public void setSeparator(String separator);
+  
\ No newline at end of file
diff --git a/doc/JalviewJS-startupParams.md b/doc/JalviewJS-startupParams.md
new file mode 100644 (file)
index 0000000..9ff3352
--- /dev/null
@@ -0,0 +1,92 @@
+## JalviewJS startup parameters
+
+TODO -- go through these
+
+# parameters -- from jalview.bin.AppletParams
+
+  private final static String[] params = { 
+      "alignpdbfiles",
+      "ANNOTATIONCOLOUR_MAX", "ANNOTATIONCOLOUR_MIN",
+      "annotations",
+      "APPLICATION_URL", "automaticScrolling", "centrecolumnlabels",
+      "debug", "defaultColour", "defaultColourNuc", "defaultColourProt",
+      "embedded", "enableSplitFrame", "externalstructureviewer", "features",
+      "file", "file2", "format", "heightScale", "hidefeaturegroups",
+      "jalviewhelpurl", "jnetfile", "jpredfile", "label", "linkLabel_",
+      "linkLabel_1", "linkURL_", "nojmol", "normaliseLogo",
+      "normaliseSequenceLogo", "oninit", "PDBFILE", "PDBSEQ",
+      "relaxedidmatch", "resolvetocodebase", "RGB", "scaleProteinAsCdna",
+      "scoreFile", "separator", "sequence", "showAnnotation", "showbutton",
+      "showConsensus", "showConsensusHistogram", "showConservation",
+      "showfeaturegroups", "showFeatureSettings", "showFullId",
+      "showGroupConsensus", "showGroupConservation", "showOccupancy",
+      "showQuality", "showSequenceLogo", "showTreeBootstraps",
+      "showTreeDistances", "showUnconserved", "showUnlinkedTreeNodes",
+      "sortBy", "sortByTree", "tree", "treeFile", "upperCase",
+      "userDefinedColour", "widthScale", "windowHeight", "windowWidth",
+      "wrap", };
+
+
+
+
+# arguments -- from jalview.bin.ArgsParser
+
+  public static final String NOCALCULATION = "nocalculation";
+
+  public static final String NOMENUBAR = "nomenubar";
+
+  public static final String NOSTATUS = "nostatus";
+
+  public static final String SHOWOVERVIEW = "showoverview";
+
+  //
+  public static final String ANNOTATIONS = "annotations";
+
+  public static final String COLOUR = "colour";
+
+  public static final String FEATURES = "features";
+
+  public static final String GROOVY = "groovy";
+
+  public static final String GROUPS = "groups";
+
+  public static final String HEADLESS = "headless";
+
+  public static final String JABAWS = "jabaws";
+
+  public static final String NOANNOTATION = "no-annotation";
+
+  public static final String NOANNOTATION2 = "noannotation"; // BH 2019.05.07
+
+  public static final String NODISPLAY = "nodisplay";
+
+  public static final String NOGUI = "nogui";
+
+  public static final String NONEWS = "nonews";
+
+  public static final String NOQUESTIONNAIRE = "noquestionnaire";
+
+  public static final String NOSORTBYTREE = "nosortbytree";
+
+  public static final String NOUSAGESTATS = "nousagestats";
+
+  public static final String OPEN = "open";
+
+  public static final String OPEN2 = "open2"; // BH added -- for applet
+                                              // compatibility; not fully
+                                              // implemented
+
+  public static final String PROPS = "props";
+
+  public static final String QUESTIONNAIRE = "questionnaire";
+
+  public static final String SETPROP = "setprop";
+
+  public static final String SORTBYTREE = "sortbytree";
+
+  public static final String TREE = "tree";
+
+  public static final String VDOC = "vdoc";
+
+  public static final String VSESS = "vsess";
+
diff --git a/doc/JalviewJS-startupParams.xlsx b/doc/JalviewJS-startupParams.xlsx
new file mode 100644 (file)
index 0000000..d32fc48
Binary files /dev/null and b/doc/JalviewJS-startupParams.xlsx differ
diff --git a/doc/addingWebClient.md b/doc/addingWebClient.md
new file mode 100644 (file)
index 0000000..0d0ee1d
--- /dev/null
@@ -0,0 +1,68 @@
+# How to add a web service backend to Jalview's web services UI
+
+### Document Status: *Work in progress !*
+
+There are two phases to services.
+
+ *Discovery* threads are started by jalview.gui.Desktop (or other UI components or the CLI if they are headless services accessible via commandline). Your service discovery thread registers discovered instances so they can be accessed by the user.
+
+ *Access* most services are accessed by selecting a menu item, option, or specifying a nickname for the service instance as a Jalview command line parameter. Different access patterns are used for the various services: seqeunce data source, sequence feature annotation source, multiple sequence alignment, multiple alignment annotation source, etc. There should be no need for your code to create UI components (if there is then Jalview's UI needs to be refactored to avoid this).
+
+## Discovery
+
+*jalview.gui.Desktop*
+- start a discovery thread to discover services provided by your framework. The discoverer should generate a list of objects that either implement WsMenuEntryProviderI or provide another mechanism to add themselves to Jalview's menus (via jalview.gui.AlignFrame.BuildWebServiceMenu() below)
+
+*jalview.gui.AlignFrame*
+- BuildWebServiceMenu()
+ This method creates a runnable that's called when the available set of web services changes (e.g. when a discovery thread completes). Add code to the Runnable's run method to create Menu Items in the web services menu via jalview.ws.WSMenuEntryProviderI instances generated by your discoverer. 
+
+
+## Types of service
+
+### Services that create new alignments or windows, optionally with a progress log
+
+The pattern for these services requires a Client that initates a thread which creates, and montors one or more jobs based on input data.
+
+jalview.ws.MsaWSClient -- currently does double duty with Jaba services for alignment analysis and instantiation of alignment services.
+
+jalview.ws.gui.MSAThread -- UI model and controller for a running MSA service. Instantiated with an instance of jalview.ws.MultipleSequenceAlignmentI provided by the service endpoint factory.
+
+
+## Other classes of interest
+
+### jalview.ws.api
+
+Interfaces and base classes to be implemented and used by a service endpoint.
+
+ jalview.ws.api.CancellableI.java - implement if the service is cancellable
+ jalview.ws.api.JalviewWebServiceI.java - base service interface
+ jalview.ws.api.JobId.java - a timestamped job id that can be saved in a Jalview project
+
+#### submission interfaces
+jalview.ws.api.MsaI.java
+jalview.ws.api.MsaWithGuideTreeI.java
+
+
+#### result interfaces
+jalview.ws.api.MsaResultI.java
+jalview.ws.api.TreeResultI.java
+jalview.ws.api.DistanceMatrixResultI.java
+
+
+#### minimal composed interfaces for a complete functional analysis 
+
+jalview.ws.api.MultipleSequenceAlignmentI.java
+
+
+#### base class for a service endpoint instance - to be materialised by service discoverers
+
+jalview.ws.api.UIinfo.java - basic information 
+jalview.ws.api.ServiceWithParameters.java
+
+jalview.ws.api.JalviewServiceEndpointProviderI - implemented by service endpoint factories (typically extended from UIinfo or ServiceWithParameters).
+
+
+## Analysis services
+
+These have not yet been refactored. Feel free to look at how jalview.ws.jws2.AACons and Disorder clients operate and adapt the pattern for your own services. The Groovy example in the help [[help/html/groovy/featuresCounter.html]] for creating custom annotation tracks uses the same basic 'alignment analysis worker' mechanism to provide dynamically executed alignment analysis calculations that result in annotation tracks displayed below the alginment.
index 8307ee3..5679cd8 100644 (file)
@@ -1,10 +1,10 @@
-<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">
+<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" />
+  <title>Building Jalview from Source</title>
   <title>Building Jalview from Source</title>
     <style type="text/css">code{white-space: pre;}</style>
 <style>
@@ -672,20 +672,17 @@ body :checked+.radio-label {
 </head>
   <body>
 <ul>
-<li><a href="#tldr">tl;dr</a></li>
-<li><a href="#setting-up">Setting up</a>
 <ul>
+<li><a href="#setting-up">Setting up</a>
+<li><a href="#tldr">tl;dr</a></li>
+<li><a href="#java-11-compliant-jdk">Java 11 compliant JDK</a></li>
 <li><a href="#java-11-compliant-jdk">Java 11 compliant JDK</a></li>
 <li><a href="#gradle-and-git">gradle and git</a></li>
-</ul>
 </li>
 <li><a href="#downloading-the-jalview-source-tree">Downloading the Jalview source tree</a>
-<ul>
+<li><a href="#whats-in-the-source-tree">What's in the source tree?</a></li>
 <li><a href="#whats-in-the-source-tree">What's in the source tree?</a></li>
 </ul>
-</li>
-<li><a href="#building-jalview">Building Jalview</a>
-<ul>
 <li><a href="#minimal-jalview-build">Minimal Jalview Build</a></li>
 <li><a href="#jalview-in-a-jar-file">Jalview in a Jar File</a></li>
 <li><a href="#distributed-jar-files">Distributed Jar Files</a></li>
@@ -693,534 +690,468 @@ body :checked+.radio-label {
 <li><a href="#building-the-getdown-launcher">Building the <code>getdown</code> launcher</a></li>
 <li><a href="#running-tests">Running tests</a></li>
 <li><a href="#installer-packaging-with-install4j">Installer packaging with <em>install4j</em></a></li>
-</ul>
-</li>
+<li><a href="#building-the-getdown-launcher">Building the <code>getdown</code> launcher</a></li>
 <li><a href="#gradle-properties">Gradle properties</a></li>
 <li><a href="#enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</a></li>
-<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a>
-<ul>
+</ul>
 <li><a href="#installing-eclipse-ide">Installing Eclipse IDE</a></li>
 <li><a href="#importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</a></li>
+<li><a href="#enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</a></li>
+<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a>
 </ul>
 </li>
 </ul>
 <h1 id="building-jalview-from-source"><a href="#building-jalview-from-source" name="building-jalview-from-source" class="anchor"><span class="octicon octicon-link"></span>Building Jalview from Source</a></h1>
-<h2 id="tldr"><a href="#tldr" name="tldr" class="anchor"><span class="octicon octicon-link"></span>tl;dr</a></h2>
+</head>
+<pre><code># download
+git clone http://source.jalview.org/git/jalview.git
+<ul>
+cd jalview
+<li><a href="#tldr">tl;dr</a></li>
+# run
+<li><a href="#java-11-compliant-jdk">Java 11 compliant JDK</a></li>
+<li><a href="#gradle-and-git">gradle and git</a></li>
+# and/or create launcher
+gradle getdown
+<li><a href="#whats-in-the-source-tree">What's in the source tree?</a></li>
+cd getdown/files
+java -jar getdown-launcher.jar . jalview
+<li><a href="#minimal-jalview-build">Minimal Jalview Build</a></li>
+<li><a href="#jalview-in-a-jar-file">Jalview in a Jar File</a></li>
+<li><a href="#distributed-jar-files">Distributed Jar Files</a></li>
+<li><a href="#single-shadow-jar-file">Single <em>shadow</em> Jar File</a></li>
+<li><a href="#building-the-getdown-launcher">Building the <code>getdown</code> launcher</a></li>
+<li><a href="#running-tests">Running tests</a></li>
+<li><a href="#installer-packaging-with-install4j">Installer packaging with <em>install4j</em></a></li>
+<li>Java 11 compliant JDK</li>
+<li><a href="#gradle-properties">Gradle properties</a></li>
+<li><a href="#enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</a></li>
+</ul>
+<li><a href="#installing-eclipse-ide">Installing Eclipse IDE</a></li>
+<li><a href="#importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</a></li>
+so are known to work).  If you need or wish to use different implementations (particularly
+you might need a bespoke JDK if you are on an exotic architecture) then the general
+</ul>
+bytecode with any JDK Java 11+.  The resulting bytecode (in particular the shadow jar)
+should be runnable in any JRE Java 1.8+.  Remember that because Jalview and the getdown launcher
+are Java bytecode you can build on one system where you might have gradle, and run
 <pre><code># download
 git clone http://source.jalview.org/git/jalview.git
 # compile
 cd jalview
 gradle shadowJar
 # run
-java -jar build/libs/jalview-all-*-j11.jar
+# compile
 
 # and/or create launcher
 gradle getdown
 # use launcher
 cd getdown/files
-java -jar getdown-launcher.jar . jalview
-</code></pre>
-<h2 id="setting-up"><a href="#setting-up" name="setting-up" class="anchor"><span class="octicon octicon-link"></span>Setting up</a></h2>
+java -jar getdown-launcher.jar . jalview</code></pre>
+<h2 id="setting-up">Setting up</h2>
 <blockquote>
-<p>To get set up using <em>only</em> the Eclipse IDE (<a href="https://www.eclipse.org/">https://www.eclipse.org/</a>) then please see the section <a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a></p>
+<p>To get set up using <em>only</em> the Eclipse IDE (<a href="https://www.eclipse.org/" class="uri">https://www.eclipse.org/</a>) then please see the section <a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a></p>
 </blockquote>
-<p>The method here is described in terms of using a command line.  You can easily do this on linux or in a Terminal window in macOS.  You can do it in Windows.</p>
+<p>The method here is described in terms of using a command line. You can easily do this on linux or in a Terminal window in macOS. You can do it in Windows.</p>
 <ul>
 <li>Java 11 compliant JDK</li>
-<li>gradle 5.2 or above <em>(NB gradle 6.6 and above currently produces NullPointerExceptions during the build. This is non-fatal and does not affect the build. Use gradle 6.5.1 to avoid this)</em></li>
+<p>To get set up using <em>only</em> the Eclipse IDE (<a href="https://www.eclipse.org/">https://www.eclipse.org/</a>) then please see the section <a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a></p>
 <li>git</li>
 </ul>
 <blockquote>
-<p>The versions and installation methods here are just suggestions (which we have tested
-so are known to work).  If you need or wish to use different implementations (particularly
-you might need a bespoke JDK if you are on an exotic architecture) then the general
-build instructions should work with any gradle 5+.  You should be able to compile the
-bytecode with any JDK Java 11+.  The resulting bytecode (in particular the shadow jar)
-should be runnable in any JRE Java 1.8+.  Remember that because Jalview and the getdown launcher
-are Java bytecode you can build on one system where you might have gradle, and run
-on another where you don't (JRE 1.8+ required).</p>
+<p>The versions and installation methods here are just suggestions (which we have tested so are known to work). If you need or wish to use different implementations (particularly you might need a bespoke JDK if you are on an exotic architecture) then the general build instructions should work with any gradle 5+. You should be able to compile the bytecode with any JDK Java 11+. The resulting bytecode (in particular the shadow jar) should be runnable in any JRE Java 1.8+. Remember that because Jalview and the getdown launcher are Java bytecode you can build on one system where you might have gradle, and run on another where you don't (JRE 1.8+ required).</p>
 </blockquote>
-<h3 id="java-11-compliant-jdk"><a href="#java-11-compliant-jdk" name="java-11-compliant-jdk" class="anchor"><span class="octicon octicon-link"></span>Java 11 compliant JDK</a></h3>
-<h4 id="all-platforms"><a href="#all-platforms" name="all-platforms" class="anchor"><span class="octicon octicon-link"></span>All platforms</a></h4>
-<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you're not sure, choose the <em>Installer</em>).</p>
+<h3 id="java-11-compliant-jdk">Java 11 compliant JDK</h3>
+<h4 id="all-platforms">All platforms</h4>
+<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot" class="uri">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you're not sure, choose the <em>Installer</em>).</p>
 <blockquote>
-<h5 id="alternativecli-install-of-adoptopenjdk-11"><a href="#alternativecli-install-of-adoptopenjdk-11" name="alternativecli-install-of-adoptopenjdk-11" class="anchor"><span class="octicon octicon-link"></span>Alternative/CLI install of AdoptOpenJDK 11</a></h5>
-<p>You can also install adoptopenjdk11 using either <code>brew</code> (macOS), <code>choco</code> (Windows)
-(see the section on <code>gradle</code> and <code>git</code> for more informaiton on <code>brew</code> and <code>choco</code>)
-or <code>yum</code> or <code>apt</code> (Linux):</p>
-<h6 id="alternative-for-macos-and-homebrew"><a href="#alternative-for-macos-and-homebrew" name="alternative-for-macos-and-homebrew" class="anchor"><span class="octicon octicon-link"></span>alternative for MacOS and Homebrew</a></h6>
+<h5 id="alternativecli-install-of-adoptopenjdk-11">Alternative/CLI install of AdoptOpenJDK 11</h5>
+<p>You can also install adoptopenjdk11 using either <code>brew</code> (macOS), <code>choco</code> (Windows) (see the section on <code>gradle</code> and <code>git</code> for more informaiton on <code>brew</code> and <code>choco</code>) or <code>yum</code> or <code>apt</code> (Linux):</p>
+<h6 id="alternative-for-macos-and-homebrew">alternative for MacOS and Homebrew</h6>
 <pre><code>brew tap adoptopenjdk/openjdk
-brew cask install adoptopenjdk11
-</code></pre>
-<h6 id="alternative-for-windows-and-chocolatey"><a href="#alternative-for-windows-and-chocolatey" name="alternative-for-windows-and-chocolatey" class="anchor"><span class="octicon octicon-link"></span>alternative for Windows and Chocolatey</a></h6>
-<pre><code>choco install adoptopenjdk11
-</code></pre>
-<h6 id="alternative-for-linux-with-yumapt"><a href="#alternative-for-linux-with-yumapt" name="alternative-for-linux-with-yumapt" class="anchor"><span class="octicon octicon-link"></span>alternative for Linux with yum/apt</a></h6>
-<p>see <a href="https://adoptopenjdk.net/installation.html#linux-pkg">https://adoptopenjdk.net/installation.html#linux-pkg</a></p>
+brew cask install adoptopenjdk11</code></pre>
+<h6 id="alternative-for-windows-and-chocolatey">alternative for Windows and Chocolatey</h6>
+<pre><code>choco install adoptopenjdk11</code></pre>
+<h6 id="alternative-for-linux-with-yumapt">alternative for Linux with yum/apt</h6>
+<p>see <a href="https://adoptopenjdk.net/installation.html#linux-pkg" class="uri">https://adoptopenjdk.net/installation.html#linux-pkg</a></p>
 </blockquote>
-<h3 id="gradle-and-git"><a href="#gradle-and-git" name="gradle-and-git" class="anchor"><span class="octicon octicon-link"></span>gradle and git</a></h3>
+<h3 id="gradle-and-git">gradle and git</h3>
 <p>You should be able to install the latest (or sufficiently recent) versions of gradle and git using your OS package manager.</p>
-<h4 id="macos"><a href="#macos" name="macos" class="anchor"><span class="octicon octicon-link"></span>MacOS</a></h4>
-<p>we recommend using <code>brew</code>, which can be installed following the instructions at <a href="https://brew.sh/">https://brew.sh/</a>.
-After installing <code>brew</code>, open a Terminal window and type in (using an Administrator privileged user):</p>
-<pre><code class="language-bash">brew install gradle git
-</code></pre>
+<h4 id="macos">MacOS</h4>
+<p>we recommend using <code>brew</code>, which can be installed following the instructions at <a href="https://brew.sh/" class="uri">https://brew.sh/</a>. After installing <code>brew</code>, open a Terminal window and type in (using an Administrator privileged user):</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> install gradle git</code></pre></div>
 <p>or if you aready have them installed but need to upgrade the version:</p>
-<pre><code class="language-bash">brew upgrade gradle git
-</code></pre>
-<h4 id="windows"><a href="#windows" name="windows" class="anchor"><span class="octicon octicon-link"></span>Windows</a></h4>
-<p>we suggest using the <strong>Chocolatey</strong> package manager.  See install instructions at <a href="https://chocolatey.org/">https://chocolatey.org/</a>, and you will just need</p>
-<pre><code class="language-bash">choco install gradle
-choco install git
-</code></pre>
-<p>Alternatively, you could install a real <code>bash</code> shell and install both <code>gradle</code> and <code>git</code> through <code>apt-get</code>.
-See <a href="https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/">https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/</a>
-for how to install the ubuntu bash shell in Windows 10.</p>
-<p>Another alternative would be to install them separately. For <code>gradle</code> follow the instructions at <a href="https://gradle.org/install/">https://gradle.org/install/</a>, and for <code>git</code> here are a couple of suggestions: Git for Windows <a href="https://gitforwindows.org/">https://gitforwindows.org/</a>.
-Getting the individual installs working together on the command line will be trickier
-so we recommend using Chocolatey or bash.</p>
-<h4 id="linux"><a href="#linux" name="linux" class="anchor"><span class="octicon octicon-link"></span>Linux</a></h4>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> upgrade gradle git</code></pre></div>
+<h4 id="windows">Windows</h4>
+<p>we suggest using the <strong>Chocolatey</strong> package manager. See install instructions at <a href="https://chocolatey.org/" class="uri">https://chocolatey.org/</a>, and you will just need</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">choco</span> install gradle
+<span class="ex">choco</span> install git</code></pre></div>
+<p>Alternatively, you could install a real <code>bash</code> shell and install both <code>gradle</code> and <code>git</code> through <code>apt-get</code>. See <a href="https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/" class="uri">https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/</a> for how to install the ubuntu bash shell in Windows 10.</p>
+<p>Another alternative would be to install them separately. For <code>gradle</code> follow the instructions at <a href="https://gradle.org/install/" class="uri">https://gradle.org/install/</a>, and for <code>git</code> here are a couple of suggestions: Git for Windows <a href="https://gitforwindows.org/" class="uri">https://gitforwindows.org/</a>. Getting the individual installs working together on the command line will be trickier so we recommend using Chocolatey or bash.</p>
+<h4 id="linux">Linux</h4>
 <p>this will depend on which distribution you're using.</p>
-<h5 id="for-debian-based-distributions-eg-mint-ubuntu-debian"><a href="#for-debian-based-distributions-eg-mint-ubuntu-debian" name="for-debian-based-distributions-eg-mint-ubuntu-debian" class="anchor"><span class="octicon octicon-link"></span>For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</a></h5>
+<h5 id="for-debian-based-distributions-e.g.-mint-ubuntu-debian">For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</h5>
 <p>run</p>
-<pre><code class="language-bash"> sudo apt-get install gradle git
-</code></pre>
-<h5 id="for-rpm-based-distributions-eg-fedora-centos-redhat"><a href="#for-rpm-based-distributions-eg-fedora-centos-redhat" name="for-rpm-based-distributions-eg-fedora-centos-redhat" class="anchor"><span class="octicon octicon-link"></span>for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</a></h5>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="fu">sudo</span> apt-get install gradle git</code></pre></div>
+<h5 id="for-rpm-based-distributions-e.g.-fedora-centos-redhat">for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</h5>
 <p>run</p>
-<pre><code class="language-bash">sudo yum install gradle git
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">sudo</span> yum install gradle git</code></pre></div>
 <p>If you have some other version of linux you'll probably be able to work it out!</p>
-<h2 id="downloading-the-jalview-source-tree"><a href="#downloading-the-jalview-source-tree" name="downloading-the-jalview-source-tree" class="anchor"><span class="octicon octicon-link"></span>Downloading the Jalview source tree</a></h2>
-<p>This can be done with <code>git</code>.
-On the command line, change directory to where you want to download Jalview's build-tree
-top level directory.  Then run</p>
-<pre><code class="language-bash">git clone http://source.jalview.org/git/jalview.git
-</code></pre>
-<p>You'll get some progress output and after a minute or two you should have the full
-Jalview build-tree in the folder <code>jalview</code>.</p>
-<h3 id="whats-in-the-source-tree"><a href="#whats-in-the-source-tree" name="whats-in-the-source-tree" class="anchor"><span class="octicon octicon-link"></span>What's in the source tree?</a></h3>
-<p>Jalview is a mature product with its codebase going back many years.  As such it doesn't
-have a folder structure that most new gradle projects would have, so you might not
-find everything in the place you might expect.  Here's a brief description of what
-you might find in the main folders under the <code>jalview</code> tree.</p>
+<h2 id="downloading-the-jalview-source-tree">Downloading the Jalview source tree</h2>
+<p>This can be done with <code>git</code>. On the command line, change directory to where you want to download Jalview's build-tree top level directory. Then run</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">git</span> clone http://source.jalview.org/git/jalview.git</code></pre></div>
+<p>You'll get some progress output and after a minute or two you should have the full Jalview build-tree in the folder <code>jalview</code>.</p>
+<h3 id="whats-in-the-source-tree">What's in the source tree?</h3>
+<p>Jalview is a mature product with its codebase going back many years. As such it doesn't have a folder structure that most new gradle projects would have, so you might not find everything in the place you might expect. Here's a brief description of what you might find in the main folders under the <code>jalview</code> tree.</p>
 <p>Within the <code>jalview</code> folder you will find (of possible interest):</p>
 <table>
+<colgroup>
+<col width="16%" />
+<col width="83%" />
+</colgroup>
 <thead>
-<tr><th>dir/ or file</th><th>contains</th></tr>
+<tr class="header">
+<th>dir/ or file</th>
+<th>contains</th>
+</tr>
 </thead>
 <tbody>
-<tr><td><code>bin/</code></td><td>used by eclipse for compiled classes -- no need to touch this</td></tr>
-<tr><td><code>build/</code></td><td>the gradle build dir</td></tr>
-<tr><td><code>classes/</code></td><td>contains the compiled Java classes for the Jalview application</td></tr>
-<tr><td><code>dist/</code></td><td>assembled <code>.jar</code> files needed to run Jalview application</td></tr>
-<tr><td><code>examples/</code></td><td>example input files usable by Jalview</td></tr>
-<tr><td><code>getdown/</code></td><td>the libraries used by the Javliew launcher (getdown)</td></tr>
-<tr><td><code>getdown/src/</code></td><td>our modified source for <code>getdown</code></td></tr>
-<tr><td><code>getdown/website/</code></td><td>the assembled &quot;download&quot; folder used by getdown for downloads/upgrades</td></tr>
-<tr><td><code>getdown/files/</code></td><td>the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application</td></tr>
-<tr><td><code>help/</code></td><td>the help documents</td></tr>
-<tr><td><code>j8lib/</code></td><td>libraries needed to run Jalview under Java 1.8</td></tr>
-<tr><td><code>j11lib/</code></td><td>libraries needed to run Jalivew under Java 11</td></tr>
-<tr><td><code>resource/</code></td><td>non-java resources used in the Jalview application</td></tr>
-<tr><td><code>src/</code></td><td>the Jalview application source <code>.java</code> files</td></tr>
-<tr><td><code>test/</code></td><td>Test class source files</td></tr>
-<tr><td><code>utils/</code></td><td>helper applications used in the build process</td></tr>
-<tr><td><code>utils/install4j/</code></td><td>files used by the packaging tool, install4j</td></tr>
-<tr><td><code>build.gradle</code></td><td>the build file used by gradle</td></tr>
-<tr><td><code>gradle.properties</code></td><td>configurable properties for the build process</td></tr>
-<tr><td><code>RELEASE</code></td><td>propertyfile configuring JALVIEW_VERSION (from jalview.version) and the release branch (from jalview.release). An alternative file can be specified via JALVIEW_RELEASE_FILE property</td></tr>
+<tr class="odd">
+<td><code>bin/</code></td>
+<td>used by eclipse for compiled classes -- no need to touch this</td>
+</tr>
+<tr class="even">
+<td><code>build/</code></td>
+<td>the gradle build dir</td>
+</tr>
+<tr class="odd">
+<td><code>classes/</code></td>
+<td>contains the compiled Java classes for the Jalview application</td>
+</tr>
+<tr class="even">
+<td><code>dist/</code></td>
+<td>assembled <code>.jar</code> files needed to run Jalview application</td>
+</tr>
+<tr class="odd">
+<td><code>examples/</code></td>
+<td>example input files usable by Jalview</td>
+</tr>
+<tr class="even">
+<td><code>getdown/</code></td>
+<td>the libraries used by the Javliew launcher (getdown)</td>
+</tr>
+<tr class="odd">
+<td><code>getdown/src/</code></td>
+<td>our modified source for <code>getdown</code></td>
+</tr>
+<tr class="even">
+<td><code>getdown/website/</code></td>
+<td>the assembled &quot;download&quot; folder used by getdown for downloads/upgrades</td>
+</tr>
+<tr class="odd">
+<td><code>getdown/files/</code></td>
+<td>the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application</td>
+</tr>
+<tr class="even">
+<td><code>help/</code></td>
+<td>the help documents</td>
+</tr>
+<tr class="odd">
+<td><code>j8lib/</code></td>
+<td>libraries needed to run Jalview under Java 1.8</td>
+</tr>
+<tr class="even">
+<td><code>j11lib/</code></td>
+<td>libraries needed to run Jalivew under Java 11</td>
+</tr>
+<tr class="odd">
+<td><code>resource/</code></td>
+<td>non-java resources used in the Jalview application</td>
+</tr>
+<tr class="even">
+<td><code>src/</code></td>
+<td>the Jalview application source <code>.java</code> files</td>
+</tr>
+<tr class="odd">
+<td><code>test/</code></td>
+<td>Test class source files</td>
+</tr>
+<tr class="even">
+<td><code>utils/</code></td>
+<td>helper applications used in the build process</td>
+</tr>
+<tr class="odd">
+<td><code>utils/install4j/</code></td>
+<td>files used by the packaging tool, install4j</td>
+</tr>
+<tr class="even">
+<td><code>build.gradle</code></td>
+<td>the build file used by gradle</td>
+</tr>
+<tr class="odd">
+<td><code>gradle.properties</code></td>
+<td>configurable properties for the build process</td>
+</tr>
 </tbody>
 </table>
 <p>Note that you need a Java 11 JDK to compile Jalview whether your target build is Java 1.8 or Java 11.</p>
-<h2 id="building-jalview"><a href="#building-jalview" name="building-jalview" class="anchor"><span class="octicon octicon-link"></span>Building Jalview</a></h2>
-<p>You will need to have the Java 11 <code>javac</code> in your path, or alternatively you can configure
-gradle to know where this is by putting</p>
-<pre><code>org.gradle.java.home=/path_to_jdk_directory
-</code></pre>
+<h2 id="building-jalview">Building Jalview</h2>
+<p>You will need to have the Java 11 <code>javac</code> in your path, or alternatively you can configure gradle to know where this is by putting</p>
+<pre><code>org.gradle.java.home=/path_to_jdk_directory</code></pre>
 <p>in the <code>gradle.properties</code> file.</p>
 <blockquote>
 <p><em>You may want to see some of the other properties you can change at the end of this document.</em></p>
 </blockquote>
-<h3 id="minimal-jalview-build"><a href="#minimal-jalview-build" name="minimal-jalview-build" class="anchor"><span class="octicon octicon-link"></span>Minimal Jalview Build</a></h3>
+<h3 id="minimal-jalview-build">Minimal Jalview Build</h3>
 <p>To compile the necessary class files, just run</p>
-<pre><code class="language-bash">gradle compileJava
-</code></pre>
-<p>to compile the classes into the <code>classes</code> folder.
-You should now be able to run the Jalview application directly with</p>
-<pre><code class="language-bash">java -cp &quot;classes:resources:help:j11lib/*&quot; jalview.bin.Jalview
-</code></pre>
-<p>You can also run with an automatic large memory setting (which will set the maximum
-memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting
-(if possible in your OS) with</p>
-<pre><code class="language-bash">java -cp &quot;classes:resources:help:j11lib/*&quot; jalview.bin.Launcher
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> compileJava</code></pre></div>
+<p>to compile the classes into the <code>classes</code> folder. You should now be able to run the Jalview application directly with</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<p>You can also run with an automatic large memory setting (which will set the maximum memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting (if possible in your OS) with</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Launcher</code></pre></div>
 <blockquote>
-<p><em>You must use just &quot;<code>j11lib/*</code>&quot; and not &quot;<code>j11lib/*.jar</code>&quot; as this is a special Java
-classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted
-by the shell.</em></p>
+<p><em>You must use just &quot;<code>j11lib/*</code>&quot; and not &quot;<code>j11lib/*.jar</code>&quot; as this is a special Java classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted by the shell.</em></p>
 </blockquote>
-<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code>
-with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code>
-memory setting if one hasn't been provided.</p>
-<h3 id="jalview-in-a-jar-file"><a href="#jalview-in-a-jar-file" name="jalview-in-a-jar-file" class="anchor"><span class="octicon octicon-link"></span>Jalview in a Jar File</a></h3>
+<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code> with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code> memory setting if one hasn't been provided.</p>
+<h3 id="jalview-in-a-jar-file">Jalview in a Jar File</h3>
 <p>To package the <code>classes</code>, <code>resources</code>, and <code>help</code> into one jar, you can run</p>
-<pre><code class="language-bash">gradle jar
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> jar</code></pre></div>
 <p>which assembles the Jalview classes and resources into <code>dist/jalview.jar</code></p>
 <p>To run this, use</p>
-<pre><code class="language-bash">java -cp &quot;dist/jalview.jar:j11lib/*&quot; jalview.bin.Jalview
-</code></pre>
-<h3 id="distributed-jar-files"><a href="#distributed-jar-files" name="distributed-jar-files" class="anchor"><span class="octicon octicon-link"></span>Distributed Jar Files</a></h3>
-<p>To simplify this, all required <code>.jar</code> files can be assembled into the <code>dist</code> folder
-using</p>
-<pre><code class="language-bash">gradle makeDist
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/jalview.jar:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<h3 id="distributed-jar-files">Distributed Jar Files</h3>
+<p>To simplify this, all required <code>.jar</code> files can be assembled into the <code>dist</code> folder using</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> makeDist</code></pre></div>
 <p>which puts all required jar files into <code>dist</code> so you can run with</p>
-<pre><code class="language-bash">java -cp &quot;dist/*&quot; jalview.bin.Jalview
-</code></pre>
-<h3 id="single-shadow-jar-file"><a href="#single-shadow-jar-file" name="single-shadow-jar-file" class="anchor"><span class="octicon octicon-link"></span>Single <em>shadow</em> Jar File</a></h3>
-<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code>
-and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive
-file.  A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code>
-manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn't need to be specified.</p>
-<p>Build the shadow jar file in <code>build/libs/jalview-all-VERSION-j11.jar</code> with</p>
-<pre><code class="language-bash">gradle shadowJar
-</code></pre>
-<p><strong>NB</strong> <code>VERSION</code> will be replaced with a version number or &quot;<code>DEVELOPMENT</code>&quot; or &quot;<code>TEST</code>&quot; depending on how the branch is set up.</p>
-<p>Run it with</p>
-<pre><code class="language-bash">java -jar build/libs/jalview-all-VERSION-j11.jar
-</code></pre>
-<p>Because no arguments are required, most OSes will associate a <code>.jar</code> file with the
-<code>java</code> application (if this has been installed through the OS and not just a local
-unzip) as a <code>-jar</code> argument so you may find you can launch <code>jalview-all-VERSION-j11.jar</code>
-just by double-clicking on it)!</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<h3 id="single-shadow-jar-file">Single <em>shadow</em> Jar File</h3>
+<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code> and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive file. A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code> manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn't need to be specified.</p>
+<p>Build the shadow jar file in <code>build/lib/jalview-all-11.jar</code> with</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar</code></pre></div>
+<p>and run it with</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar build/lib/jalview-all-11.jar</code></pre></div>
+<p>Because no arguments are required, most OSes will associate a <code>.jar</code> file with the <code>java</code> application (if this has been installed through the OS and not just a local unzip) as a <code>-jar</code> argument so you may find you can launch <code>jalview-all-11.jar</code> just by double-clicking on it)!</p>
 <blockquote>
-<p>The <code>shadowJar</code> task is not a requirement for any other task, so to build the shadow
-jar file you must specify the <code>shadowJar</code> task.</p>
+<p>The <code>shadowJar</code> task is not a requirement for any other task, so to build the shadow jar file you must specify the <code>shadowJar</code> task.</p>
 </blockquote>
 <blockquote>
-<p>The shadow jar file represents probably the simplest way to distribute the Jalview application to machines that already have a Java 11 installed,
-although without the many and compelling benefits of the <code>getdown</code> launcher.</p>
+<p>The shadow jar file represents probably the simplest way to distribute the Jalview application to machines that already have a Java 11 installed, although without the many and compelling benefits of the <code>getdown</code> launcher.</p>
 </blockquote>
-<h3 id="building-the-getdown-launcher"><a href="#building-the-getdown-launcher" name="building-the-getdown-launcher" class="anchor"><span class="octicon octicon-link"></span>Building the <code>getdown</code> launcher</a></h3>
-<p>We have made significant customisations to the <code>getdown</code> launcher which you can find
-in <code>getdown/src/getdown</code>.</p>
+<h3 id="building-the-getdown-launcher">Building the <code>getdown</code> launcher</h3>
+<p>We have made significant customisations to the <code>getdown</code> launcher which you can find in <code>getdown/src/getdown</code>.</p>
 <blockquote>
-<p>You don't need to build this afresh as the required <code>gradle-core.jar</code>
-and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then
-you'll need a working Maven and also a Java 8 JDK.  Ensure the Java 8 <code>javac</code> is forefront
-in your path and do</p>
-<pre><code class="language-bash">cd getdown/src/getdown
-mvn clean package -Dgetdown.host.whitelist=&quot;jalview.org,*.jalview.org&quot;
-</code></pre>
-<p>and you will find the required <code>.jar</code> files in <code>core/target/gradle-core-XXX.jar</code>
-and <code>launcher/target/gradle-launcher-XXX.jar</code>.  The <code>gradle-core.jar</code> should then be copied
-to all three of the <code>j8lib</code>, <code>j11lib</code> and <code>getdown/lib</code> folders, whilst the <code>gradle-launcher.jar</code> only
-needs to be copied to <code>getdown/lib</code>.</p>
-<p>The <code>mvn</code> command should ideally include the <code>-Dgetdown.host.whitelist=*.jalview.org</code> setting.
-This, and the necessary file copying commands, can be found in <code>getdown/src/getdown/mvn_cmd</code>.</p>
+<p>You don't need to build this afresh as the required <code>gradle-core.jar</code> and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 <code>javac</code> is forefront in your path and do</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="bu">cd</span> getdown/src/getdown
+<span class="ex">mvn</span> clean package -Dgetdown.host.whitelist=<span class="st">&quot;jalview.org,*.jalview.org&quot;</span></code></pre></div>
+<p>and you will find the required <code>.jar</code> files in <code>core/target/gradle-core-XXX.jar</code> and <code>launcher/target/gradle-launcher-XXX.jar</code>. The <code>gradle-core.jar</code> should then be copied to all three of the <code>j8lib</code>, <code>j11lib</code> and <code>getdown/lib</code> folders, whilst the <code>gradle-launcher.jar</code> only needs to be copied to <code>getdown/lib</code>.</p>
+<p>The <code>mvn</code> command should ideally include the <code>-Dgetdown.host.whitelist=*.jalview.org</code> setting. This, and the necessary file copying commands, can be found in <code>getdown/src/getdown/mvn_cmd</code>.</p>
 </blockquote>
 <p>To assemble Jalview with <code>getdown</code> use the following gradle task:</p>
-<pre><code class="language-bash">gradle getdown
-</code></pre>
-<p>This puts all the necessary files to launch Jalview with <code>getdown</code>
-into <code>getdown/website/11/</code>.  This could be treated as the reference folder
-for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application
-files it has are up to date, and download if they aren't or it simply doesn't have
-them.</p>
-<p>A minimal getdown-launcher can be found in <code>getdown/files/11/</code> which checks its up-to-date
-status with (the absolute path to) <code>getdown/website/11/</code>.</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown</code></pre></div>
+<p>This puts all the necessary files to launch Jalview with <code>getdown</code> into <code>getdown/website/11/</code>. This could be treated as the reference folder for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application files it has are up to date, and download if they aren't or it simply doesn't have them.</p>
+<p>A minimal getdown-launcher can be found in <code>getdown/files/11/</code> which checks its up-to-date status with (the absolute path to) <code>getdown/website/11/</code>.</p>
 <p>This can be launched with</p>
-<pre><code class="language-bash">java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview</code></pre></div>
 <blockquote>
-<p>We've already met the <code>-jar file.jar</code> arguments.  The next argument is the working folder for
-getdown, and the final argument, &quot;<code>jalview</code>&quot;, is a getdown application id (only &quot;<code>jalview</code>&quot;
-is defined here).</p>
+<p>We've already met the <code>-jar file.jar</code> arguments. The next argument is the working folder for getdown, and the final argument, &quot;<code>jalview</code>&quot;, is a getdown application id (only &quot;<code>jalview</code>&quot; is defined here).</p>
 </blockquote>
-<h3 id="running-tests"><a href="#running-tests" name="running-tests" class="anchor"><span class="octicon octicon-link"></span>Running tests</a></h3>
+<h3 id="running-tests">Running tests</h3>
 <p>There are substantial tests written for Jalview that use TestNG, which you can run with</p>
-<pre><code class="language-bash">gradle test
-</code></pre>
-<p>These normally take around 5 - 10 minutes to complete and outputs its full results into
-the <code>tests/</code> folder.  A summary of results should appear in your console.</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test</code></pre></div>
+<p>These normally take around 5 - 10 minutes to complete and outputs its full results into the <code>tests/</code> folder. A summary of results should appear in your console.</p>
 <p>You can run different defined groups of tests with</p>
-<pre><code class="language-bash">gradle test -PtestngGroups=Network
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngGroups=Network</code></pre></div>
 <p>Available groups include Functional (default), Network, External.</p>
-<h4 id="excluding-some-tests"><a href="#excluding-some-tests" name="excluding-some-tests" class="anchor"><span class="octicon octicon-link"></span>Excluding some tests</a></h4>
+<h4 id="excluding-some-tests">Excluding some tests</h4>
 <p>Some of Jalview's Functional tests don't pass reliably in all environments. We tag these tests with a group like 'Not-bamboo' to mark them for exclusion when we run tests as part of continuous integration.</p>
 <p>To exclude one or more groups of tests, add them as a comma separated list in testngExcludedGroups.</p>
-<pre><code class="language-bash">gradle test -PtestngExcludedGroups=Not-bamboo
-</code></pre>
-<h3 id="installer-packaging-with-install4j"><a href="#installer-packaging-with-install4j" name="installer-packaging-with-install4j" class="anchor"><span class="octicon octicon-link"></span>Installer packaging with <em>install4j</em></a></h3>
-<p>Jalview is currently using <em>install4j</em> <a href="https://www.ej-technologies.com/products/install4j/overview.html">https://www.ej-technologies.com/products/install4j/overview.html</a>
-as its installer packaging tool.</p>
-<p>If you have a licensed installation of <em>install4j</em> you can build Jalview installers
-by running</p>
-<pre><code class="language-bash">gradle installers
-</code></pre>
-<p>though you may need to fiddle with the <code>install4j</code> and <code>copyInstall4jTemplate</code> tasks
-in <code>build.gradle</code> file to point to your installation of <em>install4j</em> and also to bundled
-JREs if you want to bundle those into the installers.</p>
-<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org">jalview-dev@jalview.org</a>.
-Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
-<h2 id="gradle-properties"><a href="#gradle-properties" name="gradle-properties" class="anchor"><span class="octicon octicon-link"></span>Gradle properties</a></h2>
-<p>There are a lot of properties configured in <code>gradle.properties</code> which we strongly recommend
-being left as they are unless you have a specific problem with the build process.</p>
-<p>There are a few gradle properties you might want to set on the command line with the
-<code>-P</code> flag when building a version of Jalview with specific requirements:</p>
-<h4 id="java-version"><a href="#java-version" name="java-version" class="anchor"><span class="octicon octicon-link"></span><code>JAVA_VERSION</code></a></h4>
-<p>This changes the <em>target</em> java bytecode version</p>
-<blockquote>
-<p>NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build
-Jalview for any byte-code target version.</p>
-</blockquote>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngExcludedGroups=Not-bamboo</code></pre></div>
+<h3 id="installer-packaging-with-install4j">Installer packaging with <em>install4j</em></h3>
+<p>Jalview is currently using <em>install4j</em> <a href="https://www.ej-technologies.com/products/install4j/overview.html" class="uri">https://www.ej-technologies.com/products/install4j/overview.html</a> as its installer packaging tool.</p>
+<p>If you have a licensed installation of <em>install4j</em> you can build Jalview installers by running</p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers</code></pre></div>
+<p>though you may need to fiddle with the <code>install4j</code> and <code>copyInstall4jTemplate</code> tasks in <code>build.gradle</code> file to point to your installation of <em>install4j</em> and also to bundled JREs if you want to bundle those into the installers.</p>
+<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org">jalview-dev@jalview.org</a>. Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev" class="uri">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
+<h2 id="gradle-properties">Gradle properties</h2>
+<p>There are a lot of properties configured in <code>gradle.properties</code> which we strongly recommend being left as they are unless you have a specific problem with the build process.</p>
+<p>There are a few gradle properties you might want to set on the command line with the <code>-P</code> flag when building a version of Jalview with specific requirements:</p>
+<h4 id="java_version"><code>JAVA_VERSION</code></h4>
+<p>This changes the <em>target</em> java bytecode version &gt; NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build Jalview for any byte-code target version.</p>
 <p>Valid values are <code>11</code> and <code>1.8</code>.</p>
 <p>e.g.</p>
-<pre><code class="language-bash">gradle shadowJar -PJAVA_VERSION=1.8
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar -PJAVA_VERSION=1.8</code></pre></div>
 <p>When using <code>-PJAVA_VERSION=1.8</code> the libraries from <code>j8lib</code> (instead of <code>j11lib</code>) will be used in the compile<br />
-and runtime classpath and also used in the <code>makeDist</code> build step.  Where a Java version of <code>11</code> is used in folder and file names, it will
-instead use <code>1.8</code>.  Also if you are building installer packages with <em>install4j</em> the
-package builder will look for JRE 1.8 bundles to package in the installers.</p>
+and runtime classpath and also used in the <code>makeDist</code> build step. Where a Java version of <code>11</code> is used in folder and file names, it will instead use <code>1.8</code>. Also if you are building installer packages with <em>install4j</em> the package builder will look for JRE 1.8 bundles to package in the installers.</p>
 <blockquote>
-<p>Note that continued development of Jalview will assume a Java 11+ runtime environment,
-the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.</p>
+<p>Note that continued development of Jalview will assume a Java 11+ runtime environment, the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.</p>
 </blockquote>
-<h4 id="channel"><a href="#channel" name="channel" class="anchor"><span class="octicon octicon-link"></span><code>CHANNEL</code></a></h4>
-<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher
-looks to see if there's an updated file) to point to a particular Jalview channel or some other appropriate
-place to look for required files.  If the selected channel type requires the getdown <code>appbase</code> to be a local
-directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will
-be used to allow this.  The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>.
-Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code>
-to allow smooth operation of getdown in the given context.</p>
-<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>.  Here's what they're for and what they do:</p>
+<h4 id="channel"><code>CHANNEL</code></h4>
+<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher looks to see if there's an updated file) to point to a particular Jalview channel or some other appropriate place to look for required files. If the selected channel type requires the getdown <code>appbase</code> to be a local directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will be used to allow this. The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>. Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code> to allow smooth operation of getdown in the given context.</p>
+<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>. Here's what they're for and what they do:</p>
 <ul>
-<li><code>LOCAL</code>: This is for running the compiled application from the development directory.
-It will set
+<li><code>LOCAL</code>: This is for running the compiled application from the development directory. It will set
 <ul>
-<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code>
-(e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
+<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code> (e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>BUILD</code>: This is for creating an appbase channel on the build server by an automatic or manually started build.
-It will set
+</ul></li>
+<li><code>BUILD</code>: This is for creating an appbase channel on the build server by an automatic or manually started build. It will set
 <ul>
-<li><code>appbase</code> as <code>https://builds.jalview.org/browse/${bamboo_planKey}/latest/artifact/shared/getdown-channel/JAVA_VERSION</code>
-Note that bamboo_planKey should be set by the build plan with <code>-Pbamboo_planKey=${bamboo.planKey}</code></li>
+<li><code>appbase</code> as <code>https://builds.jalview.org/browse/${bamboo_planKey}/latest/artifact/shared/getdown-channel/JAVA_VERSION</code> Note that bamboo_planKey should be set by the build plan with <code>-Pbamboo_planKey=${bamboo.planKey}</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server.
-It will set
+</ul></li>
+<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/develop/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server.  This won't become live until the actual getdown artefact is synced to the web server.  This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels.  The value of <code>NAME</code> can be any &quot;word-character&quot; [A-Za-z0-9_]
-It will set
+</ul></li>
+<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. The value of <code>NAME</code> can be any &quot;word-character&quot; [A-Za-z0-9_] It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/SCRATCH-NAME/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>TEST-LOCAL</code>:  Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase.  This is meant for testing an over-the-air update on the local filesystem.  An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>)
-It will set
+</ul></li>
+<li><code>TEST-LOCAL</code>: Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>) It will set
 <ul>
 <li><code>appbase</code> as <code>file://${LOCALDIR}</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>TEST-RELEASE</code>:  Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.  This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels.
-It will set
+</ul></li>
+<li><code>TEST-RELEASE</code>: Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/test-release/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>RELEASE</code>:  This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.
-It will set
+</ul></li>
+<li><code>RELEASE</code>: This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/release/JAVA_VERSION</code></li>
 <li>application subdir as <code>release</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>ARCHIVE</code>:  This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.
-You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files.  This should create a getdown structure and digest with the older jar files.
-It will set
+</ul></li>
+<li><code>ARCHIVE</code>: This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/archive/JALVIEW_VERSION/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
-<li><code>ARCHIVELOCAL</code>:  Like <code>ARCHIVE</code> but with a local filesystem appbase for local testing.
-You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files.  This should create a getdown structure and digest with the older jar files.
-It will set
+</ul></li>
+<li><code>ARCHIVELOCAL</code>: Like <code>ARCHIVE</code> but with a local filesystem appbase for local testing. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
 <ul>
 <li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/website/JAVA_VERSION</code> (where the old jars will have been copied and digested)</li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul>
-</li>
+</ul></li>
 </ul>
 <p>e.g.</p>
-<pre><code class="language-bash">gradle getdown -PCHANNEL=SCRATCH-my_test_version
-</code></pre>
-<h4 id="jalview-version-and-the-release-file"><a href="#jalview-version-and-the-release-file" name="jalview-version-and-the-release-file" class="anchor"><span class="octicon octicon-link"></span>JALVIEW_VERSION and the RELEASE file</a></h4>
-<p>Any Jalview build will include the value of JALVIEW_VERSION in various places, including the 'About' and Jalview Desktop window title, and in filenames for the stand-alone executable jar. You can specify a custom version for a build via the JALVIEW_VERSION property, but for most situations, JALVIEW_VERSION will be automatically configured according to the value of the CHANNEL property, using the <code>jalview.version</code> property specified in the RELEASE file:</p>
-<ul>
-<li><code>CHANNEL=RELEASE</code> will set version to jalview.version</li>
-<li><code>CHANNEL=TEST or DEVELOP</code> will append '-test' or '-develop' to jalview.version</li>
-</ul>
-<p>It is also possible to specify a custom location for the RELEASE file via an optional JALVIEW_RELEASE_FILE property.</p>
-<h4 id="install4jmediatypes"><a href="#install4jmediatypes" name="install4jmediatypes" class="anchor"><span class="octicon octicon-link"></span><code>install4jMediaTypes</code></a></h4>
-<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated
-list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
-<p>Currently the valid values are
-<code>linuxDeb</code>,
-<code>linuxRPM</code>,
-<code>macosArchive</code>,
-<code>unixArchive</code>,
-<code>unixInstaller</code>,
-<code>windows</code></p>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown -PCHANNEL=SCRATCH-my_test_version</code></pre></div>
+<h4 id="install4jmediatypes"><code>install4jMediaTypes</code></h4>
+<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
+<p>Currently the valid values are <code>linuxDeb</code>, <code>linuxRPM</code>, <code>macosArchive</code>, <code>unixArchive</code>, <code>unixInstaller</code>, <code>windows</code></p>
 <p>The default value is all of them.</p>
 <p>e.g.</p>
-<pre><code class="language-bash">gradle installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive</code></pre></div>
 <p>To get an up-to-date list of possible values, you can run</p>
-<pre><code class="language-bash">perl -n -e 'm/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;' utils/install4j/install4j_template.install4j  | sort -u
-</code></pre>
+<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">perl</span> -n -e <span class="st">&#39;m/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;&#39;</span> utils/install4j/install4j_template.install4j  <span class="kw">|</span> <span class="fu">sort</span> -u</code></pre></div>
 <p>in the <code>jalview</code> root folder.</p>
-<h2 id="enabling-code-coverage-with-openclover"><a href="#enabling-code-coverage-with-openclover" name="enabling-code-coverage-with-openclover" class="anchor"><span class="octicon octicon-link"></span>Enabling Code Coverage with OpenClover</a></h2>
+<h2 id="enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</h2>
 <p>Bytecode instrumentation tasks are enabled by specifying 'true' (or just a non-whitespace non-numeric word) in the 'clover' property. This adds the 'openclover' plugin to the build script's classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.</p>
 <p><code>gradle -Pclover=true test cloverReport</code></p>
-<h4 id="troubleshooting-report-generation"><a href="#troubleshooting-report-generation" name="troubleshooting-report-generation" class="anchor"><span class="octicon octicon-link"></span>Troubleshooting report generation</a></h4>
+<h4 id="troubleshooting-report-generation">Troubleshooting report generation</h4>
 <p>The build forks a new JVM process to run the clover report generation tools (both XML and HTML reports are generated by default). The following properties can be used to specify additional options or adjust JVM memory settings. Default values for these options are:</p>
-<h5 id="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported"><a href="#jvm-memory-settings---increase-if-out-of-memory-errors-are-reported" name="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported" class="anchor"><span class="octicon octicon-link"></span>JVM Memory settings - increase if out of memory errors are reported</a></h5>
+<h5 id="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported">JVM Memory settings - increase if out of memory errors are reported</h5>
 <p><code>cloverReportJVMHeap = 2g</code></p>
-<h5 id="-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space"><a href="#-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space" name="-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space" class="anchor"><span class="octicon octicon-link"></span>-Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.</a></h5>
+<h5 id="dfile.encodingutf-8-is-an-essential-parameters-for-report-generation.-add-additional-ones-separated-by-a-space.">-Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.</h5>
 <p><code>cloverReportJVMArgs = -Dfile.encoding=UTF-8</code></p>
-<h5 id="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database"><a href="#add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database" name="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database" class="anchor"><span class="octicon octicon-link"></span>Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database</a></h5>
+<h5 id="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database">Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database</h5>
 <p><code>cloverReportHTMLOptions =</code></p>
-<h5 id="-v-for-verbose--d-for-debug-level-messages-as-above"><a href="#-v-for-verbose--d-for-debug-level-messages-as-above" name="-v-for-verbose--d-for-debug-level-messages-as-above" class="anchor"><span class="octicon octicon-link"></span>-v for verbose, -d for debug level messages (as above)</a></h5>
+<h5 id="v-for-verbose--d-for-debug-level-messages-as-above">-v for verbose, -d for debug level messages (as above)</h5>
 <p><code>cloverReportXMLOptions =</code></p>
 <p><em>Note</em> do not forget to include the -Dfile.encoding=UTF-8 option: this is essential for some platforms in order for Clover to correctly parse some Jalview source files that contain characters that are UTF-8 encoded.</p>
-<h2 id="setting-up-in-eclipse-ide"><a href="#setting-up-in-eclipse-ide" name="setting-up-in-eclipse-ide" class="anchor"><span class="octicon octicon-link"></span>Setting up in Eclipse IDE</a></h2>
-<h3 id="installing-eclipse-ide"><a href="#installing-eclipse-ide" name="installing-eclipse-ide" class="anchor"><span class="octicon octicon-link"></span>Installing Eclipse IDE</a></h3>
-<p>We develop in Eclipse, and support settings to develop and save Jalview source code
-in our preferred style.  We also support running the Jalview application, debugging
-and running tests with TestNG from within Eclipse.</p>
-<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2020-03
-version of Eclipse IDE for Java Developers which you can download from the Eclipse
-website: <a href="https://www.eclipse.org/downloads/">https://www.eclipse.org/downloads/</a>.  Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page).
-In the installer, when given a choice of packages for Eclipse you should choose the &quot;Eclipse IDE for Enterprise Java Developers&quot; package.</p>
-<p><img src="./images/eclipse_installer.png" alt="" title="Eclipse Installer screenshot" /></p>
+<h2 id="setting-up-in-eclipse-ide">Setting up in Eclipse IDE</h2>
+<h3 id="installing-eclipse-ide">Installing Eclipse IDE</h3>
+<p>We develop in Eclipse, and support settings to develop and save Jalview source code in our preferred style. We also support running the Jalview application, debugging and running tests with TestNG from within Eclipse.</p>
+<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-12 version of Eclipse IDE for Java Developers which you can download from the Eclipse website: <a href="https://www.eclipse.org/downloads/" class="uri">https://www.eclipse.org/downloads/</a>. Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page). In the installer, when given a choice of packages for Eclipse you should choose the &quot;Eclipse IDE for Enterprise Java Developers&quot; package.</p>
+<div class="figure">
+<img src="./images/eclipse_installer.png" title="Eclipse Installer screenshot" />
+
+</div>
 <p>Once Eclipse is installed, we also recommend installing several plugins from the Eclipse Marketplace.</p>
 <p>Some of these should already be installed with the Enterprise Java Developer package:</p>
-<ol>
+<ol style="list-style-type: decimal">
 <li>Buildship Gradle Integration 3.0 (or greater)</li>
 <li>EclEmma Java Code Coverage</li>
 <li>Egit - Git Integration for Eclipse</li>
 </ol>
 <p>To install the others, launch Eclipse, and go to Help-&gt;Eclipse Marketplace...</p>
 <p>Search for and install:</p>
-<ol>
+<ol style="list-style-type: decimal">
 <li>Groovy Development Tools 3.4.0 (or greater)</li>
 <li>Checkstyle Plug-in (optional)</li>
 <li>TestNG for Eclipse (optional -- only needed if you want to run tests from Eclipse)</li>
 </ol>
 <blockquote>
-<p>At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace
-as the latest released version does not install in Eclipse 2020-03.
-However, you can install a working release of TestNG for Eclipse by going to</p>
+<p>At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace as the latest released version does not install in Eclipse 2019-03. However, you can install a working release of TestNG for Eclipse by going to</p>
 <p>Help-&gt;Install New Software...</p>
 <p>and entering</p>
 <p><code>TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release</code></p>
 <p>into the <em>Work with</em> box and click on the <em>Add...</em> button.</p>
-<p>Eclipse might pause for a bit with the word <em>Pending</em> in the table below at this point, but it will eventually list TestNG with
-a selection box under the <em>Name</em> column.</p>
-<p>Select <em>TestNG</em> and carry on through the
-install process to install the TestNG plugin.</p>
+<p>Eclipse might pause for a bit with the word <em>Pending</em> in the table below at this point, but it will eventually list TestNG with a selection box under the <em>Name</em> column.</p>
+<p>Select <em>TestNG</em> and carry on through the install process to install the TestNG plugin.</p>
 </blockquote>
 <p>After installing the plugins, check that Java 11 is set up in Eclipse as the default JRE (see section <a href="#java-11-compliant-jdk">Java 11 compliant JDK</a>).</p>
-<p>To do this go to Preferences (Eclipse-&gt;Preferences in macOS, File-&gt;Preferences
-on Windows or Window-&gt;Preferences on Linux) and find</p>
+<p>To do this go to Preferences (Eclipse-&gt;Preferences in macOS, File-&gt;Preferences on Windows or Window-&gt;Preferences on Linux) and find</p>
 <p>Java -&gt; Installed JREs</p>
 <p>If your Java 11 installation is not listed, click on</p>
 <p><em>Add</em> -&gt; Standard VM -&gt; <em>Next</em></p>
-<p>and enter the JRE home.  You can browse to where it is installed. Give it a name (like &quot;AdoptOpenJDK 11&quot;).  Select this JDK
-as the default JRE and click on <em>Apply and Close</em>.</p>
+<p>and enter the JRE home. You can browse to where it is installed. Give it a name (like &quot;AdoptOpenJDK 11&quot;). Select this JDK as the default JRE and click on <em>Apply and Close</em>.</p>
 <p>You can now import Jalview.</p>
-<h3 id="importing-jalview-as-an-eclipse-project"><a href="#importing-jalview-as-an-eclipse-project" name="importing-jalview-as-an-eclipse-project" class="anchor"><span class="octicon octicon-link"></span>Importing Jalview as an Eclipse project</a></h3>
-<h4 id="importing-an-already-downloaded-git-repo"><a href="#importing-an-already-downloaded-git-repo" name="importing-an-already-downloaded-git-repo" class="anchor"><span class="octicon octicon-link"></span>Importing an already downloaded git repo</a></h4>
+<h3 id="importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</h3>
+<h4 id="importing-an-already-downloaded-git-repo">Importing an already downloaded git repo</h4>
 <p>If you have already downloaded Jalview using <code>git clone</code> then you can import this folder into Eclipse directly.</p>
-<p><strong>Before importing the cloned git repo you must create the Eclipse project files.</strong> You can do this by either running</p>
-<p><code>gradle eclipse</code></p>
-<p>or</p>
-<p>Unzipping the file <code>utils/eclipse/eclipse_startup_files.zip</code> in the base repo directory (<code>jalview</code>)</p>
-<p>It is important to import
-Jalview as a Gradle project (not as a Java project), so go to</p>
+<p>It is important to import Jalview as a Gradle project (not as a Java project), so go to</p>
 <p>File-&gt;Import...</p>
 <p>find and select</p>
 <p>Gradle-&gt;Existing Gradle Project</p>
 <p>and then click on the <em>Next</em> button.</p>
-<p>In the following options, it is the <strong>Project Root Directory</strong> you should set to be the
-<code>jalview</code> folder that git downloaded.  Then you can click on the <em>Finish</em> button.</p>
-<h4 id="using-eclipse-ide-to-download-the-git-repo"><a href="#using-eclipse-ide-to-download-the-git-repo" name="using-eclipse-ide-to-download-the-git-repo" class="anchor"><span class="octicon octicon-link"></span>Using Eclipse IDE to download the git repo</a></h4>
-<p>If you don't have git as a command line tool or would prefer to work entirely within Eclipse IDE then
-Eclipse's eGit plugin can set up a git repo of the jalview source.  Go to</p>
+<p>In the following options, it is the <strong>Project Root Directory</strong> you should set to be the <code>jalview</code> folder that git downloaded. Then you can click on the <em>Finish</em> button.</p>
+<h4 id="using-eclipse-ide-to-download-the-git-repo">Using Eclipse IDE to download the git repo</h4>
+<p>If you don't have git as a command line tool or would prefer to work entirely within Eclipse IDE then Eclipse's eGit plugin can set up a git repo of the jalview source. Go to</p>
 <p>File-&gt;Import...</p>
 <p>Find and select</p>
 <p>Git-&gt;Projects from Git</p>
 <p>and then click on the <em>Next</em> button.</p>
 <p>Then select Clone URI and click on <em>Next</em>.</p>
-<p>In the next window (Source Git Repository) you should put the <code>git clone</code> URL in the text box labelled <code>URI</code>.  If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter
-<code>https://source.jalview.org/git/jalview.git</code>.
-If you do not have a Jalview developer account then you should enter
-<code>http://source.jalview.org/git/jalview.git</code>.
-You will not be able to push any of your changes back to the Jalview git repository. However you can still pull all branches of the Jalview source code to your computer and develop the code there.</p>
-<blockquote>
-<p>You can sign up for a Jalview developer account at <a href="https://source.jalview.org/crucible/">https://source.jalview.org/crucible/</a></p>
-</blockquote>
-<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse's secure storage.  If you don't have an account you can leave the Authentication section blank.</p>
-<p><img src="./images/eclipse_egit_connection.png" alt="Eclipse eGit connection configuration" /></p>
+<p>In the next window (Source Git Repository) you should put the <code>git clone</code> URL in the text box labelled <code>URI</code>. If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter <code>https://source.jalview.org/git/jalview.git</code>. If you do not have a Jalview developer account then you should enter <code>http://source.jalview.org/git/jalview.git</code>. You will not be able to push any of your changes back to the Jalview git repository. However you can still pull all branches of the Jalview source code to your computer and develop the code there. &gt; You can sign up for a Jalview developer account at <a href="https://source.jalview.org/crucible/" class="uri">https://source.jalview.org/crucible/</a></p>
+<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse's secure storage. If you don't have an account you can leave the Authentication section blank.</p>
+<div class="figure">
+<img src="./images/eclipse_egit_connection.png" alt="Eclipse eGit connection configuration" />
+<p class="caption">Eclipse eGit connection configuration</p>
+</div>
 <p>Click on the <em>Next</em> button.</p>
-<p>The next window (Branch Selection) gives a list of the many Jalview branches, which by default will be all checked.  You probably only want to download one branch (you can always download others at a later time).  This is likely to be the <code>develop</code> branch so you can click on the <em>Deselect All</em> button, find the <code>develop</code> branch (the filter text helps), select that, and then click on the <em>Next</em> button.</p>
-<p>Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the <em>Next</em> button.  The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.</p>
-<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>.</p>
-<blockquote>
-<p>Ideally there would be an <em>Import as gradle project</em> here but there isn't -- we'll sort that out later.</p>
-</blockquote>
-<p><img src="./images/eclipse_egit_import.png" alt="Eclipse eGit import choice" /></p>
+<p>The next window (Branch Selection) gives a list of the many Jalview branches, which by default will be all checked. You probably only want to download one branch (you can always download others at a later time). This is likely to be the <code>develop</code> branch so you can click on the <em>Deselect All</em> button, find the <code>develop</code> branch (the filter text helps), select that, and then click on the <em>Next</em> button.</p>
+<p>Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the <em>Next</em> button. The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.</p>
+<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>. &gt; Ideally there would be an <em>Import as gradle project</em> here but there isn't -- we'll sort that out later.</p>
+<div class="figure">
+<img src="./images/eclipse_egit_import.png" alt="Eclipse eGit import choice" />
+<p class="caption">Eclipse eGit import choice</p>
+</div>
 <p>Click on the <em>Next</em> button.</p>
-<p>You can change the project name here.  By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project's name now to avoid a conflict within Eclipse.</p>
+<p>You can change the project name here. By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project's name now to avoid a conflict within Eclipse.</p>
 <p>Click on <em>Finish</em>!</p>
 <p>However, we haven't finished...</p>
-<p>You should now see, and be able to expand, the jalview project in the Project Explorer.  We need to tell eclipse that this is a Gradle project, which will then allow the Eclipse Buildship plugin to automatically configure almost everything else!</p>
+<p>You should now see, and be able to expand, the jalview project in the Project Explorer. We need to tell eclipse that this is a Gradle project, which will then allow the Eclipse Buildship plugin to automatically configure almost everything else!</p>
 <p>Right click on the project name (jalview) in the Project Explorer and find Configure towards the bottom of this long context menu, then choose Add Gradle Nature.</p>
-<p><img src="./images/eclipse_add_gradle_nature.png" alt="Eclipse Add Gradle Nature" /></p>
+<div class="figure">
+<img src="./images/eclipse_add_gradle_nature.png" alt="Eclipse Add Gradle Nature" />
+<p class="caption">Eclipse Add Gradle Nature</p>
+</div>
 <p>The project should now reconfigure itself using the <code>build.gradle</code> file to dynamically set various aspects of the project including classpath.</p>
-<h4 id="additional-views"><a href="#additional-views" name="additional-views" class="anchor"><span class="octicon octicon-link"></span>Additional views</a></h4>
-<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on
-Window-&gt;Show View-&gt;Console
-and
-Window-&gt;Show View-&gt;Other...
-Filter with the word &quot;gradle&quot; and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
-<p>Okay, ready to code!  Use of Eclipse is beyond the scope of this document, but you can find more information about developing jalview and our developer workflow in the google doc <a href="https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing">https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing</a></p>
+<h4 id="additional-views">Additional views</h4>
+<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on Window-&gt;Show View-&gt;Console and Window-&gt;Show View-&gt;Other... Filter with the word &quot;gradle&quot; and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
+<p>Okay, ready to code! Use of Eclipse is beyond the scope of this document, but you can find more information about developing jalview and our developer workflow in the google doc <a href="https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing" class="uri">https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing</a></p>
 <hr />
 <p><a href="mailto:help@jalview.org">Jalview Development Team</a></p>
-
-  </body>
+</body>
 </html>
diff --git a/examples/groovy/hmmertestimport.groovy b/examples/groovy/hmmertestimport.groovy
new file mode 100644 (file)
index 0000000..9f0d34e
--- /dev/null
@@ -0,0 +1,6 @@
+ def alv = new jalview.io.FileLoader().LoadFileWaitTillLoaded("examples/testdata/hmmer3/alignment_res.fa.gz",jalview.io.DataSourceType.FILE).getViewport();
+//def alv = jalview.bin.Jalview.getCurrentAlignFrame().getViewport();
+def al = alv.getAlignment();
+def jproc = new jalview.ws.ebi.HmmerJSONProcessor(al)
+jproc.parseFrom(new jalview.io.FileParse("/Users/jprocter/git/jalview/examples/testdata/hmmer3/hmmeresult.json.gz",jalview.io.DataSourceType.FILE))
+jproc.updateView(alv)
\ No newline at end of file
diff --git a/examples/testdata/hmmer3/alignment_frag.fa.gz b/examples/testdata/hmmer3/alignment_frag.fa.gz
new file mode 100644 (file)
index 0000000..a4e79b2
Binary files /dev/null and b/examples/testdata/hmmer3/alignment_frag.fa.gz differ
diff --git a/examples/testdata/hmmer3/alignment_res.fa.gz b/examples/testdata/hmmer3/alignment_res.fa.gz
new file mode 100644 (file)
index 0000000..03206f4
Binary files /dev/null and b/examples/testdata/hmmer3/alignment_res.fa.gz differ
diff --git a/examples/testdata/hmmer3/hit_fragment.json.gz b/examples/testdata/hmmer3/hit_fragment.json.gz
new file mode 100644 (file)
index 0000000..bbc8405
Binary files /dev/null and b/examples/testdata/hmmer3/hit_fragment.json.gz differ
diff --git a/examples/testdata/hmmer3/hmmeresult.json.gz b/examples/testdata/hmmer3/hmmeresult.json.gz
new file mode 100644 (file)
index 0000000..c3fa9a2
Binary files /dev/null and b/examples/testdata/hmmer3/hmmeresult.json.gz differ
diff --git a/examples/uniref50.hmm b/examples/uniref50.hmm
new file mode 100644 (file)
index 0000000..b07ec14
--- /dev/null
@@ -0,0 +1,466 @@
+HMMER3/f [3.1b2 | February 2015]
+NAME  uniref50
+LENG  148 
+ALPH  amino
+ RF    no
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Mon Jun 12 14:32:05 2017
+NSEQ  15
+EFFN  0.648193
+CKSUM 2563184735
+STATS LOCAL MSV      -10.0682  0.70956
+STATS LOCAL VITERBI  -10.7870  0.70956
+STATS LOCAL FORWARD   -4.6837  0.70956
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   2.40816  3.71583  2.80837  2.63551  3.47092  2.77036  3.85028  2.88279  2.80267  2.53589  3.68425  3.22102  3.34428  3.14560  3.11474  2.53177  2.75990  2.58070  5.00144  3.51101
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.10433  3.97705  2.52157  0.61958  0.77255  0.00000        *
+      1   2.99317  4.53706  4.08224  3.67789  3.22093  3.85692  4.40592  2.39958  3.44433  1.81088  1.32037  3.95163  4.31759  3.82413  3.64608  3.34553  3.29902  2.40928  5.06591  3.83044      1 m - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+      2   0.97044  4.20710  3.49350  3.28598  4.08269  3.03087  4.30444  3.21278  3.30302  3.09300  4.12129  3.42792  3.77296  3.64134  3.55157  2.55705  2.83652  2.86187  5.50678  4.31039      2 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.11955  3.90315  2.38048  0.61958  0.77255  0.55549  0.85282
+      3   1.74035  4.09199  3.34495  3.00819  4.05288  2.91638  4.05509  3.33461  3.01251  3.11462  3.97728  3.20348  3.62940  3.32849  3.32799  1.99191  1.99086  2.89563  5.42641  4.20726      3 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03323  3.81683  4.53917  0.61958  0.77255  0.62561  0.76558
+      4   2.37663  4.18625  3.55838  3.17855  3.75132  3.18264  4.13666  2.64341  3.07328  2.62344  3.69712  3.39774  3.83139  3.44405  3.35689  2.61052  1.64486  2.15973  5.30034  4.05468      4 t - - -
+          2.68617  4.42230  2.77525  2.73129  3.46359  2.40511  3.72500  3.29359  2.67746  2.69350  4.24695  2.90352  2.73719  3.18152  2.89806  2.37885  2.77501  2.98524  4.58482  3.61508
+          0.33937  1.47682  2.82312  0.71438  0.67236  0.50642  0.92294
+      5   1.78828  4.31568  3.18846  2.75366  3.97645  3.11838  3.84000  3.34055  2.67250  3.02453  3.87541  3.10729  3.71676  3.06391  2.67828  2.15246  2.50645  2.96422  5.30410  4.05011      9 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03188  3.85742  4.57976  0.61958  0.77255  0.59400  0.80322
+      6   2.60146  4.34120  4.20318  3.64233  3.23200  4.03946  4.38229  1.91078  3.53317  1.46276  2.80385  3.95250  4.35454  3.78680  3.77614  3.34686  3.12310  1.90138  5.05979  3.90282     10 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03188  3.85742  4.57976  0.61958  0.77255  0.59400  0.80322
+      7   2.51618  4.38929  3.20785  2.68482  3.53842  3.39630  3.71025  2.97988  2.63127  2.66315  3.23915  3.11866  3.35060  2.98059  3.01133  2.11689  2.77574  2.72369  4.95633  3.10650     11 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03188  3.85742  4.57976  0.61958  0.77255  0.59400  0.80322
+      8   1.83112  4.12334  3.20156  2.95461  4.23963  1.79514  4.09699  3.64646  3.07597  3.33878  4.16582  3.16397  3.61350  3.35846  3.40724  1.96001  2.64648  3.10558  5.57497  4.34818     12 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.07518  3.85742  2.97012  0.61958  0.77255  0.59400  0.80322
+      9   2.02467  4.19921  3.47207  3.00878  3.03795  3.27081  3.95597  2.70270  2.96218  2.55248  3.54468  3.31239  3.83177  3.27950  3.28895  2.63385  2.09090  2.46488  5.06606  3.80489     13 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03327  3.81551  4.53786  0.61958  0.77255  0.56041  0.84624
+     10   2.25155  4.19461  3.85704  3.29295  2.92371  3.72128  4.04839  2.07964  3.20853  2.08962  2.71310  3.60847  4.09120  3.47607  3.47945  3.00735  2.74040  2.14097  4.86082  3.65928     14 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03170  3.86316  4.58551  0.61958  0.77255  0.53107  0.88667
+     11   2.97746  4.34766  4.53309  3.96552  2.78843  4.21079  4.53997  1.80103  3.85948  1.59494  2.57669  4.20401  4.48099  4.02954  4.02513  3.53069  3.20806  1.63119  5.03312  3.89134     15 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     12   2.01604  4.15364  3.23013  2.99255  4.26101  2.66741  4.12859  3.66796  3.09383  3.37186  4.20680  3.19709  3.64453  3.39377  3.41395  1.27425  2.68165  3.13093  5.59931  4.36607     16 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     13   2.41396  4.26265  3.40226  2.93569  3.72492  3.24366  3.92462  2.93397  2.83929  2.73560  3.16052  3.25846  3.81309  3.20648  3.16819  2.37913  1.73294  2.65776  5.16309  3.91530     17 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     14   2.06589  4.14828  3.30608  3.02868  4.20053  2.92394  4.12227  3.52677  3.06750  3.27902  4.13704  3.22373  3.65849  3.38850  3.37961  1.31011  2.40891  3.04032  5.55736  4.32788     18 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     15   2.74009  4.29274  3.92243  3.42912  1.74599  3.72053  3.90473  2.32427  3.32349  2.18956  3.31553  3.65797  4.14118  3.57919  3.55745  2.71376  3.01017  2.38146  4.46330  2.97696     19 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     16   2.76094  4.39380  4.31945  3.75589  3.18422  4.12530  4.45889  2.01307  3.63847  1.30728  2.59228  4.05575  4.41925  3.86957  3.85970  3.43754  3.19324  1.94550  5.06650  3.93230     20 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     17   2.70295  4.73637  3.16526  2.68917  4.18427  3.37581  3.71759  3.61851  2.27125  3.18722  4.06871  3.11427  2.39752  2.89725  1.66361  2.77186  2.98335  3.28411  5.34221  4.10051     21 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.07762  3.90315  2.90945  0.61958  0.77255  0.55549  0.85282
+     18   2.49055  4.81399  3.14505  2.59164  4.19520  3.44406  3.60132  3.55142  1.92611  3.12799  3.98038  3.04037  3.86298  2.75679  1.83484  2.76119  2.76042  3.23493  5.31169  4.06564     22 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.06501  3.85742  3.17445  0.61958  0.77255  0.59400  0.80322
+     19   2.43450  4.81939  2.81703  2.39298  4.15804  3.33308  3.59871  3.56807  2.06609  3.14527  3.97247  2.89702  3.36070  2.29211  2.66793  2.54254  2.87166  3.22778  5.35034  4.03520     23 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03294  3.82535  4.54770  0.61958  0.77255  0.61916  0.77305
+     20   2.61608  4.67386  2.79167  2.51004  4.07010  3.23635  3.73836  3.55025  2.48903  3.13320  4.04098  2.97430  2.02723  2.34075  2.83811  2.68076  2.93097  3.21608  5.34739  4.03735     24 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03294  3.82535  4.54770  0.61958  0.77255  0.51069  0.91649
+     21   1.83080  4.22890  3.60014  3.08598  3.61014  3.38577  4.01761  2.55536  3.00939  2.52819  3.20189  3.39791  3.91288  3.33215  3.33591  2.72706  2.41446  2.12040  5.12303  3.89826     25 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     22   2.43845  4.29437  3.39983  3.04145  3.80350  3.19872  4.06195  2.96220  2.98493  2.76612  3.80233  3.32666  1.71751  3.35496  3.29387  2.63325  2.83846  2.21304  5.28839  4.02321     26 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     23   2.55648  4.28453  3.47615  2.92649  3.45346  3.51308  3.86072  2.37030  2.85167  2.40896  2.93550  3.31525  2.97437  3.17776  3.19434  2.79032  2.33896  2.42929  4.94762  3.71651     27 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     24   2.23005  4.31546  3.25841  2.77512  3.78441  3.22479  3.82985  3.12652  2.73582  2.83244  3.03171  3.14960  3.26067  3.07830  3.10838  1.85715  2.73859  2.81468  5.16957  3.91289     28 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     25   2.72216  3.51794  4.14734  3.58168  3.26953  3.85722  4.23682  2.10046  3.47012  1.71220  2.91412  3.83361  4.22096  3.71804  3.68973  3.16541  2.79579  1.71693  4.93656  3.75086     29 l - - -
+          2.68606  4.42227  2.77522  2.73126  3.46337  2.40515  3.72497  3.29356  2.67743  2.69357  4.24692  2.90349  2.73742  3.18149  2.89784  2.37889  2.77522  2.98521  4.58479  3.61506
+          0.31572  1.55442  2.82310  0.27619  1.42159  0.55549  0.85282
+     26   2.06718  4.47388  3.00085  2.59301  4.03403  3.18938  3.75234  3.40130  2.21691  3.06406  3.90876  3.01767  3.74194  2.94856  2.91976  2.09488  2.60568  3.04014  5.32541  4.04568     31 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03205  3.85226  4.57460  0.61958  0.77255  0.52495  0.89548
+     27   2.09937  4.25808  3.35967  2.86682  3.65405  3.28224  3.85971  2.93650  2.81278  2.15397  3.59474  3.22143  3.43624  3.14697  3.16235  2.43009  2.43628  2.66078  5.07593  3.83686     32 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     28   2.56380  4.32380  3.45039  2.92280  3.52453  3.48960  3.88793  2.33272  2.83649  2.29194  3.45598  3.31192  2.56007  3.18425  3.18050  2.78603  2.52825  2.43071  5.02605  3.78340     33 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     29   2.52137  4.41862  3.18708  2.64701  3.24265  3.41116  3.70401  2.99879  2.50849  2.54033  3.56040  3.09794  3.19241  2.94883  3.00442  2.32271  2.53579  2.73855  5.01724  3.74765     34 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     30   2.00013  4.58033  2.94855  2.53646  4.07584  3.24271  3.72285  3.46598  2.50253  3.10366  3.94143  2.28591  3.76727  2.90505  2.65085  2.58556  2.61842  3.10852  5.34830  4.05040     35 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     31   2.38233  4.27978  3.41241  2.86526  3.23400  3.48475  3.81694  2.67786  2.81404  2.37039  3.42629  2.84230  3.90072  3.13098  3.17039  2.75440  2.50322  2.16090  4.92926  3.68461     36 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     32   2.57741  4.71210  2.95136  2.48036  4.10354  2.51445  3.64563  3.50876  2.11219  3.10615  3.93363  2.96219  3.79067  2.80786  2.54902  2.50592  2.51289  3.16566  5.32824  4.02677     37 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     33   2.48649  4.79372  2.91636  2.20828  4.05411  3.38774  3.59403  3.46081  2.32653  3.05709  3.87988  2.78811  3.79502  2.48988  2.50461  2.63652  2.83744  2.71303  5.28703  3.96685     38 e - - -
+          2.68619  4.42226  2.77520  2.73124  3.46338  2.40514  3.72495  3.29355  2.67742  2.69356  4.24691  2.90348  2.73732  3.18147  2.89802  2.37888  2.77520  2.98519  4.58478  3.61504
+          0.07101  2.83445  4.62550  0.63888  0.75053  0.55549  0.85282
+     34   1.61247  4.12982  3.30077  3.01153  4.23531  2.30721  4.11812  3.64225  3.09602  3.33089  4.15414  3.20546  3.63382  3.37908  3.42786  1.71494  2.65404  3.10635  5.57312  4.35188     41 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     35   2.72153  4.23229  3.98871  3.42694  2.97156  3.37228  4.12428  2.11066  3.32720  1.56866  3.14804  3.72158  4.17041  3.58506  3.57523  3.10753  2.96236  2.01957  4.85619  3.63474     42 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     36   2.59948  4.33296  4.13320  3.58598  1.90470  3.92561  4.14661  2.31847  3.46961  1.74381  2.73954  3.84829  4.26638  3.69996  3.68809  3.23462  3.08833  2.27549  4.71854  3.39100     43 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     37   2.58931  4.58161  2.85848  2.66250  4.27569  1.35942  3.92193  3.72667  2.37699  3.36968  4.25453  3.08080  3.80217  3.15095  3.05968  2.68693  2.97358  3.32002  5.50315  4.25822     44 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     38   2.65443  4.41996  3.43781  2.92526  3.49579  3.52457  3.83449  2.83057  2.64507  1.65492  3.47551  3.30970  3.36731  3.12885  2.65176  2.85231  2.91346  2.65496  4.98654  3.73087     45 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     39   2.72350  4.79628  3.09209  2.60253  4.10688  3.43694  3.64699  3.48037  1.53491  2.80300  3.97997  3.05455  3.87955  2.81812  2.48623  2.53142  2.96661  3.17965  5.30004  4.01932     46 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     40   1.82674  4.13179  3.37396  3.04098  4.15468  2.93287  4.10316  3.48191  3.05943  3.22685  4.07364  3.23234  3.65478  3.36800  3.38156  1.54612  2.23418  3.00585  5.51189  4.29184     47 s - - -
+          2.68618  4.42225  2.77520  2.73124  3.46354  2.40513  3.72495  3.29354  2.67741  2.69355  4.24690  2.90347  2.73740  3.18147  2.89801  2.37887  2.77520  2.98508  4.58477  3.61503
+          0.07101  2.83445  4.62550  0.49418  0.94179  0.55549  0.85282
+     41   1.87334  4.14690  3.25058  2.97809  4.26468  2.12605  4.11046  3.68104  3.08263  3.36184  4.18261  3.18779  3.63442  3.36584  3.42015  1.58161  2.66271  3.13490  5.59481  4.36766     49 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.11955  3.90315  2.38048  0.61958  0.77255  0.55549  0.85282
+     42   2.21070  4.25592  3.22113  2.77420  3.85340  3.11965  3.84389  3.18555  2.74438  2.69874  3.77079  3.12117  3.16635  3.08826  3.10948  2.13243  2.17697  2.83916  5.22265  3.97074     50 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03323  3.81683  4.53917  0.61958  0.77255  0.62561  0.76558
+     43   2.34367  4.62113  3.10732  2.54725  3.88271  3.41056  3.61301  3.25335  2.12788  2.89080  3.32993  3.01581  3.81452  2.79680  2.35592  2.55841  2.82420  2.96607  5.15612  3.88260     51 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.07842  3.81683  2.92953  0.61958  0.77255  0.50642  0.92294
+     44   2.82371  4.84862  3.20218  2.72371  4.21308  3.44211  3.65714  3.71157  2.07944  3.25434  4.14899  2.84303  3.91564  2.83614  1.36807  2.87376  3.07618  3.38687  5.31229  4.06004     52 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.12129  3.86316  2.37274  0.61958  0.77255  0.58934  0.80899
+     45   2.50307  4.57358  2.77436  2.48515  4.13002  2.10145  3.72113  3.58708  2.48196  3.19739  4.04544  2.56817  3.72971  2.91621  2.54565  2.57756  2.84032  3.19983  5.38083  4.07634     53 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03462  3.77648  4.49883  0.61958  0.77255  0.55399  0.85485
+     46   2.68547  4.96255  2.09111  2.21584  4.36925  1.97687  3.70446  3.84410  2.62382  3.41713  4.25934  2.75900  3.76266  2.58919  3.12019  2.66938  2.98839  3.45462  5.60805  4.21173     54 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03205  3.85226  4.57460  0.61958  0.77255  0.52495  0.89548
+     47   2.70472  4.70735  3.22145  2.64799  3.96742  3.15410  3.63371  3.33902  2.12091  2.43520  3.82970  3.09240  3.88741  2.81708  1.91192  2.78290  2.92703  3.06076  5.19463  3.94224     55 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     48   2.39251  4.23853  3.75245  3.18839  3.35715  3.69117  4.01036  2.17738  3.07061  2.00722  3.28706  3.53429  4.07078  3.39072  3.07752  2.97399  2.89120  1.85844  4.92824  3.71532     56 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     49   2.56050  4.39105  3.34411  2.78319  3.60155  3.46480  3.77128  2.83822  2.62742  2.58594  3.21262  3.20237  3.88486  3.03059  2.56448  2.73642  2.19035  2.37832  5.02624  3.77891     57 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     50   1.37812  2.80072  3.83498  3.42070  3.87397  2.99921  4.24161  2.96530  3.32256  2.89459  3.81917  3.44880  3.71764  3.60422  3.56604  2.43360  2.40411  2.61888  5.33835  4.15246     58 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.55549  0.85282
+     51   2.74532  4.50724  3.42983  2.95244  3.42568  3.60626  3.88910  2.77026  2.70375  2.22807  1.95486  3.35569  4.02547  2.58121  2.99178  2.94186  2.99694  2.66631  5.01046  3.74777     59 m - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03044  3.90315  4.62550  0.61958  0.77255  0.44450  1.02483
+     52   0.87297  4.24657  3.57075  3.36786  4.15839  3.06811  4.37727  3.29971  3.38685  3.17640  4.19749  3.49040  3.81731  3.71861  3.62959  2.59634  2.88098  2.93600  5.57511  4.38724     60 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     53   1.96298  4.17134  3.43460  3.07855  4.14627  2.98936  4.12481  3.46945  3.08161  3.21642  4.06614  3.27556  3.70074  3.39215  3.40515  1.84467  1.63498  3.01250  5.50977  4.28998     61 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     54   3.40066  4.84720  3.99369  3.74344  2.30278  3.95279  3.67128  3.39553  3.59798  2.82228  4.05988  3.85394  4.42456  3.87882  3.75994  3.52665  3.68805  3.27113  3.95029  0.80817     62 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     55   2.86663  5.06853  2.87640  2.51952  4.45748  3.43879  3.66568  3.89866  1.33937  3.41664  4.27179  2.44223  3.91577  2.81805  2.46217  2.85914  3.10555  3.54515  5.50478  4.21799     63 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     56   2.89101  4.40561  4.26922  3.93641  3.62800  3.83108  4.72052  2.04974  3.80577  2.29743  3.57810  4.10920  4.38636  4.15841  4.00806  3.34183  3.25039  0.90272  5.42983  4.16757     64 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     57   2.78989  4.87427  3.10728  2.66228  4.33471  3.44160  3.70636  3.64306  1.30828  3.25641  4.14609  3.10047  3.92071  2.86764  2.46119  2.83588  2.59339  3.32217  5.44317  4.19498     65 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     58   3.28295  4.64373  4.65267  4.15432  2.12281  4.39226  4.40565  2.30868  4.01063  0.99404  2.96208  4.34589  4.63887  4.13687  4.14117  3.75853  3.51440  2.42033  4.68321  3.26182     66 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     59   3.08248  4.42168  4.72065  4.23378  3.50231  4.37831  4.95293  1.13074  4.10572  1.99571  3.31982  4.46032  4.70821  4.37869  4.30619  3.77127  3.35421  1.44848  5.47660  4.27063     67 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     60   2.47137  4.31634  3.52340  3.28135  4.06362  3.15188  4.29038  3.20358  3.22639  3.06334  4.10788  3.47324  3.86421  3.61618  3.47946  2.67390  1.00625  2.88730  5.49222  4.28053     68 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     61   2.89484  4.63395  3.51346  3.36817  4.35306  3.31558  4.43642  3.92611  3.43456  3.54133  4.59617  3.66397  0.65427  3.82016  3.66760  3.06578  3.34480  3.55944  5.51022  4.47541     69 P - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     62   2.83547  5.32344  1.90710  1.42799  4.63051  3.24198  3.71244  4.09996  2.65068  3.63347  4.46321  2.71732  3.81726  2.86723  3.20067  2.75440  2.81778  3.69487  5.81218  4.35094     70 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     63   2.79923  4.56350  3.49435  3.40744  4.54632  0.58454  4.52062  4.17205  3.60289  3.81287  4.79002  3.65874  3.95796  3.91884  3.82676  2.97427  3.28861  3.68020  5.61312  4.65200     71 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     64   2.69344  5.03834  2.59685  1.78499  4.35492  3.31996  3.63904  3.79867  2.33359  3.34661  4.15382  2.64678  2.71516  2.78054  2.89199  2.67010  2.77928  3.42501  5.53498  4.15652     72 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     65   2.70271  4.40317  3.56325  3.00957  3.45446  3.68235  3.94694  2.18180  2.88182  2.21283  3.37093  3.41883  4.05792  2.33611  3.21378  2.95204  2.94041  2.01647  5.02429  3.78732     73 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     66   3.04481  5.17464  2.53544  0.88540  4.53437  3.34332  3.94029  4.00914  2.83416  3.60986  4.58016  2.99593  3.95313  3.15433  3.25317  3.01711  3.35013  3.68253  5.68627  4.40959     74 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     67   3.05681  4.44153  4.54138  3.99838  1.97987  4.22221  4.40129  2.17878  3.87522  1.45215  3.02589  4.20160  4.50632  4.03401  4.02978  3.55251  3.29207  1.80812  4.80289  3.46847     75 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     68   2.82984  5.35567  1.99168  1.57663  4.64578  3.25717  3.68509  4.14274  2.58330  3.63869  4.45307  2.71724  3.81329  2.22362  3.11653  2.74299  3.09347  3.72626  5.79564  4.33600     76 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     69   2.50977  1.62448  4.23724  3.80750  3.60189  3.40590  4.45756  2.35254  3.63764  2.45765  3.57920  3.82230  4.04603  3.92585  3.81907  2.84633  2.91668  1.82275  5.24689  4.01880     77 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     70   2.77571  5.11398  2.10801  2.05696  4.45701  3.25620  3.72899  3.91339  2.64389  3.48166  4.32027  2.78446  1.86353  2.89484  3.15510  2.73789  3.05806  3.53410  5.67671  4.27030     78 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     71   2.91274  5.42662  1.24765  1.93231  4.73792  3.20738  3.76458  4.24169  2.78531  3.76893  4.62568  2.68636  3.83308  2.93581  3.37349  2.60161  3.20907  3.82383  5.94007  4.44265     79 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     72   2.93000  5.34144  1.08347  2.15883  4.66482  3.19912  3.82811  4.25255  2.88821  3.80850  4.69751  2.51974  3.85217  3.02158  3.46889  2.84580  3.25390  3.83179  5.90365  4.41772     80 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     73   2.89101  4.40561  4.26922  3.93641  3.62800  3.83108  4.72052  2.04974  3.80577  2.29743  3.57810  4.10920  4.38636  4.15841  4.00806  3.34183  3.25039  0.90272  5.42983  4.16757     81 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     74   3.40066  4.84720  3.99369  3.74344  2.30278  3.95279  3.67128  3.39553  3.59798  2.82228  4.05988  3.85394  4.42456  3.87882  3.75994  3.52665  3.68805  3.27113  3.95029  0.80817     82 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     75   3.08173  4.41570  4.73343  4.24332  3.51344  4.38931  4.96241  1.16063  4.11994  2.00857  3.32756  4.46929  4.71466  4.39012  4.32070  3.78014  3.35186  1.39369  5.48545  4.28088     83 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     76   3.27378  4.70553  4.35708  3.99825  3.17780  4.11433  4.62122  2.38848  3.76496  0.75643  3.16963  4.26355  4.53226  4.10824  3.92358  3.69863  3.56359  2.45281  5.09417  3.84135     84 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     77   3.10426  5.24570  0.77891  2.40498  4.63731  3.29196  4.02650  4.24977  3.13093  3.83985  4.82107  2.96628  3.95224  3.26642  3.66070  3.05541  3.44536  3.87811  5.78539  4.49402     85 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     78   2.10165  4.63153  3.04360  2.53313  3.37849  3.42468  3.14974  3.24484  2.48640  2.87927  3.73924  3.02051  3.83549  2.34761  2.89722  2.67591  2.83487  2.96227  5.11406  3.77860     86 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     79   0.87297  4.24657  3.57075  3.36786  4.15839  3.06811  4.37727  3.29971  3.38685  3.17640  4.19749  3.49040  3.81731  3.71861  3.62959  2.59634  2.88098  2.93600  5.57511  4.38724     87 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     80   3.04481  5.17464  2.53544  0.88540  4.53437  3.34332  3.94029  4.00914  2.83416  3.60986  4.58016  2.99593  3.95313  3.15433  3.25317  3.01711  3.35013  3.68253  5.68627  4.40959     88 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     81   3.04481  5.17464  2.53544  0.88540  4.53437  3.34332  3.94029  4.00914  2.83416  3.60986  4.58016  2.99593  3.95313  3.15433  3.25317  3.01711  3.35013  3.68253  5.68627  4.40959     89 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     82   2.02804  4.79412  2.56696  2.04470  4.09359  3.33689  3.67081  3.44631  2.51465  3.10781  3.94410  2.90500  3.80252  2.83547  2.98166  2.64658  2.87169  2.76307  5.37415  4.03168     90 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     83   2.79923  4.56350  3.49435  3.40744  4.54632  0.58454  4.52062  4.17205  3.60289  3.81287  4.79002  3.65874  3.95796  3.91884  3.82676  2.97427  3.28861  3.68020  5.61312  4.65200     91 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     84   2.73428  4.33692  3.73213  3.19478  3.16177  3.72868  2.92126  1.73180  3.00305  2.10225  3.32627  3.53324  4.10443  3.37488  3.27326  3.02489  2.97120  2.37844  4.75364  3.40581     92 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     85   2.98626  5.46880  1.08491  1.97249  4.77077  3.21550  3.81546  4.28421  2.87189  3.82944  4.72170  2.71026  3.86087  3.00007  3.46540  2.87647  3.29141  3.87860  5.96770  4.48798     93 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     86   3.27378  4.70553  4.35708  3.99825  3.17780  4.11433  4.62122  2.38848  3.76496  0.75643  3.16963  4.26355  4.53226  4.10824  3.92358  3.69863  3.56359  2.45281  5.09417  3.84135     94 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     87   2.89484  4.63395  3.51346  3.36817  4.35306  3.31558  4.43642  3.92611  3.43456  3.54133  4.59617  3.66397  0.65427  3.82016  3.66760  3.06578  3.34480  3.55944  5.51022  4.47541     95 P - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     88   3.48726  4.82741  4.35628  4.01269  1.82549  4.23514  3.48811  3.25093  3.87322  2.62544  3.87831  3.93991  4.57991  3.96204  3.99151  3.61127  3.72377  3.16284  3.67256  0.95290     96 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     89   2.36871  4.29005  3.29968  3.12112  4.18153  3.01810  4.22768  3.71868  3.20194  3.43114  4.34903  3.32859  3.76571  3.54324  3.48419  0.94365  2.85682  3.23083  5.54583  4.26583     97 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     90   2.70964  0.75093  4.25205  4.01610  3.98428  3.30982  4.64026  3.14621  3.84384  3.05665  4.18165  3.97525  4.02496  4.18572  3.94943  2.98138  3.18527  2.88356  5.37752  4.27103     98 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     91   3.12574  4.95602  3.62729  3.11760  4.33719  3.59265  3.90370  3.88680  2.28467  3.38377  4.38192  3.46899  4.09078  3.11945  0.82993  3.20529  3.38498  3.60641  5.37824  4.24257     99 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     92   0.87297  4.24657  3.57075  3.36786  4.15839  3.06811  4.37727  3.29971  3.38685  3.17640  4.19749  3.49040  3.81731  3.71861  3.62959  2.59634  2.88098  2.93600  5.57511  4.38724    100 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     93   2.79923  4.56350  3.49435  3.40744  4.54632  0.58454  4.52062  4.17205  3.60289  3.81287  4.79002  3.65874  3.95796  3.91884  3.82676  2.97427  3.28861  3.68020  5.61312  4.65200    101 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     94   2.36871  4.29005  3.29968  3.12112  4.18153  3.01810  4.22768  3.71868  3.20194  3.43114  4.34903  3.32859  3.76571  3.54324  3.48419  0.94365  2.85682  3.23083  5.54583  4.26583    102 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     95   2.70964  0.75093  4.25205  4.01610  3.98428  3.30982  4.64026  3.14621  3.84384  3.05665  4.18165  3.97525  4.02496  4.18572  3.94943  2.98138  3.18527  2.88356  5.37752  4.27103    103 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     96   2.36871  4.29005  3.29968  3.12112  4.18153  3.01810  4.22768  3.71868  3.20194  3.43114  4.34903  3.32859  3.76571  3.54324  3.48419  0.94365  2.85682  3.23083  5.54583  4.26583    104 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     97   2.36871  4.29005  3.29968  3.12112  4.18153  3.01810  4.22768  3.71868  3.20194  3.43114  4.34903  3.32859  3.76571  3.54324  3.48419  0.94365  2.85682  3.23083  5.54583  4.26583    105 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     98   2.70964  0.75093  4.25205  4.01610  3.98428  3.30982  4.64026  3.14621  3.84384  3.05665  4.18165  3.97525  4.02496  4.18572  3.94943  2.98138  3.18527  2.88356  5.37752  4.27103    106 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+     99   0.87297  4.24657  3.57075  3.36786  4.15839  3.06811  4.37727  3.29971  3.38685  3.17640  4.19749  3.49040  3.81731  3.71861  3.62959  2.59634  2.88098  2.93600  5.57511  4.38724    107 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    100   2.79923  4.56350  3.49435  3.40744  4.54632  0.58454  4.52062  4.17205  3.60289  3.81287  4.79002  3.65874  3.95796  3.91884  3.82676  2.97427  3.28861  3.68020  5.61312  4.65200    108 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    101   3.06946  5.03551  3.25229  2.85531  4.44585  3.54670  3.82012  3.88188  0.90373  3.42461  4.38128  3.27486  4.04036  3.00629  2.45564  3.10509  3.31538  3.58407  5.45821  4.28639    109 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    102   3.06247  4.41039  4.65086  4.13598  3.48324  4.35915  4.85935  1.65561  4.00771  1.89605  3.31161  4.38665  4.67296  4.28428  4.22305  3.72759  3.32396  1.08186  5.42132  4.21018    110 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    103   2.33323  4.28075  3.53672  3.04537  3.62455  3.45748  3.98551  2.58410  2.72399  2.59228  3.55231  3.38781  3.95328  3.30102  3.25844  2.79290  2.46624  1.66085  5.10597  3.87716    111 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    104   2.09904  4.31904  3.13967  2.79512  4.23584  2.43802  3.97520  3.66080  2.88605  3.30435  4.12032  2.79615  3.70254  3.18983  3.27753  1.61859  2.49732  3.17574  5.54095  4.27688    112 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    105   2.79923  4.56350  3.49435  3.40744  4.54632  0.58454  4.52062  4.17205  3.60289  3.81287  4.79002  3.65874  3.95796  3.91884  3.82676  2.97427  3.28861  3.68020  5.61312  4.65200    113 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    106   2.34210  4.57052  2.98887  2.43186  3.63497  3.31233  3.71920  3.32573  2.57569  2.97882  3.82112  2.81207  3.79423  2.91349  3.00476  1.86229  2.69145  3.00524  5.25570  3.95436    114 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    107   3.05964  4.41092  4.64145  4.12759  3.48238  4.35054  4.85240  1.66963  3.99735  1.89738  3.31309  4.37857  4.66775  4.27653  4.21363  3.71953  3.32208  1.07763  5.41853  4.20516    115 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    108   2.91005  5.38045  1.25818  2.12941  4.69830  3.20228  3.78776  4.26304  2.81947  3.79137  4.65619  2.14323  3.83796  2.96707  3.40279  2.81725  3.21882  3.83565  5.91563  4.42054    116 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    109   2.96665  4.95377  2.97494  2.73211  4.13599  3.43683  3.89106  3.78353  2.54586  3.27542  4.30113  3.19246  3.99230  1.05904  2.82765  3.01035  3.26391  3.50694  5.38986  4.09082    117 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    110   2.64618  4.90499  2.31134  2.18942  4.30332  3.26131  3.70172  3.73572  2.57300  3.32434  4.14511  2.84177  3.78986  2.86174  3.06104  1.76618  2.63178  3.35922  5.54260  4.16860    118 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    111   3.10426  5.24570  0.77891  2.40498  4.63731  3.29196  4.02650  4.24977  3.13093  3.83985  4.82107  2.96628  3.95224  3.26642  3.66070  3.05541  3.44536  3.87811  5.78539  4.49402    119 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    112   2.73269  4.96873  2.61141  2.26957  4.35491  2.17303  3.70728  3.80016  2.46897  3.36392  4.20366  2.88233  3.83451  1.94009  2.87446  2.73151  3.00866  3.43365  5.54626  4.19876    120 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    113   2.56630  4.67539  2.76483  2.50324  4.25470  3.20379  3.80042  3.71196  2.47872  3.32135  4.15966  2.47516  3.79205  2.98884  3.00841  1.53662  2.90903  3.30833  5.51362  4.18451    121 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    114   3.45422  4.77029  4.46140  4.11213  1.01250  4.26302  3.55945  3.07075  3.96969  2.40922  3.69615  4.01237  4.59409  4.02617  4.05737  3.63979  3.68916  3.02613  3.73517  1.83750    122 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    115   3.27378  4.70553  4.35708  3.99825  3.17780  4.11433  4.62122  2.38848  3.76496  0.75643  3.16963  4.26355  4.53226  4.10824  3.92358  3.69863  3.56359  2.45281  5.09417  3.84135    123 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    116   2.92493  5.35887  1.12201  2.14550  4.68054  3.19834  3.81470  4.26138  2.86788  3.80711  4.68829  2.43062  3.84716  3.00361  3.45124  2.83676  3.24430  3.83759  5.91360  4.42201    124 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02824  3.97705  4.69940  0.61958  0.77255  0.48576  0.95510
+    117   2.99103  5.43832  1.04731  2.03422  4.75036  3.21916  3.83186  4.26509  2.89245  3.82288  4.72422  2.72815  3.86743  3.02108  3.48036  2.88767  3.30065  3.86474  5.95049  4.48470    125 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.05161  3.97705  3.45590  0.61958  0.77255  0.48576  0.95510
+    118   2.86997  5.43069  1.57180  1.74614  4.72729  2.80306  3.71453  4.23943  2.69618  3.73332  4.56237  2.43722  3.80910  2.87126  3.27991  2.76596  3.14983  3.81040  5.89719  4.40314    126 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    119   2.94701  4.93449  2.95833  2.71460  4.11381  3.42085  3.87354  3.75736  2.52981  3.25157  4.27740  3.17495  3.97565  1.09531  2.81227  2.99143  3.24405  3.48203  5.37148  4.07074    127 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    120   2.77849  4.28259  3.99549  3.02221  3.34513  3.89873  4.21017  1.60238  3.32583  1.96055  3.04166  3.76188  4.24121  3.61775  3.59603  3.18793  3.01465  1.89778  5.01613  3.81900    128 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    121   2.05663  4.99383  2.42035  1.95337  4.30142  3.11016  3.64379  3.74448  2.38547  3.30891  4.11909  2.80952  3.78509  2.79112  2.97996  2.54099  2.92358  3.37653  5.51637  4.13250    129 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    122   2.37074  5.20065  2.03752  1.62561  4.52764  2.94012  3.68254  3.99698  2.59389  3.53203  4.34713  2.73405  3.79399  2.83254  3.13133  2.70217  3.03318  3.59496  5.71621  4.27884    130 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    123   2.76891  4.53595  3.45942  3.36936  4.50963  0.61326  4.48439  4.12940  3.56175  3.77320  4.74981  3.62373  3.92970  3.87945  3.78857  2.94321  3.25651  3.64192  5.58291  4.61439    131 G - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    124   3.45280  4.72560  4.55948  4.18538  1.45983  4.27876  3.45556  3.19175  4.01502  2.56322  3.77632  3.99268  4.58568  4.02082  4.06587  3.62371  3.67122  3.09770  1.76510  1.80304    132 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    125   2.87283  4.39315  4.23821  3.90420  3.61058  3.80809  4.69198  2.04849  3.77322  2.28363  3.56364  4.08078  4.36427  4.12727  3.97777  3.31702  3.23254  0.92773  5.40873  4.14528    133 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    126   3.24760  4.68579  4.32075  3.96132  3.16793  4.08580  4.59138  2.37683  3.72825  0.78064  3.16594  4.22883  4.50730  4.07699  3.89079  3.66669  3.53825  2.43535  5.07658  3.81868    134 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    127   2.45786  4.30296  3.50186  3.25812  4.04222  3.13941  4.26905  3.17937  3.20271  3.03994  4.08552  3.45408  3.84975  3.59352  3.45722  2.66008  1.04037  2.86556  5.47283  4.25897    135 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    128   2.68154  0.78852  4.21932  3.97783  3.95068  3.28696  4.60693  3.10838  3.80528  3.02007  4.14415  3.94300  4.00035  4.14805  3.91507  2.95331  3.15521  2.84739  5.34926  4.23772    136 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    129   2.02617  4.29048  3.42265  2.94627  3.54036  3.40652  3.31257  2.77872  2.86355  2.59741  3.53948  3.30371  3.90044  3.21666  3.18816  2.73904  2.82169  1.86270  5.00831  3.74432    137 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    130   0.90195  4.23400  3.54659  3.34205  4.13472  3.05626  4.35430  3.27256  3.36043  3.15035  4.17350  3.47069  3.80326  3.69417  3.60509  2.58374  2.86679  2.91280  5.55371  4.36315    138 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    131   3.44381  4.80306  4.30117  3.95207  1.85364  4.19548  3.48537  3.22033  3.81418  2.60484  3.85318  3.90681  4.54641  3.92239  3.94488  3.57229  3.68268  3.12961  3.68006  0.97942    139 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    132   2.86235  4.60578  3.47842  3.32948  4.31715  3.28867  4.39999  3.88386  3.39382  3.50174  4.55528  3.62780  0.68858  3.78012  3.62986  3.03247  3.31042  3.51987  5.48103  4.43903    140 P - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    133   2.60256  4.60991  3.08123  2.59418  3.90705  3.39886  3.69738  3.25085  2.27951  2.91739  3.78849  3.05676  3.84470  2.63540  2.79735  2.69573  1.94572  2.76699  5.21418  3.93668    141 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    134   2.16968  3.47307  3.59233  3.22260  4.15866  2.47411  4.19438  3.56363  3.18625  3.27458  4.10858  3.32406  3.66022  3.48496  3.48055  1.23700  2.65371  3.05043  5.52336  4.31592    142 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    135   3.07859  5.21813  0.80740  2.38838  4.60638  3.27362  4.00471  4.21341  3.10456  3.80732  4.78755  2.94873  3.93224  3.24424  3.63163  3.03196  3.41903  3.84400  5.75900  4.46621    143 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    136   2.87283  4.39315  4.23821  3.90420  3.61058  3.80809  4.69198  2.04849  3.77322  2.28363  3.56364  4.08078  4.36427  4.12727  3.97777  3.31702  3.23254  0.92773  5.40873  4.14528    144 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    137   2.50102  4.23694  3.76525  3.33965  3.72246  3.37067  4.24809  2.46042  3.23911  2.55924  3.64161  3.56053  3.97799  3.58142  3.52035  2.77764  1.69802  1.75863  5.30971  4.08018    145 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    138   3.09498  4.49149  4.45102  4.07022  3.43002  4.15408  4.78347  0.98199  3.89992  1.99977  3.35310  4.30411  4.57779  4.24002  4.08717  3.65761  3.39284  1.85079  5.33749  4.08258    146 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    139   3.02382  5.15051  2.52384  0.91441  4.50717  3.32764  3.92196  3.97736  2.81286  3.58149  4.55225  2.98104  3.93620  3.13613  3.22992  2.99812  3.32879  3.65289  5.66334  4.38601    147 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    140   2.45786  4.30296  3.50186  3.25812  4.04222  3.13941  4.26905  3.17937  3.20271  3.03994  4.08552  3.45408  3.84975  3.59352  3.45722  2.66008  1.04037  2.86556  5.47283  4.25897    148 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    141   3.08695  4.88934  3.20791  2.97643  3.34377  3.51816  0.98092  3.80251  2.81733  3.28606  4.33566  3.38232  4.07539  3.34383  3.07797  3.15699  3.39324  3.53609  4.80769  3.29239    149 h - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    142   2.99214  5.06805  3.26268  2.74531  4.47846  3.57206  3.65342  3.86981  1.12103  3.36156  4.25702  3.16311  3.99796  2.80635  2.11223  3.00404  3.19554  3.55761  5.42835  4.23089    150 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    143   3.02382  5.15051  2.52384  0.91441  4.50717  3.32764  3.92196  3.97736  2.81286  3.58149  4.55225  2.98104  3.93620  3.13613  3.22992  2.99812  3.32879  3.65289  5.66334  4.38601    151 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    144   2.49167  5.21869  2.13891  1.38935  4.54510  3.23855  3.71342  3.98470  2.64038  3.55288  4.39217  2.73628  3.81159  2.87379  3.17028  2.74234  3.08340  3.59770  5.74876  4.31122    152 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    145   2.67924  5.36825  1.81597  1.42545  4.67130  3.22067  3.71949  4.14895  2.68617  3.67868  4.51726  2.69278  3.81366  2.87890  3.25116  2.76699  3.13943  3.73938  5.85528  4.37934    153 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    146   3.24708  4.60677  4.69416  4.13988  3.08947  4.47063  4.76500  1.92492  3.96672  0.95356  2.71915  4.43331  4.67883  4.15873  4.13317  3.81764  3.47493  2.15974  5.15578  4.04247    154 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02889  3.95434  4.67668  0.61958  0.77255  0.50828  0.92013
+    147   2.69781  4.29649  3.84369  3.29786  3.44437  3.73086  4.15124  2.24231  3.19336  2.10104  3.11890  3.63278  4.14347  3.51043  3.49251  3.03876  2.03846  1.78833  5.07714  3.86271    155 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.18495  3.95434  1.89921  0.61958  0.77255  0.50828  0.92013
+    148   1.44115  4.10304  3.22225  2.99375  4.15355  2.08176  4.10711  3.47540  3.09081  3.24183  4.11431  3.18864  3.61518  3.39097  3.39593  2.35434  2.65737  2.99565  5.50492  4.29375    156 a - - -
+          2.68608  4.42226  2.77520  2.73124  3.46354  2.40513  3.72495  3.29355  2.67741  2.69355  4.24690  2.90347  2.73740  3.18147  2.89801  2.37887  2.77520  2.98519  4.58477  3.61504
+          0.08120  2.55114        *  0.46726  0.98542  0.00000        *
+//
index acb65e9..1cbfd21 100644 (file)
@@ -74,7 +74,6 @@ releases_dir = help/markdown/releases
 getdown_website_dir = build/website/docroot/getdown
 getdown_archive_dir = build/website/docroot/old
 getdown_files_dir = build/getdown/files
-
 getdown_local = false
 getdown_resource_dir = resource
 getdown_lib_dir = getdown/lib
index 1710bbc..b06472d 100755 (executable)
    <mapID target="alwCalc" url="html/menus/alwcalculate.html"/>
    
    <mapID target="wsMenu" url="html/menus/wsmenu.html"/>
+   <mapID target="alwHmmer" url="html/menus/alwhmmer.html"/>
    <mapID target="popMenu" url="html/menus/popupMenu.html"/>
    <mapID target="popMenuAddref" url="html/menus/popupMenu.html#addrefannot"/>
    <mapID target="annotPanelMenu" url="html/menus/alwannotationpanel.html"/>
index 3308456..00aa8b0 100755 (executable)
                                <tocitem text="Colour Menu" target="alwColour" />
                                <tocitem text="Calculate Menu" target="alwCalc" />
                                <tocitem text="Web Service Menu" target="wsMenu" />
+                               <tocitem text="HMMER Menu" target="alwHmmer" />
                                <tocitem text="Annotation Panel Menu" target="annotPanelMenu" />
                                <tocitem text="Popup Menu" target="popMenu" />
                        </tocitem>
index 6708c57..509fa35 100755 (executable)
@@ -68,6 +68,9 @@
         Preferences</a> tab allows you to adjust how much memory is
       allocated to Jalview when it is launched.
     </li>
+    <li>The <a href="#hmmer"><strong>&quot;HMMER&quot;</strong>
+        Preferences</a> tab allows you to configure locally installed HMMER tools.
+    </li>
     <li>The <a href="../webServices/webServicesPrefs.html"><strong>&quot;Web
           Service&quot;</strong> Preferences</a> tab allows you to configure the <a
       href="http://www.compbio.dundee.ac.uk/jabaws">JABAWS</a>
     stored in your .jalview_properties file.
   </p>
   <p>&nbsp;</p>
+  <p>
+    <a name="hmmer"><strong>&quot;HMMER&quot; Preferences tab</strong></a>
+  </p>
+  <p>If you have installed HMMER tools (available from <a href="http://hmmerorg">hmmer.org</a>),
+  then you should specify on this screen the location of the installation (the path to the folder 
+  containing binary executable programs). Double-click in the input field to open a file browser.</p>
+  <p>When this path is configured, the <a href="../menus/alwhmmer.html">HMMER menu</a> will be
+  enabled in the Alignment window.</p>
+  <p>&nbsp;</p>
   <em>Web Services Preferences</em> - documentation for this tab is
   given in the
   <a href="../webServices/webServicesPrefs.html">Web Services
diff --git a/help/help/html/menus/alwhmmer.html b/help/help/html/menus/alwhmmer.html
new file mode 100644 (file)
index 0000000..ce23c4b
--- /dev/null
@@ -0,0 +1,87 @@
+<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>
+</head>
+<body>
+<p>
+  <strong>HMMER Menu</strong>
+</p>
+<p>This menu is available if hmmbuild tools have been installed and configured in the 
+<a href="../features/preferences.html#hmmer">HMMER Preferences</a> panel.
+  <br><br>
+  
+  <strong>hmmbuild</strong>
+  <br><br>Run hmmbuild to create a Hidden Markov Model profile of your sequence alignment or sub-groups.
+  <br><br>The consensus sequence for the computed profile is inserted as the first sequence of
+  the alignment (or group) for which hmmbuild is run.
+  <br>Jalview also computes and displays an annotation below the alignment, showing
+  the information content of each column.
+  <br>This is calculated as the sum over residue symbols of
+  <pre>
+    match emission probability * log(match emission probability / background frequency) / log(2)
+  </pre>
+  where the background frequencies are taken from (tbc: where? not https://www.ebi.ac.uk/uniprot/TrEMBLstats)
+  <ul>
+  <li>Edit settings and run...
+  <br><br>Choose whether to run hmmbuild for the whole alignment, or all or selected groups, or both 
+  (default is the alignment).
+  <br>Optionally enter a name to give the HMM profile consensus sequence
+  (default is "Alignment" or group name, with "_HMM" appended).
+  <br>Use Reference Annotation: select this option if your alignment has an RF reference annotation,
+  and you wish to constrain the HMM profile to it (hmmbuild option '--hand').
+  <br><br></li>
+  <li>hmmbuild with default settings
+  <br><br>Runs hmmbuild for the whole alignment.</li>
+  </ul>
+  
+  <strong>hmmalign</strong> and <strong>hmmsearch</strong> require an HMM consensus sequence to be selected first.
+  <br><em>To do this, right-click on the name of an HMM sequence added by hmmbuild, and 'Select HMM'.
+  The alignment will be with respect to the HMM profile for the consensus sequence.</em>
+  <br><br>
+  <strong>hmmalign</strong>
+  <br><br>Run hmmalign to align selected sequences (or the whole alignment) to a selected HMM profile.
+  <ul>
+  <li>Edit settings and run...
+  <br><br>Choose whether to 'trim non-matching termini' - hmmalign option '--trim'.
+  <br><br></li>
+  <li>hmmalign with default settings</li>
+  </ul>
+  <strong>hmmsearch</strong>
+  <br><br>Run hmmsearch to use an HMM profile as input to search a sequence database.
+  <ul>
+  <li>Edit settings and run...
+  <ul>
+  <li>tbc: choose database</li>
+  <li>tbc: automatically align</li>
+  <li>tbc: return accessions</li>
+  <li>tbc: number of results</li>
+  <li>tbc: sequence eValue cutoff</li>
+  <li>tbc: domains eValue cutoff</li>
+  </ul>
+  </li>
+  <br><li>hmmsearch with default settings</li>
+  <br><li>Add database
+  <br>Browse to select a local sequence data file to be searched</li>
+  </ul>
+<br>
+</body>
+</html>
diff --git a/j11lib/Jmol-15.1.3.jar b/j11lib/Jmol-15.1.3.jar
new file mode 100644 (file)
index 0000000..1672e67
Binary files /dev/null and b/j11lib/Jmol-15.1.3.jar differ
diff --git a/j11lib/apache-mime4j-0.6.jar b/j11lib/apache-mime4j-0.6.jar
deleted file mode 100644 (file)
index 1d2282c..0000000
Binary files a/j11lib/apache-mime4j-0.6.jar and /dev/null differ
diff --git a/j11lib/apache-mime4j-core-0.8.3.jar b/j11lib/apache-mime4j-core-0.8.3.jar
new file mode 100644 (file)
index 0000000..448cf92
Binary files /dev/null and b/j11lib/apache-mime4j-core-0.8.3.jar differ
diff --git a/j11lib/apache-mime4j-dom-0.8.3.jar b/j11lib/apache-mime4j-dom-0.8.3.jar
new file mode 100644 (file)
index 0000000..938c215
Binary files /dev/null and b/j11lib/apache-mime4j-dom-0.8.3.jar differ
diff --git a/j11lib/httpclient-4.0.3.jar b/j11lib/httpclient-4.0.3.jar
deleted file mode 100644 (file)
index fd0d377..0000000
Binary files a/j11lib/httpclient-4.0.3.jar and /dev/null differ
diff --git a/j11lib/httpclient-4.5.6.jar b/j11lib/httpclient-4.5.6.jar
new file mode 100644 (file)
index 0000000..56231de
Binary files /dev/null and b/j11lib/httpclient-4.5.6.jar differ
diff --git a/j11lib/httpcore-4.0.1.jar b/j11lib/httpcore-4.0.1.jar
deleted file mode 100644 (file)
index 4aef35e..0000000
Binary files a/j11lib/httpcore-4.0.1.jar and /dev/null differ
diff --git a/j11lib/httpcore-4.4.10.jar b/j11lib/httpcore-4.4.10.jar
new file mode 100644 (file)
index 0000000..dc510f8
Binary files /dev/null and b/j11lib/httpcore-4.4.10.jar differ
diff --git a/j11lib/httpmime-4.0.3.jar b/j11lib/httpmime-4.0.3.jar
deleted file mode 100644 (file)
index 0dfd331..0000000
Binary files a/j11lib/httpmime-4.0.3.jar and /dev/null differ
diff --git a/j11lib/httpmime-4.5.6.jar b/j11lib/httpmime-4.5.6.jar
new file mode 100644 (file)
index 0000000..df5a7d1
Binary files /dev/null and b/j11lib/httpmime-4.5.6.jar differ
diff --git a/j11lib/java-json.jar b/j11lib/java-json.jar
deleted file mode 100755 (executable)
index 2f211e3..0000000
Binary files a/j11lib/java-json.jar and /dev/null differ
diff --git a/j11lib/json-20180130.jar b/j11lib/json-20180130.jar
new file mode 100644 (file)
index 0000000..bc2cd41
Binary files /dev/null and b/j11lib/json-20180130.jar differ
diff --git a/j11lib/slivka-client.jar b/j11lib/slivka-client.jar
new file mode 100644 (file)
index 0000000..49ab4fc
Binary files /dev/null and b/j11lib/slivka-client.jar differ
diff --git a/j8lib/Jmol-15.1.3.jar b/j8lib/Jmol-15.1.3.jar
new file mode 100644 (file)
index 0000000..1672e67
Binary files /dev/null and b/j8lib/Jmol-15.1.3.jar differ
diff --git a/j8lib/apache-mime4j-core-0.8.3.jar b/j8lib/apache-mime4j-core-0.8.3.jar
new file mode 100644 (file)
index 0000000..448cf92
Binary files /dev/null and b/j8lib/apache-mime4j-core-0.8.3.jar differ
diff --git a/j8lib/apache-mime4j-dom-0.8.3.jar b/j8lib/apache-mime4j-dom-0.8.3.jar
new file mode 100644 (file)
index 0000000..938c215
Binary files /dev/null and b/j8lib/apache-mime4j-dom-0.8.3.jar differ
diff --git a/resources/ProbabilityOfMatch b/resources/ProbabilityOfMatch
new file mode 100644 (file)
index 0000000..9e3869f
--- /dev/null
@@ -0,0 +1,261 @@
+0.2
+A
+200, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.20369859505605942, 0.4222571919176904, 0.02232980369562049, 0.026676627662949624, 0.03525533903309387, 0.04787669271298644, 0.05605478196465866, 0.0641749787418314, 0.10286871806399077, 0.12105923035393054, 0.12422338883170829, 0.10304081214854802, 0.10333588151825282, 0.10114320874739426, 0.11812749552768892, 0.15006056120099137, 0.2237625777282616, 0.30253227447901293, 0.388557592836257, 0.43721427997253054, 0.47970928320880174, 0.5289808943443863, 0.5486523348637581, 0.5583247254358412, 0.5484761487937164, 0.5995060657240099, 0.6195991719342547, 0.6227297938639322, 0.6284738072714547, 0.5548992929945823, 0.5416658828311707, 0.5848669247143425, 0.5528051163252368, 0.6785332996776757, 0.5806004778403442, 0.18593167643573633, 0.38318927736500424
+400, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.00538163724710359, 0.050246851326647975, 0.00325623383324987, 0.016089404836314707, 0.004768961434464644, 0.012975554662909325, 0.014612779422905292, 0.036453046922170884, 0.03669851726847946, 0.06053063401623625, 0.07734802668205788, 0.14448615467437756, 0.24953322777140308, 0.34806276783515594, 0.4229567261622065, 0.4873077681738967, 0.5401722415436365, 0.5521510226102508, 0.6000517399892216, 0.6107765515229574, 0.5870927019689605, 0.6298630970573851, 0.6879023365675971, 0.6221611553796009, 0.632263810408655, 0.689461445849211, 0.6390236708841557, 0.7242036199282817, 0.6557168681820945, 0.21069080034931634
+600, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.009258546378795182, 0.022829316518316413, 0.022698074547786367, 0.012736836086937675, 0.016469563173777596, 0.01913720324613999, 0.03173961484046805, 0.04293589176349238, 0.08585848446208531, 0.16926121837954017, 0.293436232527367, 0.3543603601267507, 0.48651755499862337, 0.5103148855367983, 0.5876985421521675, 0.5784255879120095, 0.5938007169568057, 0.5598080715719793, 0.691548146841681, 0.6943388099375437, 0.6996541955589088, 0.7434201980299975, 0.8365119642131629, 0.9095567810750043, 0.5207569176305396, 0.7405911308670332
+800, -2.4, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4
+, 0.008917934185414377, 0.01017895688795886, 0.037528784049424314, 0.02798827469166613, 0.03137703539242772, 0.11784556048768703, 0.11121306048524869, 0.20430912771074103, 0.2598593792958281, 0.4306348256035734, 0.46316119541365697, 0.5405314728294079, 0.539282096838667, 0.42055185929928385, 0.5448818087581953, 0.5997338164946914, 0.62308312092143, 0.7089840268242462, 0.6436353618329662, 0.826552573493094, 0.7908826508888537, 0.6905508284818629, 0.3823632872193804
+1000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2
+, 0.006152180116633862, 0.015427371964555698, 0.02331992788794896, 0.10944473717642993, 0.19333261931256743, 0.2004513811627833, 0.2384363292885486, 0.5272892625055869, 0.5168199037063749, 0.5652991338441917, 0.4397653398451027, 0.46013409044401904, 0.5175981692561336, 0.6704643484850927, 0.7969708452261104, 0.4745152293484279, 0.9101407067992686, 0.9288992619408138, 0.8325611428419273
+10000000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0
+, 0.0013574547186773595, 0.07926513808931503, 0.07102964878743583, 0.0726328476783432, 0.188665578230727, 0.4670863411106134, 0.6080090637275772, 0.44379833749922704, 0.5310140600224029, 0.5931610388683948, 0.4655146952326047, 0.7948299079449878, 0.45438605875039634, 0.7676093316330358, 0.5325662206236474, 0.5570031538223215
+Q
+200, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.8
+, 0.030660402054098843, 0.029515962408728325, 0.01386298123421587, 0.03323790765774019, 0.050077081129415675, 0.029026658778299946, 0.04803400920855964, 0.046080800910896676, 0.05350762941049219, 0.07623076885791424, 0.07766677224262616, 0.12818678386245805, 0.13808429251771162, 0.15976782729500769, 0.18553311032498795, 0.21473885233239454, 0.26862488576858196, 0.3335898147114968, 0.3933292215057105, 0.4471255648270871, 0.5140106474150711, 0.5606446177282451, 0.56645579177951, 0.6187003826533546, 0.6264565231734968, 0.6902587142708823, 0.7310434998635741, 0.7404822151595194, 0.7455970773478046, 0.8194310136729327, 0.754028761152219, 0.6904665436774613, 0.7865490878301985, 0.8251017523682402, 0.25767625767590513, 0.09683794373033726
+400, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.015174619218570292, 0.037092256738479906, 0.02807957626685945, 0.020952215954002592, 0.012490724790011861, 0.015060526993713729, 0.02337051753316606, 0.03585907871833321, 0.04997995343607782, 0.05116995577905059, 0.08421567659761686, 0.11469062311930732, 0.1729106310315939, 0.19279889620933457, 0.2305248874399254, 0.26979519803194807, 0.35005713230775476, 0.442197230272885, 0.5123688583661171, 0.5530137422740864, 0.5569824269831563, 0.5644003448573167, 0.5847125012080069, 0.6854939519416933, 0.7023668672066997, 0.7154562402783202, 0.7676568753108612, 0.8390636087066714, 0.8073438244745248, 0.7137044911123218, 0.7005129526561815, 0.660501122004602, 0.977346670748799
+600, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.06409443044310832, 0.17043537478138396, 0.06708416589502411, 0.04885363429869391, 0.03348033250104629, 0.05051027800044158, 0.027081552065751212, 0.05641650262162609, 0.08745592321636787, 0.13269225526345627, 0.17856051100094275, 0.2604735522213605, 0.2257012643447686, 0.33727227847072533, 0.43694229465002715, 0.47575874028429543, 0.5515806648065332, 0.5766003713260374, 0.5333600150424079, 0.6251127815619932, 0.6323814616237637, 0.7636923593854934, 0.8150120235799707, 0.8554510812808985, 0.8528317416457601, 0.8932737202285638, 0.8996973853124699, 0.7504095750846632, 0.0782035688876777
+800, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.6, 2.8, 3.0, 3.2
+, 0.008569761190854706, 0.01818566336086026, 0.0035886737075778684, 0.019557202027668318, 0.04730700327264522, 0.11713253719294132, 0.11747711101000212, 0.13566131005576462, 0.22173593877845912, 0.23226589196469324, 0.3285201642634684, 0.40175918428239643, 0.514273714962315, 0.6426839325030723, 0.5592607511903168, 0.4484374500207914, 0.4271084024627013, 0.5761775665904221, 0.7207597567939774, 0.8261386365447307, 0.9533012758522402, 0.9546438827372017, 0.9144745505555422, 0.5300797598551829, 0.3222227681422887
+1000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 3.0
+, 0.03406830709780076, 0.059250254494420895, 0.06295161152712787, 0.14778317919449488, 0.2052302207229962, 0.18103793431053206, 0.27419636115330304, 0.43791113182322405, 0.5014274959179482, 0.6332032204767004, 0.4625651540928214, 0.5822609669670592, 0.46439614255985395, 0.5769223811890896, 0.7117235531057876, 0.7830268783073607, 0.7662000487154872, 0.9574188255261584, 0.9791270764706063, 0.3459984609465178
+10000000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.4
+, 0.011375782562223234, 0.015110412493978646, 0.025236652426745946, 0.08429389717841733, 0.14087146909054712, 0.11134410528964563, 0.7012573537893643, 0.6706932505976297, 0.36458107960413166, 0.4313977123835829, 0.5936455596283988, 0.6425632118499391, 0.5015729047474079, 0.692265646351462, 0.8633350923482849, 0.4146853327478658, 0.4854770135494893
+L
+200, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.022694200314865198, 0.04438120468049618, 0.2034621056438575, 0.09596674258314453, 0.0936871324670998, 0.14776469827002428, 0.13802184826675024, 0.13439766666587188, 0.04220458436568829, 0.16944536376489286, 0.11668704319818315, 0.10097026506035328, 0.13321742110406912, 0.1826244747204611, 0.24029848496689793, 0.27293725510554856, 0.3702265782860514, 0.39675970663839577, 0.3865827048643012, 0.4121574468469467, 0.42297029574231776, 0.42374694961798354, 0.45387310170747625, 0.4990958786276845, 0.5249310443913181, 0.5568838889388209, 0.5814034735034034, 0.5874918865708025, 0.544773016253518, 0.5740018578298114, 0.5921713379469711, 0.631807381420908, 0.7064114207808031, 0.7452793584039196, 0.5607075738856433, 0.6177654347620544, 0.2210712700377047, 0.0341445893559449
+400, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.022810154959402515, 0.022810154959402515, 0.007720793314408286, 0.020949129787512125, 0.008085477457937203, 0.025751402801163902, 0.010533333633145176, 0.019532582257393728, 0.02675455267790389, 0.04368701982880921, 0.0642728570843146, 0.09326829811586024, 0.1600825548451298, 0.2628755953431343, 0.33349628915200696, 0.3715599414484014, 0.39048003188172026, 0.3639567373219425, 0.3686159885628984, 0.3815167578493698, 0.4323222079737531, 0.47570929722908906, 0.5414607289365889, 0.5729269739868003, 0.6300711763621937, 0.6385473260853091, 0.6476475561759117, 0.6476414812252087, 0.6798341599008929, 0.7011957467766897, 0.6072402235385015, 0.6366008002469443, 0.28404340538354134
+600, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.004522531317523935, 0.005379331911902031, 0.041373584208902116, 0.06341550713400419, 0.00541136917154892, 0.024970967450366772, 0.01737419159452633, 0.02698710179147721, 0.10931342415377847, 0.16986739364325762, 0.3018235387326877, 0.3955846475210277, 0.36463488371059793, 0.35591519236678093, 0.33945447736638934, 0.3474316317416148, 0.40674510340538544, 0.45543036568909234, 0.5303127712434488, 0.5480395796161592, 0.6182397608837148, 0.6337199392749969, 0.6954318960326403, 0.7741570775389336, 0.7714912509580867, 0.8938303831713577, 0.8447712444019307, 0.6180977910562903, 0.09687554836070501
+800, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0
+, 0.00376740149279639, 0.008994324259810987, 0.011217681458643158, 0.01782829505291646, 0.06663319133607505, 0.10356871471685111, 0.22278722861655195, 0.42553503993613984, 0.36496033595587996, 0.3610925735058211, 0.3820858282736695, 0.4198207783566425, 0.4120354668294257, 0.41931435853443944, 0.3957678881854993, 0.530671191878201, 0.5316696899190078, 0.5311791062303839, 0.5776547205659004, 0.6069486624133318, 0.6843818658889624, 0.6689819508356897, 0.851214696227136
+1000, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8
+, 0.032368973346990515, 0.13969512844032048, 0.2276500581125476, 0.4431061395070267, 0.44559967376517107, 0.4080484956725524, 0.345312870981004, 0.5593329427588312, 0.3453546578106801, 0.39706278392354083, 0.4575762231727116, 0.5177464816296974, 0.47914956650885854, 0.5118580467178341, 0.6401841274969214, 0.6712200329105288, 0.6463117979877456, 0.42810448131912826
+10000000, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6
+, 0.01782272397306565, 0.09395030002738014, 0.28736733981389734, 0.4610503140581492, 0.4504825574929253, 0.5433177175779089, 0.39843618244538137, 0.35264792682742047, 0.2477238149909607, 0.36972491007649827, 0.4492191259986524, 0.4335075346288497, 0.5333388835840573, 0.5258079002931256, 0.4621631821269529, 0.6088093248295579, 0.7590881193363881, 0.7903597389905895
+S
+200, -4.2, -4.0, -3.8, -3.6, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.12438418712700766, 0.017446874253851875, 0.18519360573679758, 0.22124855285421674, 0.059752889119277716, 0.042504821227910966, 0.0663164525876336, 0.029121816870577095, 0.05492112929293922, 0.06720779941392006, 0.08127909407093194, 0.0560348871102672, 0.09659058156200162, 0.10353812840581252, 0.1210800822205396, 0.1562651703452635, 0.20319933061104634, 0.26245317523800094, 0.3369190441521257, 0.4095080052105503, 0.48191521218608413, 0.5284157963491289, 0.5606549634969592, 0.5950347464160363, 0.6187357500970652, 0.6470608094279561, 0.6356898770675693, 0.6714774329043008, 0.6121942241790169, 0.6095743971384333, 0.6564633462830982, 0.8280795031669954, 0.726251886423071, 0.7056292388804567, 0.6339622337745136
+400, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.03629666034518226, 0.010251346587675996, 0.00588376225845094, 0.017103038428544992, 0.014167000294475663, 0.027002831996804307, 0.022073022212184695, 0.03815019381933721, 0.056348100149132954, 0.08949481882232799, 0.11963538365342391, 0.17229156999292647, 0.23102140894642761, 0.30799404180594553, 0.39670138039825537, 0.4994930747566133, 0.5572615383451857, 0.5696161186424068, 0.6158152862031046, 0.6439833627431268, 0.6519249573567936, 0.6333835930691046, 0.6961475696922559, 0.6193005635430306, 0.6552521049455082, 0.7202828580642087, 0.7532262221700042, 0.8334731780932044, 0.2368772002791371
+600, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.013059399423161333, 0.24102871126123523, 0.01585339152122089, 0.028060159218492607, 0.026905462227703764, 0.04927252800462799, 0.03723841497717851, 0.02803294010626132, 0.03829902396434643, 0.06344601230608479, 0.057968769867864034, 0.10541929960012424, 0.19562626406778896, 0.27703475443325176, 0.41179023162123574, 0.5335069983729668, 0.5546150860338063, 0.5747348009876617, 0.5514881911764865, 0.6364965911968703, 0.6801059018570212, 0.6534703205286367, 0.5564196913416785, 0.7302176125008304, 0.8131069350450936, 0.8027519219958231, 0.6085485091562549, 0.8202839094617357, 0.405278218549064
+800, -2.4, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.04216693629487124, 0.0043830307084839195, 0.014462196442335668, 0.01919041673092326, 0.01977825622316669, 0.023712744831969675, 0.055443290493752295, 0.08851448392127609, 0.11455256546377275, 0.2328694553482898, 0.3367326328850246, 0.459317221407433, 0.5879316224828962, 0.6470056873737324, 0.570650862440862, 0.7095801714949331, 0.706462120036267, 0.7170494260774206, 0.6295006361454005, 0.7778910581389612, 0.7148705132619393, 0.7592112207571959, 0.7482104188792188, 0.9522502952396771
+1000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0
+, 0.026214357205221576, 0.04427631406271417, 0.1264935961447764, 0.14077609356326534, 0.2960780435431137, 0.38160536439121107, 0.474947107253397, 0.5727918213213382, 0.6855543310528419, 0.6138913582635468, 0.5778692630210621, 0.6580943515434279, 0.6050444129262045, 0.6168509337333394, 0.8288146290440119, 0.6657847422198185
+10000000, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.2
+, 0.027142096860031932, 0.013757755597518209, 0.0227211975407645, 0.05284974093264248, 0.048659901118917506, 0.16142942336804442, 0.12657774238502886, 0.26692784650029305, 0.3779063407756724, 0.643762567432302, 0.49130042503233945, 0.7242627649143978, 0.6783062480117502, 0.6304716291654356, 0.7038812933324693, 0.9237125448626993, 0.620579612380977, 0.6038819322983577, 0.4726479391294941
+R
+200, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.03207726503553209, 0.005274482832016623, 0.02353481066931443, 0.01858536618468486, 0.029482970704681824, 0.029822145312603715, 0.03156800848371642, 0.030513654789762576, 0.04606365662704011, 0.04081943283545797, 0.05812095484759577, 0.07558217960358522, 0.08275712090661415, 0.09765221787115734, 0.13423340776679776, 0.1489303205649009, 0.2101302968591223, 0.241768086761866, 0.28003078459011693, 0.3620682642630574, 0.41228413694312804, 0.4842228134280171, 0.5208837054055414, 0.5601867659345883, 0.5971129002888642, 0.5931505256179169, 0.6398668937991495, 0.6522501064755667, 0.6681713881294856, 0.6631087700660413, 0.6581604871860909, 0.6224264678321053, 0.6767846770830652, 0.6536296591076727, 0.47393142233152774, 0.6996554390188527, 0.5687643264275022, 0.6981446635867379, 0.4736078327732734
+400, -3.4, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.06120237498398166, 0.041651302875030785, 0.005403336877600516, 0.005329713060580334, 0.014009522483041804, 0.030143197221196488, 0.030445491091082598, 0.029049177244755166, 0.04466300930040355, 0.02959186639672202, 0.07858217329768569, 0.10613250573098461, 0.13756433589801076, 0.1753766185741036, 0.24220992595864874, 0.3379268214752498, 0.43933847181192914, 0.4687608305155847, 0.5219843880663324, 0.5378031723967835, 0.5885073150547451, 0.5790215715229141, 0.6033105062790582, 0.6369385345570912, 0.6367438494487759, 0.6442980558476543, 0.7448119005362304, 0.6787320027676245, 0.7192587968629324, 0.6923730973665412, 0.5134331453351957, 0.5810559726136121, 0.5684926350975337, 0.6125402492396689, 0.9289869625201223
+600, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.1378365503565375, 0.050594717687005665, 0.12781834109421955, 0.08752773884720516, 0.049670070100221224, 0.07664827304436632, 0.046105225891738925, 0.02153118141988729, 0.03233228014945535, 0.0604699995627281, 0.053891025109674896, 0.06548772677419284, 0.15524097503420448, 0.20322318539844983, 0.2685506473941544, 0.3672922083066144, 0.4728367869025848, 0.5215150279053673, 0.5827726312548501, 0.5019429810469788, 0.6236707269413359, 0.6381821952554616, 0.5617610775185542, 0.6050454853873801, 0.6781155911224431, 0.6977041177757346, 0.7921057139590649, 0.6326622936336238, 0.8560845234345356, 0.8638521228076326, 0.9234678570523027, 0.5228541729414256, 0.7057173072099404
+800, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.012317541353916594, 0.006880746923185741, 0.0018860052516560708, 0.09200540112678679, 0.03267249789183022, 0.07654308724863221, 0.13111551917418324, 0.11110315232892057, 0.2858352233385768, 0.3183838804941956, 0.3214764152349552, 0.5381027132381613, 0.5154858992781721, 0.5938759396466544, 0.5317669156056538, 0.474571014220306, 0.6656176259936978, 0.4470667687414132, 0.5694015228099887, 0.5721682279897327, 0.8456605579912806, 0.6845196622874592, 0.701356537506164, 0.9379461680382511, 0.47849324750243555
+1000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.03673099779274436, 0.05975508700856275, 0.11729622266401589, 0.07431155893971492, 0.20129024254078534, 0.4122602456482906, 0.6700041328568925, 0.4307533654784136, 0.43106406500951033, 0.4362086337224014, 0.4358870724581107, 0.5947155769405055, 0.5170772477786677, 0.6550101981782633, 0.42172408225949354, 0.6801152737752161, 0.5717836886325366, 0.8028045185803616, 0.5636513233075834, 0.32157261133978665, 0.7451205510907003, 0.25624149496885484, 0.030797703222132593
+10000000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.8
+, 0.0062919463087248335, 0.024701523260601082, 0.2840737382895135, 0.0578473552912253, 0.07021063189568708, 0.436086646036658, 0.5269060018126795, 0.3787509834038887, 0.4402173913043479, 0.578105949559571, 0.6284310137501962, 0.7335907335907337, 0.45416143979928836, 0.7206038447930181, 0.9030250931270212, 0.6380443086325439, 0.7962851491959756, 0.7771192021827078, 0.4714806276492899, 0.3111369584181448, 0.5469497035762096
+E
+200, -4.4, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.8
+, 0.009621440371994583, 0.004146270499067931, 0.027007253515549435, 0.03136727179124811, 0.038744876752604566, 0.016229285222223038, 0.03822370326896233, 0.03570001601652651, 0.04401784840638429, 0.05086975856820555, 0.07644960546391222, 0.06614806173185703, 0.061318793235008814, 0.10185835962162645, 0.1450144368887007, 0.1425492047876539, 0.20799587783194384, 0.19641586610839798, 0.2043744860695266, 0.2275087023393559, 0.2756277828898441, 0.3308814934337169, 0.4129586159308165, 0.48035942769144885, 0.5342480296805483, 0.5657700139485319, 0.6110696594264812, 0.6158626562381674, 0.6120291915122046, 0.6455192674095419, 0.6780413894925083, 0.6495724311929135, 0.6361403433588055, 0.7950927440077705, 0.5949832111286862, 0.37058920504165416, 0.3524209038075929, 0.5271389454687425, 0.7803589040683762, 0.47464831353328946
+400, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.017589676152591112, 0.06683204264988173, 0.01657213629129284, 0.00332003136981703, 0.00930518388267171, 0.005974036257469811, 0.017903762602890994, 0.03761496143931975, 0.03408447798140072, 0.07714241431466798, 0.10029575266470864, 0.09561574949799023, 0.14597655312457017, 0.15454703000082912, 0.17666151890870305, 0.18425024121949826, 0.20492924164637813, 0.27328431284733345, 0.35372364617784047, 0.45535119003288305, 0.5450906270210514, 0.5819563761275088, 0.6164230496799653, 0.6272027332867812, 0.6065517245120872, 0.5779706100641258, 0.6540444997527108, 0.5860082895169939, 0.6757729762046188, 0.5782390265906364, 0.6483029111399775, 0.7238765375054353, 0.8145822355514108, 0.38720430152211993, 0.29944373638984595, 0.26181929584821206
+600, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.053283575664541366, 0.03616471216877128, 0.06326608218206026, 0.011131203387245636, 0.028527095216323052, 0.014063364780183754, 0.045519433969399455, 0.10546598100346138, 0.14949350502179756, 0.15101877591261562, 0.09738810031195794, 0.13481415571820254, 0.14257886566448752, 0.2860309312905558, 0.3680760044864281, 0.4681448135606153, 0.5807334464914079, 0.5840788193716014, 0.5684825986445871, 0.5423637974395175, 0.6322784178809525, 0.5910884453929658, 0.4403967228080618, 0.7715571363898848, 0.6055828711644718, 0.7943209953485183, 0.44098092121701576, 0.8394721618942949, 0.771295161426513, 0.28710576355040146, 0.07682020988610647, 0.011131203387245636
+800, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.03430651347512906, 0.01170316774637141, 0.02172101047389925, 0.006417685703626693, 0.008803131942231902, 0.02558597050998453, 0.07306274568825875, 0.11280115830989065, 0.09350632912488134, 0.19757784784170848, 0.13668996284243146, 0.10109885106932971, 0.21372922331058639, 0.33935195047966055, 0.4835653244772779, 0.5933154629689057, 0.5389606803781795, 0.5486205951197954, 0.6256878350116513, 0.4954150416424488, 0.6982028550061831, 0.7725732080733803, 0.7556245509596634, 0.820419992617725, 0.3871956296488177, 0.21161330536631878
+1000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4
+, 0.007075408536552707, 0.052106342694856415, 0.054267687080583535, 0.11138738604476046, 0.18238487903712933, 0.13756590467213264, 0.20171268632711103, 0.3204675946135979, 0.4939881373188593, 0.48725191860865325, 0.4577231292051552, 0.5029274757637503, 0.5911749097279292, 0.617890908127232, 0.6754028092464903, 0.7538926271666511, 0.7790160448683208, 0.9317881518423954, 0.2815681472036956
+10000000, -1.2, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.05348182058279778, 0.033963789927669706, 0.16707500121047786, 0.16511036477645546, 0.17236127301119492, 0.10153344754105208, 0.5210622228521675, 0.5405779170203302, 0.6467331482363765, 0.5613844499745355, 0.4960746174216681, 0.6196549070937729, 0.5823067577399326, 0.6626260661097897, 0.3857996700919029, 0.2655828409440213, 0.7804169453620828, 0.6799529670918482, 0.14494194369333543
+K
+200, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.8, 4.0
+, 0.006233425622353032, 0.030408921119546396, 0.02093592220185473, 0.03558424628095059, 0.010346048488097845, 0.005378266919520889, 0.03131994736626652, 0.028487448042530204, 0.03379094987238925, 0.024700449398553797, 0.03347424071928632, 0.07302434835089781, 0.073512659195887, 0.09610703764665972, 0.10795809398583348, 0.12494848067773846, 0.15366097022715058, 0.19623704670279507, 0.20948968747096094, 0.24338255490000843, 0.2906241049173688, 0.33876574586758207, 0.4184822248111851, 0.47928225866895335, 0.5290089476862202, 0.5603050017954271, 0.5786006171900762, 0.5901574708056799, 0.6223281637221282, 0.628128623412185, 0.5921857112354082, 0.6140101201683108, 0.5920359569841578, 0.5965921083365542, 0.6087237791828294, 0.4924534069623098, 0.38684136208760095, 0.8267798205668004, 0.052443291913803605, 0.13555614153669435, 0.4949704158969042
+400, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.6
+, 0.020798610371752655, 0.005632200186423676, 0.020798610371752648, 0.017012541269074165, 0.006032073496491691, 0.01364226026152598, 0.021143675648025915, 0.01868251927506294, 0.025685901851698386, 0.05158053569829252, 0.06125362327569112, 0.10431760125648461, 0.11657963235000496, 0.16124223048718866, 0.16595610614723216, 0.20943134871276328, 0.2710357434275307, 0.3346119224884066, 0.4332968718252851, 0.5110259226245114, 0.5749134438517876, 0.6081320558919092, 0.5874087675618509, 0.6035394962722405, 0.6231341763893807, 0.5835358073444723, 0.5961581174202724, 0.5709563638636703, 0.5779902811937659, 0.6074718329071743, 0.659698021432162, 0.5065478271339936, 0.8797381245584289, 0.7417154908480318, 0.44066114867280576, 0.3040791284756775, 0.15208479719465084, 0.0783083282618651
+600, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.08005202718154787, 0.04962009188671522, 0.039406216500345155, 0.002740406190104946, 0.011403405751254717, 0.0028098082516713977, 0.0071633756207432455, 0.03306075603501498, 0.0646831514898025, 0.11569513004064116, 0.13990454263335425, 0.17679877588621143, 0.16262258352799974, 0.1706813596201359, 0.2931339965678484, 0.43799455936872084, 0.5507332991803784, 0.6083433873274956, 0.6506958124282605, 0.5950317952941324, 0.5577837159753122, 0.6396812143891745, 0.6025343792533054, 0.41729136641693454, 0.5159270205746278, 0.550689666206242, 0.6076331037708719, 0.5624524359539496, 0.6696235241307377, 0.8959934752809087, 0.07262822815281679, 0.13051495642939867
+800, -2.2, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.018911760605919804, 0.004795965261657025, 0.045975193728396195, 0.008989657476380065, 0.017318826511208574, 0.025057718947206414, 0.12261710593295176, 0.11213161725895983, 0.19187686131466689, 0.14188715644056538, 0.14546530966094284, 0.31206534648850154, 0.47086768089542014, 0.545789218740381, 0.5885204739595565, 0.5773710383535828, 0.46045973024930353, 0.5908802815580871, 0.626959762836653, 0.6316980119840698, 0.664414162678641, 0.79310547563987, 0.33278822567457084, 0.7061514195583597, 0.15356389927792902, 0.054667562122229715
+1000, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.02052890441228883, 0.04023189215617797, 0.07663031858028652, 0.20025096556556882, 0.18400997644450603, 0.18611930275336508, 0.21527083749750153, 0.3970930399563381, 0.4481132941803083, 0.5686396636354857, 0.5301579703207275, 0.621978803891937, 0.7140332311751852, 0.6286470990555741, 0.6377560435504707, 0.7720935141696131, 0.8320930630399452, 0.3946962688868332
+10000000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.6
+, 0.01260629568849769, 0.01879100107483044, 0.09267256151895265, 0.04568967070994719, 0.09962589275592335, 0.11544315827770806, 0.28789943693289816, 0.2591618348295238, 0.3929734264526971, 0.39127635602768385, 0.5935754756602113, 0.6145883983450332, 0.3876823250131941, 0.6618930447590909, 0.6606724003127443, 0.5138853185066581, 0.5624084011945034
+T
+200, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.008187665742003893, 0.028861201409083426, 0.00980913610487576, 0.1083985994398448, 0.0598706444724795, 0.05947128234147885, 0.16040484993339152, 0.09915598512252316, 0.10446723349172699, 0.1200298989675841, 0.15094705758522858, 0.1328601676913035, 0.07572629791473424, 0.12872358724292082, 0.14537735848143038, 0.18757394078969675, 0.20983017792030184, 0.2754583875906324, 0.3381900390309666, 0.40271265447333354, 0.4729359584475696, 0.5373178412824395, 0.5595641713342255, 0.6104980864110142, 0.6378975434547939, 0.6441654366086963, 0.6732334439933628, 0.666053356346672, 0.6774517895349775, 0.6637156928572294, 0.606772055426944, 0.6491227745638032, 0.6968486954506264, 0.6098259501108972, 0.6265547114255277
+400, -3.0, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.0027025687608381557, 0.004064194112156794, 0.0016032875228119338, 0.009757997483425634, 0.007868980509490626, 0.007607238389344213, 0.02595046601655586, 0.01905911002852271, 0.031882814581105576, 0.04062653475175, 0.08168889317790175, 0.1302149726190696, 0.17840065319707346, 0.2606656745972047, 0.39450792466149653, 0.4911094546024143, 0.5585520055189517, 0.5923224948870527, 0.6302384579809271, 0.6457682162071837, 0.6946071862394861, 0.7185853742529493, 0.7454414752360349, 0.618499419222971, 0.7074628958097988, 0.7352779958711105, 0.6955516339755666, 0.726348989935302, 0.31671773573998374, 0.40958798265579704
+600, -2.8, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.052328852070039164, 0.026867396031232334, 0.16837658222176605, 0.024245798575350842, 0.0262292817988048, 0.12130083036303745, 0.0613620195234056, 0.060587459883910885, 0.05055444507030803, 0.07451277195194778, 0.07160801588372659, 0.09814403889181059, 0.23180293729028179, 0.37716308908081375, 0.5000132531217487, 0.5844300853732715, 0.588706451584414, 0.6364889102697228, 0.6447154143653666, 0.6571490187910064, 0.7745048328842353, 0.6898685260527815, 0.5933031009014547, 0.8136253894039864, 0.737081747390056, 0.612229773163282, 0.8993016606028706, 0.17838061372014036
+800, -2.6, -2.2, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.8
+, 0.021818989702759535, 0.021818989702759535, 0.026069027674035287, 0.01465256116221214, 0.029619466548546323, 0.011029824666793777, 0.019680030570921273, 0.03947987072242306, 0.0986257950403479, 0.18456806333044862, 0.3139427691570446, 0.4815565268749844, 0.6261430195281782, 0.5140415380874448, 0.5665602405952214, 0.5992870215665328, 0.6250365102046636, 0.8048492147720124, 0.6377275954637704, 0.5701006015265965, 0.8557024286413152, 0.9530038501783763, 0.8085467849780019, 0.27882798691668526
+1000, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.4
+, 0.009332428725528, 0.04925965308388099, 0.07181890841950693, 0.1406860776336484, 0.259162393696497, 0.601915813203475, 0.5614990245451659, 0.5400938444987884, 0.6009355243934302, 0.5240251717766059, 0.5904347159672351, 0.7086467981563515, 0.6532544497183701, 0.7830103565979136, 0.8442480557193919, 0.6152519207547671
+10000000, -1.4, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.2
+, 0.017400075652502834, 0.013968671710909226, 0.016258931051023752, 0.02276972220938904, 0.07144236497729933, 0.25014108242074024, 0.6377278912714578, 0.6213497886400078, 0.8644058949419727, 0.43810672452330535, 0.5254430132270977, 0.8481163148765324, 0.3798431831480397, 0.7667171569008974, 0.8092008618156888, 0.5447444189442937
+N
+200, -4.2, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.0712065157005121, 0.13294638271304174, 0.024918407202641813, 0.3991956150411048, 0.0953625460996715, 0.022620790870142397, 0.07653812401707984, 0.04013908322757407, 0.06637157555040195, 0.08790716077730971, 0.06987895197872204, 0.11594595731928202, 0.09469654486442684, 0.1055032820197743, 0.11677327019941976, 0.1383930868361456, 0.17556655708200233, 0.21274981636519386, 0.24681031402251496, 0.275707492383167, 0.343676483955714, 0.41361073312621865, 0.49954020026571794, 0.5289597425675846, 0.5644353295028, 0.594018016380281, 0.6394736076806113, 0.664612890069819, 0.6676529289997108, 0.6609569315008057, 0.7241923463576184, 0.6799056719677031, 0.6618977735500067, 0.7626413640672547, 0.5363488878232969, 0.7741958263705373, 0.6860956762633909, 0.7969009695833165, 0.7827637598777691
+400, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.001128461356220943, 0.007744997057307509, 0.007104165781121, 0.012715233504607883, 0.01992997097665505, 0.010023506476345678, 0.008734440583315614, 0.012766891527232208, 0.037250387032780934, 0.03554618185109885, 0.0641277000435746, 0.10383126855713888, 0.15817420705543073, 0.1590150077356472, 0.19733501123405667, 0.2655449837641665, 0.33774475148059496, 0.42156779664100374, 0.49403682221432815, 0.5567834425997928, 0.5869474638861647, 0.598529564395528, 0.6427745755499635, 0.6511468650484329, 0.656275764390928, 0.7339886223828026, 0.7133697636691161, 0.7833908369222597, 0.7814001135653762, 0.6921926967996985, 0.6930677127575958, 0.8719006330411663, 0.6993407299981116, 0.7520194008570942
+600, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.012861627109688046, 0.05731785655357574, 0.07991944732610307, 0.08479562252863253, 0.08630884538516512, 0.06730490514845179, 0.008611336673529002, 0.024364202282699095, 0.04027411837341045, 0.05638279176751919, 0.07866817897417579, 0.07014008024714515, 0.1488228079108204, 0.21117858715982965, 0.22492604700759586, 0.3527511628371557, 0.4612776292791651, 0.508376750438461, 0.5978425447842742, 0.6169494954975383, 0.5870009692193582, 0.6834443025176401, 0.7503292713151367, 0.6041252831156599, 0.7114403688322494, 0.7942233006410815, 0.8025344848026892, 0.480061963819044, 0.8510863288832535, 0.4311453569540338, 0.4549099721194074, 0.8294748061125389, 0.3232898151906798
+800, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.019989818428644154, 0.023892261719647902, 0.0029054710661450957, 0.004062937593554483, 0.008984136668700427, 0.010308406280718847, 0.08691527710437, 0.10361026893347543, 0.04789619477114294, 0.16745026833775423, 0.21747643195160038, 0.20845806212626344, 0.32694891534008996, 0.47723349576907653, 0.5295067551508112, 0.5770392423901299, 0.5730686049665393, 0.5894890079270304, 0.6047381214895978, 0.6682541217110526, 0.7311441820863821, 0.652518439576027, 0.8361938939030221, 0.4820988922476904, 0.7577299982101305, 0.5925855581398125, 0.12158342006740268, 0.9351822615425827, 0.12598320331575102
+1000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.08803701255783213, 0.09574103749053965, 0.08474819481634101, 0.17517515123980776, 0.26799302520655116, 0.28439407716762816, 0.41648646362717917, 0.5455595697137675, 0.5615697118402637, 0.6793471491223992, 0.6046007911293165, 0.5491817816902066, 0.49692743170413184, 0.7221889848936341, 0.5269348636773619, 0.7588655952688864, 0.47213759827611784, 0.4709761521355724, 0.643696648197198, 0.20472622478386168
+10000000, -1.4, -1.2, -1.0, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0, 2.2, 2.4
+, 0.011076077289999191, 0.021909483447944987, 0.019761832256823684, 0.1397123465907093, 0.42129914368169563, 0.0812885393899792, 0.40490640849438697, 0.445164695597671, 0.5457577680850202, 0.6138506965124918, 0.499762804469091, 0.6076612903225806, 0.6718496818294005, 0.36172655263459996, 0.30939476061427285, 0.7177078765779696, 0.6569729941950139, 0.7172609072522392
+G
+200, -4.4, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0
+, 0.04980271364834668, 0.08033718293586467, 0.02137202174972912, 0.23924182209704178, 0.04290368826652217, 0.10995998186386532, 0.09077215489621959, 0.020063069662946225, 0.05199866894271401, 0.09869248618709753, 0.05755910536963877, 0.07335095177942563, 0.07836111992160895, 0.10947432785061317, 0.1829574600664543, 0.19644932151930236, 0.2475476355976773, 0.28047029664287276, 0.3663585502825831, 0.41381370184743865, 0.45028476809894513, 0.49271429268477074, 0.5257516371642744, 0.5380569146271968, 0.5816939874640986, 0.569518767778985, 0.5743891432518737, 0.5686338321658405, 0.6140459068175762, 0.5643093026733956, 0.6011486020284298, 0.573474665378709, 0.5168116740045788, 0.5321248352677072, 0.5278162040229152, 0.4301895642957476, 0.4371969736562028, 0.28501611757799006, 0.6434525427174271, 0.28996678413494487, 0.08989837533651014, 0.2567539583435563, 0.04184963224881832
+400, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.013901301934598695, 0.004289024917785121, 0.013437974346902313, 0.007368860439472664, 0.01073447463708951, 0.02777219816300249, 0.017735008916501906, 0.03167632189935238, 0.065395714271814, 0.08023970903915105, 0.11154164414214832, 0.2022512296813042, 0.25545491618906413, 0.3536476783862624, 0.42363655299206915, 0.4684936278546968, 0.4921050743779281, 0.5494874471369424, 0.558926685575915, 0.5687086639940497, 0.6215795839689724, 0.6083484928768131, 0.6314576358198148, 0.6230514702472063, 0.5920065807809958, 0.626983997044515, 0.6050340178513397, 0.5291775304616348, 0.49878218646796507, 0.47333053527490554, 0.5819171331727555, 0.5436606285875814, 0.7368953451348795, 0.21159919104802896, 0.2423277350632243
+600, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.004262592522543604, 0.009609989324161305, 0.040722908138649094, 0.017614076524928034, 0.0093627162404978, 0.015355034655796965, 0.024755600324803256, 0.020194055025720813, 0.026706288439159422, 0.08077741729614026, 0.12928457280333558, 0.2529615159130915, 0.34196845700567596, 0.45149860216958276, 0.40996695404314654, 0.42578108033137246, 0.4803786191950474, 0.5843776520341477, 0.5586147764363851, 0.617306728496986, 0.6227856958198603, 0.6671398928615958, 0.6500952347555135, 0.6889882325738811, 0.6256734694615649, 0.5995168124878059, 0.7490707491628702, 0.5011582262180214, 0.5080490865168124, 0.28843898542192936, 0.6652919608969325, 0.7408142102868135
+800, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.0020230132774187795, 0.023747698329704935, 0.028075380703131567, 0.015626043218693486, 0.04235468455893636, 0.09197490603136489, 0.18883534980280212, 0.27670832943186874, 0.4442895339785569, 0.36389293680452, 0.5494049531835955, 0.4340033509928248, 0.6029975424434239, 0.5363372195839183, 0.574020584323784, 0.5723823552003017, 0.668482030254807, 0.4580004693100662, 0.7116840257389728, 0.5822536180791649, 0.4377759423870536, 0.6468675879501711, 0.7183741297796663, 0.6254979951414307, 0.5158137378302833, 0.7279496888294327, 0.2627362326894526
+1000, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.006489542114188809, 0.006949859307726209, 0.022534370804249342, 0.04516393000377678, 0.1136821257633273, 0.5110285116517171, 0.46770290612880394, 0.3659219700856653, 0.4583001142462624, 0.41336563823768924, 0.397921392614628, 0.39534194552429375, 0.4342297564398891, 0.5579332833345442, 0.6046583720178514, 0.41123271903157327, 0.6403926457323382, 0.6698778696734571, 0.5480418899094861, 0.7586576713030415, 0.7548134802193488, 0.6548685078706759, 0.9440873569829723, 0.7717038041572167, 0.23724929693196156
+10000000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2
+, 0.021380286118534823, 0.027108017567662453, 0.1482481505468543, 0.600664441493963, 0.6398889295975626, 0.37057797159012834, 0.7850458683916712, 0.614502459890595, 0.3122958193267364, 0.8122341869514071, 0.5943585978600379, 0.7604878736966307, 0.5587283184181798, 0.6866167554855552, 0.4973986404656242, 0.780872442534018, 0.7371014288730907, 0.30209092437916923
+M
+200, -3.4, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.02845963128097755, 0.006327814220719094, 0.03532321766363078, 0.02785817915545463, 0.06305230492182508, 0.06525558678224297, 0.06920333364821739, 0.09663310690550701, 0.09168272875852655, 0.10444060917510496, 0.14161496195972897, 0.18760094354688894, 0.27340880409079077, 0.3140034341765975, 0.3341867951289014, 0.3632541277201427, 0.39668565401586187, 0.43522081815108804, 0.47207835629148115, 0.5026342064038121, 0.5554008469913351, 0.5833574566458571, 0.6008099311783961, 0.6450861919646963, 0.6497380418171971, 0.7249028494100742, 0.7645552091301733, 0.7775959306800271, 0.7826666371582449, 0.7687189766034876, 0.7377750773840379, 0.8546113117648433, 0.7309510025155838, 0.5922461289520015
+400, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.038170835425479464, 0.01826291926366975, 0.008627909268057714, 0.005990921954905756, 0.007691489978484118, 0.020392739300842774, 0.04472291093102174, 0.060316255940233786, 0.06302004459220394, 0.1541038418593209, 0.22685507146016778, 0.2784664330885808, 0.29825937959171117, 0.3196733796620143, 0.3640325566040108, 0.40625749304857856, 0.4479345337346429, 0.5468608319197934, 0.5597030539512597, 0.6165686331561653, 0.6632688548164488, 0.6959674881486, 0.7903394987481768, 0.7268668913376479, 0.8235451001308638, 0.8275484601484625, 0.8098358326580584, 0.8637631783068633, 0.7737756627826707, 0.8647890997855323, 0.6977375511994904, 0.5247504661744165
+600, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.005736237861731748, 0.009523975244624639, 0.023891194668858876, 0.025234750802993748, 0.02552717930757656, 0.056254640669280716, 0.20941928914694077, 0.19788347655830058, 0.29589811372140384, 0.29852413139321216, 0.2897028729418557, 0.39073179258287705, 0.4552629356841357, 0.5593966833420538, 0.5750394291646366, 0.585085568055378, 0.7318672657089549, 0.6941092464581831, 0.8819680496498116, 0.8406532752846406, 0.8451650670206011, 0.9416795740780045, 0.893085997768151, 0.9110602722058794, 0.7163256112121613, 0.7174375236482226
+800, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.4, 3.8
+, 0.004674108580720676, 0.0035096825949236417, 0.02573551674322442, 0.08020943690055882, 0.2141222710358276, 0.3552257946322249, 0.323221883037281, 0.334294250957123, 0.2829357833432641, 0.43434992803525485, 0.5808355469054186, 0.6241893673747914, 0.6931880921298325, 0.5795185281057724, 0.5509178962895578, 0.8197337451110486, 0.736948316454277, 0.7846023423062995, 0.6580624097643196, 0.9191076830846109, 0.9023948888700153, 0.9474981632934666, 0.016170424235835836, 0.21365134335027647
+1000, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 3.4, 3.8
+, 0.0395955810122565, 0.20251297074083224, 0.2515696605044113, 0.23118137403932495, 0.31920693493675995, 0.32386507072905335, 0.5120874998766078, 0.5880070517335512, 0.7408938081106, 0.6469459262991496, 0.40303988237456356, 0.8411104193700822, 0.9040742300533903, 0.7482458141989218, 0.7670674781862866, 0.9574210199417486, 0.23437245866088377, 0.1630415456646714
+10000000, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.2
+, 0.005278592375366569, 0.02483900643974241, 0.21079571893904142, 0.25278114329772, 0.24822695035460993, 0.39404962972586727, 0.8361985706007341, 0.5839568257083283, 0.5340659340659342, 0.7478816708785491, 0.435701214440193, 0.7346019028542814, 0.5848733325174398, 0.5098265895953757
+W
+200, -4.0, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0
+, 0.010053137251715156, 0.03614107661201093, 0.07513752318069507, 0.06232763609362001, 0.04090719053394041, 0.03012721074281631, 0.01999689051399049, 0.020024382749013706, 0.035560145033910834, 0.041624797523196214, 0.06142487113870969, 0.07038694508427773, 0.0874141479196364, 0.14373224227628087, 0.1930317693688374, 0.17608850960040795, 0.2837591992712456, 0.31751427975693, 0.37402457119572285, 0.3767044103235768, 0.3875467674943792, 0.5162805569385304, 0.5513809807468663, 0.43645713565312544, 0.5295897770124752, 0.5509452638284295, 0.6355399448481385, 0.6292792289200955, 0.6154280980913217, 0.5862191648292456, 0.692729444731882, 0.6933688155617883, 0.6525897180760073, 0.6862900638687898, 0.6206267900753528, 0.7227963137333674, 0.6563610741001843, 0.6541491631508591, 0.6862278523667734, 0.5173986502301023, 0.6254962352564359, 0.2376686434766528, 0.2850520026378816, 0.32445331851643955, 0.1545443294828597
+400, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.2
+, 0.003936305168090178, 0.002892443565021887, 0.008130955780809584, 0.012686225767031701, 0.025774402091606103, 0.04458131185056105, 0.05957848834895015, 0.09030918669627554, 0.1253577531298458, 0.1762117242340049, 0.18978784602769633, 0.2613781994982344, 0.3479702350864556, 0.38339977058667163, 0.38746151324205624, 0.4143832977912655, 0.4326335523770048, 0.48109640926677794, 0.5253077885613805, 0.5436337693892012, 0.5957468274559441, 0.6066920552022064, 0.5856318907005583, 0.7405960813443107, 0.6556337756952331, 0.7700481180232187, 0.6123487455485063, 0.716084361648064, 0.5946136429487322, 0.7631646331908402, 0.7287205565301803, 0.7089736280501816, 0.7458023147425407, 0.800312812064102, 0.7905807458117725, 0.7524453487016617, 0.6904598548976143, 0.09244951222078437, 0.8853441973001723, 0.22803862653375243
+600, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8
+, 0.015941116115818707, 0.016594307129943656, 0.010924353018042558, 0.014218984649465553, 0.02534885725861216, 0.06771148595573359, 0.06116471271630187, 0.058466868227181355, 0.11369094282044122, 0.10028915633745711, 0.09713241672842075, 0.23121690042026713, 0.21175385468088415, 0.28462241635242436, 0.31685345966888984, 0.34243553290584766, 0.3967943463270171, 0.5322292663560987, 0.46053074781033976, 0.46756551159626847, 0.5093913642584738, 0.46665721497662693, 0.5872387559719567, 0.5295263318218335, 0.6679207002526416, 0.633194898204035, 0.8205977787023934, 0.6778801000144382, 0.6386241437433336, 0.7474959665347901, 0.7280984639756457, 0.792440375474518, 0.8269539563283489, 0.914446352387371, 0.6292990018576369, 0.9002209011938572, 0.8814371535125446, 0.9569213613893796, 0.5613551803722755, 0.15303410010373106
+800, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0
+, 0.0224761653850587, 0.06897795528325656, 0.043964184488533876, 0.05426990516042012, 0.10593446276907946, 0.11088933395600369, 0.11131403251399657, 0.41212081201518397, 0.252185523895901, 0.5131779640168599, 0.4970442274474704, 0.3753445990025042, 0.4454367118169079, 0.43641127627008697, 0.5375555342059549, 0.582743847296434, 0.700875716747453, 0.3693056263810981, 0.5166424534068959, 0.5938453539126746, 0.5650094444620126, 0.7110946731164166, 0.6077794715264038, 0.797757938175236, 0.5455658878175588, 0.673111514092521, 0.7037020400243812, 0.6435682203777798, 0.604048928630185, 0.8674003285619997, 0.2236593996887986, 0.928303029180713
+1000, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.6
+, 0.05041657765434737, 0.02326957207651351, 0.03004073319755601, 0.19675712971481144, 0.3266965127238455, 0.22095951443145867, 0.36550759392486015, 0.7630891680139287, 0.6041852181656279, 0.8423339540657977, 0.6174986823281381, 0.39799453053783046, 0.7304563141139018, 0.6849573328041277, 0.3470302731161567, 0.3593023674822529, 0.4835460460460461, 0.7854439789160697, 0.6272501956691886, 0.6353194544149319, 0.8131345333267939, 0.6726479627242986, 0.9429828499595941, 0.7991883202445581, 0.6992572398895678, 0.5080599505378266, 0.8768982229402262, 0.4046968016124748, 0.8114981199287552, 0.3381969775924961
+10000000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.4, 0.6, 0.8, 1.0, 3.0, 4.0, 4.2
+, 0.0021122338299937207, 0.1448140900195695, 0.07805907172995782, 0.28139773895169584, 0.22224694104560624, 0.16373639516804211, 0.3721264367816092, 0.3226118969192017, 0.33218720152817577, 0.0726298477372805, 0.4892249251223611, 0.8037608756665732
+D
+200, -4.6, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8
+, 0.018626895228895723, 0.11728080320078006, 0.5937426667262748, 0.03927533862645874, 0.05974671976524982, 0.0917149470704997, 0.06046434414289128, 0.04459964351175079, 0.03891584428680007, 0.05454776436988832, 0.05760795314793352, 0.06520214434682021, 0.08441878992655659, 0.10554307834242026, 0.13156414894978147, 0.14638211055267963, 0.17598859363160593, 0.17217531685054885, 0.18463737353117532, 0.20794269423656211, 0.27808363911099476, 0.3642544766197776, 0.42615125908706963, 0.5026779437159384, 0.5381913735877556, 0.5448344936741886, 0.5736658815321012, 0.5728175734848698, 0.5772159313301718, 0.5935206601384001, 0.5785927893804083, 0.5803030006598179, 0.634696992284186, 0.6632184597205736, 0.5725772600179964, 0.5806805560809384, 0.6456153291100302, 0.4008137326056845, 0.5142766154383125, 0.594144624446019, 0.9480860255395239, 0.7891323212963747
+400, -3.8, -3.6, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8
+, 0.017526022053705452, 0.010090662246000215, 0.005911072408159453, 0.0094242912522625, 0.00483203212764107, 0.014840210378253412, 0.035251055138788005, 0.03010333597027076, 0.05707110978001972, 0.06806654215390828, 0.10122954326734433, 0.13770152403508962, 0.1251534611085536, 0.15864699520014003, 0.12863271934388903, 0.17303911256916327, 0.23872337803608437, 0.3438374233682127, 0.4471077553553215, 0.5299235923012301, 0.5563624942893748, 0.5662502058564726, 0.5721638839165779, 0.6129871191897227, 0.5660340347563402, 0.6308606276684164, 0.632643587440431, 0.5798265428394158, 0.5711256183324998, 0.6104867833858457, 0.5477759251339209, 0.5034780015525244, 0.28633253697579686, 0.6019606099741027, 0.41753839461241327, 0.34898716933495605, 0.1753443010109411, 0.4937170887940028
+600, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.07572055697912322, 0.04685137919874146, 0.013470059859080275, 0.016120642626666723, 0.025934741332931405, 0.013470059859080279, 0.01047103161086878, 0.043184882720677706, 0.07524094553614041, 0.057131662342132405, 0.07555065450499734, 0.09094351900345422, 0.07943708440995989, 0.14120981974737906, 0.15889063869132145, 0.18288901171356836, 0.2994462254923587, 0.455922970640683, 0.470865053294829, 0.555923147176361, 0.5744394407250271, 0.5983652427055746, 0.6451298402584105, 0.6144094316369362, 0.5638708071261487, 0.5817023571301216, 0.7382954315694873, 0.6415137553013202, 0.6027798778331301, 0.5498240561188864, 0.3129098587277003, 0.5853527174502478, 0.6417674076529638, 0.34646976044148603, 0.8892401221593547, 0.06589683307979534
+800, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 3.0, 3.2
+, 0.008510660029390252, 0.053999319540527856, 0.029166739590808066, 0.11008787306416677, 0.06867749030405805, 0.10487922755985372, 0.08441754322657907, 0.13927445001053515, 0.1262502840915484, 0.1581350088904876, 0.37032405294594306, 0.47112296199708115, 0.5059968009083806, 0.5667473589508149, 0.5662007078489808, 0.5036665508717231, 0.5685524410133438, 0.5021030771387083, 0.46777863494735494, 0.6249417586714079, 0.6570922695616377, 0.7799387736421073, 0.7624247583463559, 0.364603814519136, 0.8518129178326466, 0.33007532508626103, 0.2808647177027291
+1000, -2.0, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.03099074564389641, 0.03526188187699943, 0.0601183779288754, 0.10708870906841245, 0.14124632869365342, 0.07858307490648676, 0.16099733636085756, 0.11251951702527954, 0.31888428435752014, 0.3845687605484582, 0.47978895233223035, 0.5356864443869647, 0.5760458574570188, 0.5243609252477301, 0.44676271680565494, 0.5517489421762919, 0.40731563845050217, 0.5530463031664227, 0.7056189995435903, 0.6602700897805741, 0.7555583244746631, 0.43490969602040125, 0.9630958463140022
+10000000, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.4, 2.6
+, 0.0014151021012620737, 0.01120977596741344, 0.04853957951178213, 0.06924634213674427, 0.2242589254898953, 0.3488169966199903, 0.4214279585474444, 0.5540891613841836, 0.6191275770879868, 0.5765505268784041, 0.7524044757887831, 0.70228140959197, 0.36758525047113005, 0.6752083232984923, 0.8562413388764525, 0.4384222653499529, 0.2229776446185757, 0.7973201791445547, 0.13059610618454792, 0.0663917127836773
+H
+200, -4.0, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0
+, 0.03366143791073794, 0.12229579473890749, 0.0023965874294210304, 0.11142894439620184, 0.0598821754984208, 0.035073646976856346, 0.030033608637588124, 0.042770531837719, 0.07436799354604083, 0.06192889876940986, 0.07501889297412434, 0.07651922461089912, 0.10236657357048272, 0.13008490869476172, 0.17570521982550932, 0.19562570288549183, 0.2402717212537774, 0.34894741760751985, 0.3926314534309388, 0.463459973122341, 0.4872892573845177, 0.5656076053978417, 0.5702410891638474, 0.5842610000246314, 0.6401215021032886, 0.6635057656919326, 0.6858272882551264, 0.7036437455064729, 0.6900683507603625, 0.7912016521539376, 0.6256905279756557, 0.6551663451279496, 0.6442387019495477, 0.5154430296772594, 0.610714922104654, 0.47655815437182536, 0.5038680884987706, 0.331638615368125, 0.16532032785248002
+400, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0
+, 0.024983509456821706, 0.0023065782730348084, 0.02498350945682171, 0.03938325596225074, 0.01924885711675501, 0.017001918349411307, 0.004532697766235948, 0.01672786076537002, 0.030488464144219574, 0.04246194666294303, 0.05482530743289866, 0.09453774664211397, 0.15068238595070957, 0.22043514025408253, 0.30862698998080434, 0.40606922105192056, 0.4635111004362203, 0.5490011391057846, 0.5896936531471226, 0.6012522032441759, 0.6334213044527051, 0.6530489917577715, 0.6816836432220428, 0.7235863198736988, 0.6104989855312881, 0.744281538492875, 0.6437700766365099, 0.6596101401847652, 0.6237587050584414, 0.4909123749450153, 0.4754536332396059, 0.5319185585373064, 0.5738471453688846, 0.5770204247077545, 0.3974957524720365, 0.12628946646351794
+600, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8
+, 0.003375360767234541, 0.02315851530668037, 0.007713013203962888, 0.020489809218885796, 0.02789797627428184, 0.05670945810086781, 0.08172911385087334, 0.10354699727263074, 0.20946023963686963, 0.3485166802620737, 0.4135052949447255, 0.5048283954868971, 0.5810659272872755, 0.6252028870513755, 0.49276922092287434, 0.6972373542165986, 0.598283244541968, 0.7060471943352125, 0.6335714820846869, 0.8409656287721421, 0.8713022883991768, 0.7548429339870076, 0.7731077709300429, 0.6286562667563377, 0.7416134578437232, 0.7973583875768113, 0.6246797630480752, 0.44839283867200974, 0.618207741949151
+800, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.004332254797836343, 0.008517472428799602, 0.02801288412634691, 0.04042976376212141, 0.05638253860440923, 0.1485878639312434, 0.45494966839303996, 0.5364218471499814, 0.6019752585827106, 0.6468967344018158, 0.7291083814949837, 0.5541234821386483, 0.44092351636485544, 0.5412120458980819, 0.5492062498043022, 0.6146558441172213, 0.46263676380203994, 0.8097888157180061, 0.8253314552869299, 0.9025689021252576, 0.3065145430410163, 0.887952826612005, 0.7349291853455371, 0.6930569512214189, 0.7189787433389939
+1000, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 3.0, 3.2, 3.4
+, 0.016726572227312234, 0.044336969269249515, 0.06230922176482117, 0.2733652504290111, 0.3606353085893688, 0.4154854412512842, 0.4931667421477475, 0.5421469025922283, 0.5483236455497511, 0.569378907839295, 0.43122599932991407, 0.506119714597294, 0.4286583086215133, 0.6048992306603105, 0.45424657023908194, 0.6333345687706163, 0.5822196798974196, 0.8918964292632531, 0.9422988109598484, 0.8579332348997845
+10000000, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0, 2.4, 3.0, 3.4
+, 0.026043125175021, 0.28627129078596353, 0.2006741486957521, 0.3602109329490757, 0.5126121076233183, 0.7521644636049453, 0.7345747319418476, 0.4402735868034601, 0.7231112172597723, 0.4617557163307154, 0.4887361728712654, 0.6417756720568545, 0.36683051156016755, 0.3773720014321518, 0.6682563906905762
+F
+200, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.8
+, 0.0118796572796037, 0.0014672582394740575, 0.03743998420606895, 0.027945978849726678, 0.042221213889387516, 0.010801552826949386, 0.04139522496607944, 0.03999191502135917, 0.05211549995458061, 0.060297109638310954, 0.10129384648254204, 0.15616351789120644, 0.21318276089539578, 0.25042509461225854, 0.29299384438800996, 0.30431199722184266, 0.31474465593677153, 0.3215608071222106, 0.34933410952686794, 0.4013372334166951, 0.4132461627252968, 0.4735993600214136, 0.5137798275706204, 0.5240892505076316, 0.5732127691892553, 0.5728471386016757, 0.587424087598907, 0.6378341853871199, 0.6652131586082336, 0.6434091755890186, 0.6811145001725185, 0.6778946461924046, 0.6531231489646425, 0.622114029030299, 0.7027944481835392, 0.6544984744633467, 0.6768972298187619, 0.7649999212127353, 0.17747727606580532, 0.03200371938741867
+400, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.004521502078034196, 0.011294546197758165, 0.01902181455153136, 0.028453600912992427, 0.045494587204780265, 0.075636622645322, 0.1346817828927533, 0.1858370410511906, 0.21145461364683354, 0.22993300300255923, 0.24758097664369763, 0.2503946720700768, 0.2893435384653396, 0.33294847977837494, 0.3801325469321394, 0.4218741104610451, 0.4797481604447298, 0.511808478587414, 0.5974117622187544, 0.5834704269428962, 0.6578279478883984, 0.6933440994726486, 0.7200687227206225, 0.6742670559283108, 0.7199198750632219, 0.7264408323450363, 0.7378516949019436, 0.8038271425512616, 0.8296853580700081, 0.8243729580366167, 0.779678703055898, 0.7171620329790568
+600, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.04225844325367724, 0.02996073852825442, 0.0033279267524589365, 0.010538147174194904, 0.0055842967220071866, 0.023707193295361365, 0.03287914981492101, 0.055581517092584964, 0.12205147623150721, 0.18674255469804368, 0.21228362626337297, 0.19176664697425205, 0.192771021747899, 0.2738504396168108, 0.2706484536269102, 0.3113119235107976, 0.3768631860175106, 0.4286865380951811, 0.4674373782473765, 0.4470173830795746, 0.5654578562715549, 0.6089083108990303, 0.6950018580809499, 0.754745539388691, 0.6729932052759504, 0.6466101067600745, 0.7630081242628347, 0.7653017355343918, 0.7660715915354379, 0.9006582480689912, 0.8613792472553212, 0.9183691145965688
+800, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.0025284892240452768, 0.0013041538615920674, 0.02322344999575436, 0.06274762646486995, 0.14440845571763275, 0.3574106974338475, 0.26027269066425, 0.2629704658055164, 0.24533138237707777, 0.26347140721903634, 0.3022788001575987, 0.29989143373704513, 0.4442104934598845, 0.5365972904585126, 0.4221062978051718, 0.5554972868238451, 0.5400155429805876, 0.6527242876757253, 0.47262603008218756, 0.6000342893579687, 0.8297108914342853, 0.5432571765506535, 0.6164512733317686, 0.659729845987438, 0.878226507885687, 0.8293156239699597, 0.39935167691260115
+1000, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.06957581181214641, 0.11727576980179334, 0.19879724787180408, 0.15514564994465022, 0.15335563242078185, 0.2029411563067941, 0.33025361900905537, 0.4179908587644115, 0.4428200805838054, 0.5634100755177667, 0.5680939840623824, 0.41520905829955296, 0.5753449870929177, 0.48414588902396805, 0.5913147768147112, 0.4648190251829891, 0.6358734895010334, 0.8449506822406887, 0.5716931289884855, 0.6013880377634973, 0.7021165582108326, 0.7605031088714594, 0.8316368363163683
+10000000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.08665812891997504, 0.1893325561060915, 0.392884167636787, 0.7103372143883693, 0.1713556511117511, 0.12470072279692956, 0.2383403904279098, 0.4472355817003856, 0.3084458638141755, 0.3922429804435704, 0.5098120423323317, 0.4048967889635629, 0.4046453884579675, 0.45231916560612523, 0.578670933838637, 0.527791925571288, 0.9204796267282687, 0.9253331472075768, 0.8919738382028523, 0.8766760073779796, 0.8865785578260142
+Y
+200, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0
+, 0.009286499266584609, 0.04190994220311158, 0.005673267516690456, 0.011282526243736116, 0.041909942203111575, 0.05957785566189, 0.04749864096985896, 0.05803797548561103, 0.07284698771143527, 0.04867312515561958, 0.0837839451245465, 0.08136844795311347, 0.1196284938247924, 0.13488330074785607, 0.1859103414172193, 0.22494333273362108, 0.2845607850383266, 0.34148624354154694, 0.3537330507841197, 0.4303267700378927, 0.4596981968135125, 0.5043265158898462, 0.5722032966248287, 0.591132260747033, 0.5615672359144327, 0.6433527557582447, 0.6200821169061512, 0.6866199534656023, 0.6409420681507526, 0.6377669951160809, 0.6475917264147603, 0.5867402271820585, 0.6081464300418282, 0.5855598936277712, 0.703751153709795, 0.5335558858652032, 0.5718292411383575, 0.7868125143340565, 0.5943859446876695, 0.2531795538962151
+400, -3.0, -2.8, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8
+, 0.0024631904120060435, 0.014599337728464305, 0.0019080473895611095, 0.0034467235991527422, 0.007898872470056247, 0.02454940809416175, 0.02767901585092433, 0.060144475081240587, 0.10182673535143302, 0.1402223895256354, 0.2159272947456956, 0.2474628127234784, 0.30672796698809546, 0.3701837946270894, 0.4281630341544478, 0.4900105528169095, 0.49672558077846896, 0.5458230184672949, 0.5822237283945346, 0.6232035400297948, 0.6309651873689367, 0.6650089068472502, 0.6906883232556257, 0.719070120561808, 0.5861318306017409, 0.6939528630753063, 0.644733589945717, 0.7557826365261244, 0.7868872016335692, 0.8077101821427416, 0.785137848061695, 0.7007336361374807, 0.7907789335787568, 0.9314640234622172
+600, -3.2, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8
+, 0.0678339420637355, 0.03132907113746549, 0.19521471211245917, 0.0790468441438464, 0.04504147115901855, 0.03621970390869113, 0.03736078959794541, 0.0702121913978338, 0.07678045932576472, 0.10633802477667664, 0.11960413292539095, 0.19900688668929817, 0.19847870369671886, 0.35052340230366474, 0.4182620478260756, 0.49334638007139636, 0.4139336918612302, 0.4758803364739189, 0.48058197497143446, 0.5325837123233392, 0.6764738640938338, 0.5824113213410999, 0.6854925565305252, 0.716699831685794, 0.6913247110998257, 0.7364537742437196, 0.6973151529206393, 0.6640017089879244, 0.7535147537884177, 0.8736524217431264, 0.8883154079240142, 0.9086524895242979, 0.3364614429341759, 0.8892455486542443, 0.516276512630574
+800, -2.4, -2.2, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.12499249996999987, 0.017542677750980205, 0.004183814710350362, 0.04049577780196881, 0.01945394480279348, 0.05981555656515628, 0.1662287133765109, 0.2470726136520115, 0.2279836885745909, 0.3405906596146762, 0.39304167365159454, 0.5479494843905584, 0.5776797970134353, 0.4969350548999451, 0.554976031602476, 0.6111452872519451, 0.5933613225870421, 0.6503765957046587, 0.6459187382843162, 0.47697114639093807, 0.4439174984783494, 0.5898786001485177, 0.6521583574634947, 0.699411044781371, 0.8755290078823845, 0.6892194962693724, 0.5956513541689771, 0.4191972780617089, 0.6859665367471306
+1000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.01513420581891473, 0.04710716529773664, 0.08675346730823665, 0.2536586494224809, 0.34051794477036007, 0.4949352227554531, 0.3744979625153848, 0.3885763166955366, 0.47500620176957165, 0.6732234245582497, 0.5916145874535242, 0.6141509220860845, 0.7235237614799357, 0.8444622025111802, 0.5840380724187212, 0.37117416203862574, 0.6845523703789804, 0.7343493349470799, 0.6147306746541727, 0.836192015818726, 0.6897855770617488, 0.7497181598163127, 0.3071472030349666, 0.1919103374866947
+10000000, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0, 2.2, 2.6, 2.8, 3.0, 3.2
+, 0.0708645474792468, 0.17151110308719983, 0.2570225856067352, 0.11678972600342932, 0.14007308160779536, 0.5103673933918981, 0.7576957600718095, 0.7229533929002657, 0.7098140887820918, 0.6723469072042269, 0.6112883643448062, 0.6166996129966311, 0.893002867868218, 0.8895255061508461, 0.8001654763978401, 0.30475369142377656, 0.6623003900213408, 0.7044407947702315, 0.6821802063854838
+C
+200, -4.4, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8
+, 0.00552600427650797, 0.03485954094930549, 0.03838810402982251, 0.025715627803848534, 0.025488221431905482, 0.04343408489770449, 0.07142970187382498, 0.061574074307699386, 0.07314774143241341, 0.0645623004653657, 0.08295448119612607, 0.11583376897861364, 0.10923780472615427, 0.15872284777216678, 0.18960300191746962, 0.21632285501348175, 0.2689843145563964, 0.317374224275957, 0.3630447998094276, 0.47533324924738357, 0.47607691305922323, 0.5079450745840531, 0.5277576798975776, 0.5628057172760719, 0.575051710042101, 0.5593880195311266, 0.6755090738783593, 0.5934386213582137, 0.6382467809460037, 0.46977743056505733, 0.49332782901573397, 0.433919671579876, 0.4787946054736947, 0.5285187386448856, 0.5192055844835611, 0.5418864058012781, 0.4974690811553878, 0.6669903566100355, 0.5635741644172909, 0.5631278587090643, 0.4509635087812499, 0.41102196954637915, 0.5675101424562242, 0.3858209367335479, 0.7158058178212813, 0.8441831930348846, 0.917028268187617
+400, -2.8, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0, 5.2
+, 0.010395396838634843, 0.021361460299502484, 0.02165535066430582, 0.015159525108778021, 0.05264200337595036, 0.09707983819746174, 0.09731511297628698, 0.14129374581890802, 0.20022262624150677, 0.1899999623811763, 0.24177069784075372, 0.2794320762962471, 0.3349563341527679, 0.39941588667848116, 0.37542427668497114, 0.4413948575311084, 0.4994511731980783, 0.5229578236619977, 0.517824249985961, 0.5986188278934524, 0.55649711734287, 0.6546467413270598, 0.7295136272915, 0.6635498671850562, 0.6708303778437357, 0.6854662728780426, 0.7366769816589981, 0.6804239212899574, 0.8020315025334613, 0.648248800129263, 0.4883801068027809, 0.5457865193156772, 0.7435757599432814, 0.3888378116158756, 0.8142396988934587, 0.35653604110313064, 0.9515094757412567, 0.8683671258886828, 0.43063122020040623, 0.20134832264371375
+600, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4
+, 0.014640944089221617, 0.047190998499466535, 0.016131092675076904, 0.05393342938205346, 0.03794117361550069, 0.14801529752994014, 0.12649865136882416, 0.12425487801283724, 0.18258838145967776, 0.17401252442552514, 0.24820371537628083, 0.2723009495881132, 0.31076828090971514, 0.4228592493350921, 0.4833419854280797, 0.48225822549606906, 0.6096979060194508, 0.6813975348841623, 0.6390568469900091, 0.6423882998985359, 0.6933120557029364, 0.7437104177397691, 0.7344965946599342, 0.5339314985180899, 0.76693091389901, 0.7860009499083788, 0.9485711375084455, 0.7614620936289629, 0.7922335003939941, 0.8140234673530409, 0.9905994173641781, 0.7496322833936753, 0.5402660706005019, 0.18423883691036483
+800, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 4.0, 4.2, 4.4
+, 0.1676606183648437, 0.1676606183648437, 0.07743155394424302, 0.07659036520366225, 0.040008410782623315, 0.06504823840581023, 0.1572369829786188, 0.20637862095592702, 0.26511828078110244, 0.26880018429549696, 0.7715955945577099, 0.5608321221346089, 0.5723698356275932, 0.5774648391346796, 0.5310722977083755, 0.5139787601877006, 0.6388370807266754, 0.5159175246850328, 0.4037730013428791, 0.6462677917648109, 0.6914477449297176, 0.7064353719025876, 0.6496598105159759, 0.8061020949745134, 0.9114250135029706, 0.805807135371541, 0.7069698372603644, 0.5579972241756949, 0.9707483336999887, 0.2521582668435688, 0.6737025368927209
+1000, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.6
+, 0.020481263776634836, 0.03747584236618773, 0.07963219211712719, 0.42117945879058266, 0.40087635526094045, 0.4186096119797778, 0.7519767441860465, 0.6969740019889189, 0.6769121710047986, 0.5095117468832265, 0.49996377012356535, 0.42357137893493324, 0.3704784074335224, 0.5022112276689343, 0.5377495655874323, 0.9377135348226019, 0.518641826827345, 0.6148997981190606, 0.925832429274806, 0.9306706710610997, 0.5275370793684547
+10000000, -0.6, 0.2, 0.6, 1.0, 1.2, 1.4, 2.2, 2.6, 2.8, 3.6
+, 0.2450956648098813, 0.34003811136989204, 0.5689392891716222, 0.5240494732020156, 0.4197691734921817, 0.6071833648393195, 0.47977746870653687, 0.1474288840262582, 0.2158490566037736, 0.059728506787330306
+I
+200, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6
+, 0.010246568324761396, 0.023586419611002423, 0.07326453667116309, 0.02266434565723754, 0.19125014334998405, 0.049215674536650926, 0.08042765604892763, 0.11504040934022898, 0.09190473758714221, 0.07884776581058621, 0.13204286686172295, 0.17339014874279796, 0.14738531701866997, 0.1925666588813994, 0.2798288367649437, 0.30356530000018567, 0.37802490411090917, 0.3891635072532392, 0.4188735667360788, 0.4130451708923584, 0.4090566074203953, 0.4005398324769401, 0.43727658014390886, 0.4525658021391098, 0.49469453336259855, 0.5364548074114078, 0.5487359041804901, 0.5589759259605349, 0.5600275134828192, 0.5857561779201617, 0.5950991838931297, 0.6171171951409766, 0.6208158282379346, 0.5341821673007862, 0.5911918922048982, 0.47767207984960436, 0.6016175443003301, 0.453017422332317, 0.009829615646825916, 0.011933949374280328
+400, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8
+, 0.009424041651289006, 0.005248090853102813, 0.020104435812286, 0.008380660947748239, 0.011633543757944554, 0.031233862457621414, 0.03796795304800285, 0.05304879411403409, 0.09428391079887828, 0.1957080355293911, 0.26417738982917194, 0.33722314135736386, 0.3835329367560942, 0.38791379723365044, 0.3469048845284936, 0.33981871326454166, 0.3718334747259479, 0.3744119941355528, 0.4371801106903041, 0.4742278199477808, 0.5467307808120919, 0.5889710925569661, 0.6003132839102469, 0.6282891377294968, 0.6667994100593078, 0.7149791981213757, 0.7165443100481403, 0.7648169965214152, 0.7931588888124155, 0.849697960723633, 0.5985009755051228
+600, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6
+, 0.01177149197646163, 0.019466387676539337, 0.009179617714196748, 0.021002809412977955, 0.012304705104874294, 0.01742489800381316, 0.09673996824968178, 0.20818513532059035, 0.24902690941466604, 0.3201033911616928, 0.3968527717263692, 0.32677150095038016, 0.2956502311222198, 0.3521918640702484, 0.34323878987875367, 0.41238085067793995, 0.4528202043089073, 0.4946606714015018, 0.60916176112803, 0.586244459052103, 0.731691655254136, 0.7615547888406847, 0.7532320282223522, 0.8256747853564931, 0.8227885041950739, 0.9414977098583945, 0.8707420724568614
+800, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4
+, 0.004884508436313263, 0.008895670331354973, 0.04738253012569462, 0.15700893886749803, 0.2720568723512916, 0.29754465051552165, 0.3529009205231974, 0.33901402505796213, 0.33151291774566327, 0.3171419785124146, 0.39610993206239475, 0.42435592249318754, 0.4284240846602554, 0.48610410052424313, 0.5011209748404598, 0.5923040805122267, 0.6801122435468188, 0.6003811302269592, 0.6886824799977288, 0.6536244005652843, 0.7340922067541863, 0.9749464234875972
+1000, -2.0, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2
+, 0.04006366374589266, 0.016420176967562962, 0.10541928765426886, 0.1842196914154018, 0.33370157870353867, 0.4140144214086954, 0.37627524716443256, 0.376828275689411, 0.350294020866546, 0.29056694294813423, 0.428977185963412, 0.4437891708347922, 0.4040640317539013, 0.5436759732095426, 0.6396942795183248, 0.699574241911313, 0.6264500766775059, 0.6958787809288652, 0.6647049856983595, 0.9007007532728866
+10000000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0
+, 0.05928374933750602, 0.2840734480639214, 0.355188042763727, 0.4981825035676783, 0.522066931657905, 0.687219937903835, 0.4285758086769892, 0.26404535582320265, 0.4070341937448583, 0.31549829407614327, 0.3522842812258061, 0.41463591162408975, 0.44042322850815757, 0.8108811576260953, 0.6660499661785326, 0.6714949448238751, 0.6945054584794939, 0.9471616054797473
+P
+200, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2
+, 0.09462093250004656, 0.06513499980673217, 0.25836149525869284, 0.003590843571806108, 0.08011419031573228, 0.03750805525576047, 0.0574609648224664, 0.021046661598639223, 0.03965651331683649, 0.03233547498093971, 0.05244949461037999, 0.059239211279768764, 0.08658427873468452, 0.11641077018764909, 0.17102302987730567, 0.21557776345358962, 0.3488992728757439, 0.38412126513874806, 0.4717518589355339, 0.48086303211958253, 0.5349972443434652, 0.5523284345890979, 0.5139727330404497, 0.5737275580736053, 0.646507295963126, 0.6177418767988746, 0.5913879649161384, 0.6113435859450091, 0.6248086488637173, 0.6177495838258914, 0.5915088103951179, 0.5390155146848349, 0.5544234138321293, 0.4961989976316854, 0.5910782276103086, 0.575955804525867, 0.8040228012576552, 0.2552364547730815, 0.1539699424587969, 0.6105382318958665, 0.6920195597291678
+400, -3.2, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 4.0, 4.2
+, 0.027678544003005952, 0.0017759936101448578, 0.01563976484987035, 0.02662887534696838, 0.010267917113890223, 0.014499665143659572, 0.027299347695871414, 0.045885094521211214, 0.04395609633506502, 0.07075355873801675, 0.11122160543559785, 0.19482765066850968, 0.3102447121351171, 0.4323382675846813, 0.5049196275808877, 0.5340408301827624, 0.5699327744168944, 0.5646308086676382, 0.5898440411347238, 0.5960532705788892, 0.6456568185067169, 0.6761301863788893, 0.6705221804706994, 0.6794427710153623, 0.6560926262495437, 0.6670270517403344, 0.6415870485838746, 0.4940385904199614, 0.5459239564924565, 0.47599332002445893, 0.409220079384302, 0.42847584927258375, 0.827666355527503, 0.6427397436884055, 0.3387975859228055, 0.6658481930242676
+600, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4
+, 0.054115861337130235, 0.046296087292164195, 0.056336173545012996, 0.005138426504631574, 0.03016715284976033, 0.02195661225379361, 0.035469037639700524, 0.043213639799410224, 0.07923069137404684, 0.1892981614814477, 0.24008662810215636, 0.4542352013348483, 0.5155927001365727, 0.6227024593529396, 0.6069408758206023, 0.5689367573494439, 0.6173067228915726, 0.6507688014522729, 0.5604747283213789, 0.7005841663886421, 0.6021972001890809, 0.7322327525953377, 0.6256764860648757, 0.6644107146326537, 0.6404289926815111, 0.5798792570169877, 0.48913332794575626, 0.4739894981973522, 0.6169184160982089, 0.6311019946564094, 0.9162938262863316
+800, -2.4, -2.2, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2
+, 0.00625109691006886, 0.0036866050811829405, 0.010821473610371099, 0.034581709047270474, 0.052505947010089406, 0.04046046640914797, 0.0715445762534076, 0.19387348265185214, 0.3024834157149413, 0.4407693068754491, 0.5501948550333603, 0.4344640358714685, 0.4429962655179792, 0.4559017802592965, 0.4550889515888062, 0.562065860749629, 0.5872819382503642, 0.5031074411074145, 0.549570630011202, 0.7331689607663807, 0.637804892388616, 0.7508243979930967, 0.6847389871555631, 0.6837776185382219, 0.7089342805159007, 0.7548686754100637, 0.6858453269953747, 0.737874223470343
+1000, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.09319850573232, 0.10979172199248835, 0.06948530038214824, 0.07586792624252905, 0.1989606990626378, 0.3974041167469781, 0.4836987107065937, 0.43529270200348963, 0.5872083609038189, 0.4748060743676909, 0.4782649923500425, 0.47660581529267176, 0.40493349371783804, 0.4729478286637323, 0.5031491548381056, 0.4918696117603916, 0.474799166651737, 0.7606885434541573, 0.6515931714096899, 0.8523660209056383, 0.6549542201339508, 0.49738075923608915, 0.37358200278550074, 0.9102043744992286, 0.30461699146099175
+10000000, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.2, 0.4, 0.6, 0.8, 1.0, 1.4, 1.6, 1.8, 2.0, 2.4, 2.8, 3.0
+, 0.002034225211333397, 0.03213966868366234, 0.1254929577464789, 0.22886329058900245, 0.5338868536424239, 0.30411880078453357, 0.7558683600047184, 0.8943652386947497, 0.5068308181096108, 0.8747794169548951, 0.808677163898561, 0.6541717723070068, 0.520123659549802, 0.8308427892277724, 0.75944364805703, 0.46422176679969807, 0.11903193440828615, 0.9246754822273444
+V
+200, -4.2, -4.0, -3.8, -3.6, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0
+, 0.3013177924972648, 0.0670576962538446, 0.027947536968587417, 0.025471567022443797, 0.17738326284128084, 0.10698026054520915, 0.027192326456929903, 0.04736489830728461, 0.15565145058718088, 0.08889759595104872, 0.0901815261535527, 0.12890229680120277, 0.17640442149470179, 0.20057478536198106, 0.25497736014857153, 0.2845496101729879, 0.3387008081814645, 0.36859541367677057, 0.408859593377653, 0.42852982770728926, 0.43491545736005727, 0.4437173153173867, 0.4307511980475097, 0.4688229717283917, 0.5041690529385188, 0.5206150940885195, 0.5504178549950146, 0.604449454781223, 0.5852695922917379, 0.5892740759521515, 0.5821242214118382, 0.611738803380447, 0.5748701424067844, 0.5470664583475426, 0.4366875437443472, 0.2990467205099567, 0.17194313586429896
+400, -3.4, -3.2, -3.0, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 3.0
+, 0.007630710431630884, 0.05643138079149608, 0.0019464106682222882, 0.00495914554400256, 0.008471384625919275, 0.022434103491537804, 0.040253776825090605, 0.025356097618350212, 0.048630164072312, 0.06994433785325964, 0.10054807768512329, 0.17714681726250375, 0.262931751831555, 0.34550802640469575, 0.3781959689270546, 0.43546932456834325, 0.4111227842326892, 0.39342474726021504, 0.40754351242412284, 0.460087545031051, 0.4784670790288331, 0.5272787211953236, 0.5658096821582352, 0.6095502530391147, 0.6572364343932448, 0.6458428060835956, 0.699498928622632, 0.7675563268175648, 0.7165153170099512, 0.5393942384511748, 0.5587490608280018, 0.7684784086894817
+600, -2.8, -2.6, -2.4, -2.2, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4
+, 0.061032997065434115, 0.18533639985747144, 0.0145577041202563, 0.010942786101539745, 0.07387934237880509, 0.037536176376309634, 0.02763024311231824, 0.02265328277135838, 0.09520478453610436, 0.1595129075059143, 0.22597260291075094, 0.37063686634929416, 0.4028000965396746, 0.40189585160555535, 0.32174279329124866, 0.37195239626061044, 0.4539851836669101, 0.4666124404448917, 0.5233672984387059, 0.5443786020355682, 0.670588238862655, 0.6746727393780464, 0.774273657653073, 0.8059482440811514, 0.7534118409082837, 0.933769318652744, 0.9199396810574295
+800, -2.0, -1.8, -1.6, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4
+, 0.008536139445826405, 0.010647452272946844, 0.003065442904427912, 0.004083085051204419, 0.017935306813610788, 0.09716361787367706, 0.19725368574759128, 0.3505081624049802, 0.4204715759928817, 0.45469008403510625, 0.388999422485893, 0.32305500994632613, 0.39140601027418337, 0.48052658276599364, 0.421967881355336, 0.5799579944888391, 0.619119965966238, 0.619504925624972, 0.6769064254531707, 0.7778709874558832, 0.771651999854801, 0.7479361219585633, 0.5246872402663989
+1000, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2
+, 0.03892955516942246, 0.12043398243351962, 0.2645304545541721, 0.4365200127998329, 0.437088025611357, 0.35702064605615885, 0.4010590154472944, 0.36781079834103564, 0.4587836912312046, 0.39663279275577046, 0.6365005914806564, 0.5655098255829267, 0.593426197811947, 0.6692760067022221, 0.8622693278223753, 0.7184982472426565, 0.7060497525545142
+10000000, -1.4, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0
+, 0.017728944142882157, 0.007167824432547233, 0.08493880095308559, 0.2528489732042521, 0.4813898894916351, 0.36070591557949294, 0.39824076805492237, 0.3119042195275812, 0.32650985724376896, 0.49659876345439163, 0.5211131565504622, 0.4260184611915101, 0.5637321616051639, 0.5428210040346628, 0.7687209074612477, 0.7000895494459963, 0.8891690518938169
index 3843ddb..f8c2b68 100644 (file)
@@ -11,6 +11,7 @@ action.paste = Paste
 action.show_html_source = Show HTML Source
 action.print = Print...
 action.web_service = Web Service
+action.hmmer = HMMER
 action.cancel_job = Cancel Job
 action.start_job = Start Job
 action.revert = Revert
@@ -59,6 +60,8 @@ action.boxes = Boxes
 action.text = Text
 action.by_pairwise_id = By Pairwise Identity
 action.by_id = By Id
+action.by_evalue = By E-Value
+action.by_bit_score = By Bit Score
 action.by_length = By Length
 action.by_group = By Group
 action.unmark_as_reference = Unmark as Reference 
@@ -98,6 +101,7 @@ action.edit_group = Edit Group
 action.border_colour = Border colour
 action.edit_new_group = Edit New Group
 action.hide_sequences = Hide Sequences
+action.add_background_frequencies = Add Background Frequencies
 action.sequences = Sequences
 action.ids = IDS
 action.ids_sequences = IDS and sequences
@@ -132,6 +136,8 @@ action.select_highlighted_columns = Select Highlighted Columns
 tooltip.select_highlighted_columns = Press B to mark highlighted columns, Ctrl-(or Cmd)-B to toggle, and Alt-B to mark all but highlighted columns 
 action.deselect_all = Deselect all
 action.invert_selection = Invert selection
+action.filter_by_evalue = Filter by E-Value
+action.filter_by_score = Filter by Score
 action.using_jmol = Using Jmol
 action.undo_changes_to_feature_settings = Undo all unapplied changes to feature settings
 action.undo_changes_to_feature_settings_and_close_the_dialog = Undo all pending changes and close the feature settings dialog
@@ -201,6 +207,9 @@ label.colourScheme_turnpropensity = Turn Propensity
 label.colourScheme_buriedindex = Buried Index
 label.colourScheme_purine/pyrimidine = Purine/Pyrimidine
 label.colourScheme_nucleotide = Nucleotide
+label.colourScheme_hmmer-uniprot = HMMER profile v global background
+label.colourScheme_hmmer-alignment = HMMER profile v alignment background
+label.colourScheme_hmm_match_score = HMM Match Score
 label.colourScheme_t-coffeescores = T-Coffee Scores
 label.colourScheme_rnahelices = By RNA Helices
 label.colourScheme_sequenceid = Sequence ID Colour
@@ -882,6 +891,7 @@ label.save_text_to_file = Save Text to File
 label.save_state = Save State
 label.restore_state = Restore State
 label.saving_jalview_project = Saving jalview project {0}
+label.loading_jalview_project = Loading jalview project {0}
 label.load_feature_colours = Load Feature Colours
 label.save_feature_colours = Save Feature Colour Scheme
 label.select_startup_file = Select startup file
@@ -969,6 +979,7 @@ 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
@@ -1048,6 +1059,7 @@ exception.ranml_couldnt_process_data = Couldn''t process data as RNAML file ({0}
 exception.ranml_invalid_file = Invalid RNAML file ({0})
 exception.ranml_problem_parsing_data = Problem parsing data as RNAML ({0})
 exception.pfam_no_sequences_found = No sequences found (PFAM input)
+exception.hmmer_no_valid_sequences_found = No valid sequences found
 exception.stockholm_invalid_format = This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM'
 exception.couldnt_parse_sequence_line = Could not parse sequence line: {0}
 exception.unknown_annotation_detected = Unknown annotation detected: {0} {1}
@@ -1064,6 +1076,7 @@ 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
@@ -1106,6 +1119,8 @@ status.export_complete = {0} Export completed
 status.fetching_pdb = Fetching PDB {0}
 status.refreshing_news = Refreshing news
 status.opening_params = Opening {0}
+status.waiting_sequence_database_fetchers_init = Waiting for Sequence Database Fetchers to initialise
+status.init_sequence_database_fetchers = Initialising Sequence Database Fetchers
 status.fetching_sequence_queries_from = Fetching {0} sequence queries from {1}
 status.finshed_querying = Finished querying
 status.parsing_results = Parsing results.
@@ -1118,14 +1133,21 @@ 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.running_hmmbuild = Building Hidden Markov Model
+status.running_hmmalign = Creating alignment with Hidden Markov Model
+status.running_search = Searching for matching sequences
 status.colouring_structures = Colouring structures
 label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
 label.font_too_small = Font size is too small
+label.error_loading_file_params = Error loading file {0}
+label.error_loading_jalview_file = Error loading Jalview file
 warn.out_of_memory_when_action = Out of memory when {0}\!\!\nSee help files for increasing Java Virtual Machine memory.
 warn.out_of_memory_loading_file = Out of memory loading file {0}\!\!\nSee help files for increasing Java Virtual Machine memory.
 label.out_of_memory = Out of memory
 label.invalid_id_column_width = Invalid ID Column width
 warn.user_defined_width_requirements = The user defined width for the\nannotation and sequence ID columns\nin exported figures must be\nat least 12 pixels wide.
+label.couldnt_create_sequence_fetcher = Couldn't create SequenceFetcher
+warn.couldnt_create_sequence_fetcher_client = Could not create the sequence fetcher client. Check error logs for details.
 warn.server_didnt_pass_validation = Service did not pass validation.\nCheck the Jalview Console for more details.
 warn.url_must_contain = Sequence URL must contain $SEQUENCE_ID$, $DB_ACCESSION$, or a regex
 warn.urls_not_contacted = URLs that could not be contacted
@@ -1309,6 +1331,7 @@ option.enable_disable_autosearch = When ticked, search is performed automaticall
 option.autosearch = Autosearch
 label.retrieve_ids = Retrieve IDs
 label.display_settings_for = Display settings for {0} features
+label.simple = Simple
 label.simple_colour = Simple Colour
 label.colour_by_text = Colour by text
 label.graduated_colour = Graduated Colour
@@ -1331,6 +1354,79 @@ label.alignment = alignment
 label.pca = PCA
 label.create_image_of = Create {0} image of {1}
 label.click_to_edit = Click to edit, right-click for menu
+label.hmmalign = hmmalign
+label.use_hmm = HMM profile to use
+label.use_sequence = Sequence to use
+label.hmmbuild = hmmbuild
+label.hmmsearch = hmmsearch
+label.jackhmmer = jackhmmer
+label.installation = Installation
+label.hmmer_location = HMMER Binaries Installation Location
+label.cygwin_location = Cygwin Binaries Installation Location (Windows)
+label.information_annotation = Information Annotation
+label.ignore_below_background_frequency = Ignore Below Background Frequency
+label.information_description = Information content, measured in bits
+warn.no_hmm = No Hidden Markov model found.\nRun hmmbuild or load an HMM file first.
+label.no_sequences_found = No matching sequences, or an error occurred.
+label.hmmer = HMMER
+label.trim_termini = Trim Non-Matching Termini
+label.trim_termini_desc = If true, non-matching regions on either end of the resulting alignment are removed.
+label.no_of_sequences = Number of sequences returned
+label.reporting_cutoff = Reporting Cut-off
+label.inclusion_threshold = Inlcusion Threshold
+label.freq_alignment = Use alignment background frequencies
+label.freq_uniprot = Use Uniprot background frequencies
+label.hmmalign_options = hmmalign options
+label.hmmsearch_options = hmmsearch options
+label.jackhmmer_options = jackhmmer options
+label.executable_not_found = The ''{0}'' executable file was not found
+warn.command_failed = {0} failed
+label.invalid_folder = Invalid Folder
+label.number_of_results = Number of Results to Return
+label.number_of_iterations = Number of jackhmmer Iterations
+label.auto_align_seqs = Automatically Align Fetched Sequences
+label.new_returned = new sequences returned
+label.use_accessions = Return Accessions
+label.check_for_new_sequences = Return Number of New Sequences
+label.evalue = E-Value
+label.reporting_seq_evalue = Reporting Sequence E-value Cut-off
+label.reporting_seq_score = Reporting Sequence Score Threshold
+label.reporting_dom_evalue = Reporting Domain E-value Cut-off
+label.reporting_dom_score = Reporting Domain Score Threshold
+label.inclusion_seq_evalue = Inclusion Sequence E-value Cut-off
+label.inclusion_seq_score = Inclusion Sequence Score Threshold
+label.inclusion_dom_evalue = Inclusion Domain E-value Cut-off
+label.inclusion_dom_score = Inclusion Domain Score Threshold
+label.number_of_results_desc = The maximum number of hmmsearch results to display
+label.number_of_iterations_desc = The number of iterations jackhmmer will complete when searching for new sequences
+label.auto_align_seqs_desc = If true, all fetched sequences will be aligned to the hidden Markov model with which the search was performed
+label.check_for_new_sequences_desc = Display number of new sequences returned from hmmsearch compared to the previous alignment 
+label.use_accessions_desc = If true, the accession number of each sequence is returned, rather than that sequence's name
+label.reporting_seq_e_value_desc = The E-value cutoff for returned sequences 
+label.reporting_seq_score_desc = The score threshold for returned sequences 
+label.reporting_dom_e_value_desc = The E-value cutoff for returned domains 
+label.reporting_dom_score_desc = The score threshold for returned domains 
+label.inclusion_seq_e_value_desc = Sequences with an E-value less than this cut-off are classed as significant
+label.inclusion_seq_score_desc = Sequences with a bit score greater than this threshold are classed as significant
+label.inclusion_dom_e_value_desc = Domains with an E-value less than this cut-off are classed as significant
+label.inclusion_dom_score_desc = Domains with a bit score greater than this threshold are classed as significant
+label.add_database = Add Database
+label.this_alignment = This alignment
+warn.invalid_format = This is not a valid database file format. The current supported formats are Fasta, Stockholm and Pfam.
+label.database_for_hmmsearch = The database hmmsearch will search through
+label.use_reference = Use Reference Annotation
+label.use_reference_desc = If true, hmmbuild will keep all columns defined as a reference position by the reference annotation
+label.hmm_name = Alignment HMM Name
+label.hmm_name_desc = The name given to the HMM for the alignment
+warn.no_reference_annotation = No reference annotation found
+label.hmmbuild_for = Build HMM for
+label.hmmbuild_for_desc = Build an HMM for the selected sets of sequences
+label.alignment = Alignment
+label.groups_and_alignment = All groups and alignment
+label.groups = All groups
+label.selected_group = Selected group
+label.use_info_for_height = Use Information Content as Letter Height
+action.search = Search
 label.backupfiles_confirm_delete = Confirm delete
 label.backupfiles_confirm_delete_old_files = Delete the following older backup files? (see the Backups tab in Preferences for more options)
 label.backupfiles_confirm_save_file = Confirm save file
@@ -1347,6 +1443,7 @@ label.backup_filename_strategy = Backup filename strategy
 label.append_to_filename = Append to filename (%n is replaced by the backup number)
 label.append_to_filename_tooltip = %n in the text will be replaced by the backup number. The text will appear after the filename. See the summary box above.
 label.index_digits = Number of digits to use for the backup number (%n)
+label.summary_of_backups_scheme = Summary of backup scheme
 label.scheme_examples = Scheme examples
 label.increment_index = Increase appended text numbers - newest file has largest number.
 label.reverse_roll = "Roll" appended text numbers - newest backup file is always number 1.
@@ -1375,11 +1472,13 @@ label.single_file_description = Keep the last version of the file
 label.keep_all_versions_description = Keep all previous versions of the file
 label.rolled_backups_description = Keep the last nine versions of the file from _bak.1 (newest) to _bak.9 (oldest)
 label.cancel_changes_description = Cancel changes made to your last saved Custom scheme
+label.previously_saved_scheme = Previously saved scheme
 label.no_backup_files = NO BACKUP FILES
 label.include_backup_files = Include backup files
 label.cancel_changes = Cancel changes
 label.warning_confirm_change_reverse = Warning!\nIf you change the increment/decrement of the backup filename number, without changing the suffix or number of digits,\nthis may cause loss of backup files created with the previous backup filename scheme.\nAre you sure you wish to do this?
 label.change_increment_decrement = Change increment/decrement?
+label.was_previous = was {0}
 label.newerdelete_replacement_line = Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted and replaced by apparently older file\n''{1}''\t(modified {3}, size {5}).
 label.confirm_deletion_or_rename = Confirm deletion of ''{0}'' or rename to ''{1}''?
 label.newerdelete_line = Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted but is newer than the oldest remaining backup file\n''{1}''\t(modified {3}, size {5}).
index d0bfd65..c4f55a8 100644 (file)
@@ -215,7 +215,7 @@ label.show_non_conserved = Mostrar no conservadas
 label.overview_window = Ventana resumen
 label.none = Ninguno
 label.above_identity_threshold = Por encima del umbral de identidad
-label.show_sequence_features = Mostrar las características de secuencia
+label.show_sequence_features = Mostrar las características de las secuencias
 label.nucleotide = Nucleótido
 label.to_new_alignment = A nuevo alineamiento
 label.to_this_alignment = Añadir a este alineamiento
@@ -379,6 +379,8 @@ label.vamsas_document_import_failed =  Fallo en la importaci
 label.couldnt_locate = No se pudo localizar {0}
 label.url_not_found = URL no encontrada
 label.new_sequence_url_link = Enlace a una nueva secuencia URL
+label.cannot_edit_annotations_in_wrapped_view = No se pueden editar anotaciones en vista envolvente
+label.wrapped_view_no_edit = Vista envolvente - no editar
 label.error_retrieving_data = Error en la recuperación de datos
 label.user_colour_scheme_must_have_name = El esquema de colores del usuario debe tener un nombre
 label.no_name_colour_scheme = No hay nombre para el esquema de colores 
@@ -651,6 +653,7 @@ label.associate_nodes_with = Asociar nodos con
 label.link_name = Nombre del enalce
 label.pdb_file = Fichero PDB
 label.colour_with_jmol = Colorear con Jmol
+label.jmol = Jmol
 label.sort_alignment_by_tree = Ordenar alineamiento por Ã¡rbol
 label.mark_unlinked_leaves = Marcar las hojas como no enlazadas
 label.associate_leaves_with = Asociar hojas con
@@ -800,6 +803,7 @@ label.save_text_to_file = Guardar Texto en un fichero
 label.save_state = Guardar estado
 label.restore_state = Restaurar estado
 label.saving_jalview_project = Guardando el proyecto de Jalview {0}
+label.loading_jalview_project = Cargando el proyecto de Jalview {0}
 label.load_feature_colours = Cargar colores de características
 label.save_feature_colours = Guardar esquema cromático de características
 label.select_startup_file = Seleccionar fichero de arranque
@@ -871,6 +875,7 @@ error.implementation_error_cannot_find_marshaller_for_param_set =Error de implem
 error.implementation_error_old_jalview_object_not_bound =Error de implementación: Â¡el objeto Jalview antiguo no está enlazado! ({0})
 error.implementation_error_vamsas_doc_class_should_bind_to_type = Error de implementación: la clase de documento VAMSAS {0} debe enlazar a {1} (pero se ha encontrado que lo está a {2})
 error.invalid_vamsas_rangetype_cannot_resolve_lists = RangeType VAMSAS no válido - Â¡no es posible resolver ambas listas de Pos y Seg con los valores elegidos!
+error.implementation_error_maplist_is_null = Error de implementación. MapList es nulo en initMapType.
 error.implementation_error_cannot_have_null_alignment = Error de implementación: no es posible tener una clave nula en el alineamiento
 error.implementation_error_null_fileparse = Error de implementación. FileParse nulo en el construictor de copia
 error.implementation_error_structure_selection_manager_null = Error de implementación. El contexto structure selection manager's es nulo
@@ -887,6 +892,7 @@ 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 
@@ -1030,6 +1036,8 @@ status.collecting_job_results = Recolectando los resultados de los trabajos.
 status.fetching_db_refs = Recuperando db refs
 label.font_doesnt_have_letters_defined = La fuente no tiene letras definidas\npor lo que no puede emplease\ncon datos de alineamientos
 label.font_too_small = Tamaño de la letra es demasiado pequeña
+label.error_loading_file_params = Error cargando el fichero {0}
+label.error_loading_jalview_file = Error cargando el fichero Jalview 
 warn.out_of_memory_when_action = Sin memoria al {0}\!\!\nConsulte los ficheros de ayuda para ajustar la memoria de la m\u00E1quina virtual de Java.
 warn.out_of_memory_loading_file = Sin memoria al cargar el fichero {0}\!\!\nConsulte los ficheros de ayuda para ajustar la memoria de la m\u00E1quina virtual de Java.
 label.out_of_memory = Sin memoria
@@ -1205,6 +1213,7 @@ exception.fts_server_unreachable=Jalview no puede conectar con el servidor {0}.
 exception.outofmemory_loading_mmcif_file=Sin memoria al cargar el fichero mmCIF
 label.hide_columns_not_containing=Ocultar las columnas que no contengan
 label.pdb_sequence_fetcher=Recuperador de secuencias PDB
+status.waiting_for_user_to_select_output_file=Esperando que el usuario seleccione el fichero {0}
 action.prev_page=<< 
 status.cancelled_image_export_operation=Operación de exportación {0} cancelada
 label.couldnt_run_groovy_script=No se ha podido ejecutar el script Groovy
@@ -1299,6 +1308,7 @@ option.enable_disable_autosearch = Marcar para buscar autom
 option.autosearch = Auto búsqueda
 label.retrieve_ids = Recuperar IDs
 label.display_settings_for = Visualización de características {0}
+label.simple = Simple
 label.simple_colour = Color simple
 label.colour_by_text = Colorear por texto
 label.graduated_colour = Color graduado
index 1d2235e..c21721b 100755 (executable)
                                                                        </xs:attribute>
                                                                </xs:complexType>
                                                        </xs:element>
+                                                       <xs:element name="hmmerProfile" minOccurs="0" type="xs:string">
+                                                               <xs:annotation>
+                                                                       <xs:documentation>name of the project jar entry that holds the HMM file with the profile for the sequence</xs:documentation>
+                                                               </xs:annotation>
+                                                       </xs:element>
                                                </xs:sequence>
                                                <xs:attribute name="colour" type="xs:int" use="optional" />
                                                <xs:attribute name="start" type="xs:int" use="required" />
index b1505d6..892b1b1 100755 (executable)
@@ -24,6 +24,7 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.Profile;
 import jalview.datamodel.ProfileI;
 import jalview.datamodel.Profiles;
@@ -32,6 +33,7 @@ import jalview.datamodel.ResidueCount;
 import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.datamodel.SequenceI;
 import jalview.ext.android.SparseIntArray;
+import jalview.schemes.ResidueProperties;
 import jalview.util.Comparison;
 import jalview.util.Format;
 import jalview.util.MappingUtils;
@@ -48,25 +50,12 @@ import java.util.List;
  * This class is used extensively in calculating alignment colourschemes that
  * depend on the amount of conservation in each alignment column.
  * 
- * @author $author$
- * @version $Revision$
  */
 public class AAFrequency
 {
-  public static final String PROFILE = "P";
-
-  /*
-   * Quick look-up of String value of char 'A' to 'Z'
-   */
-  private static final String[] CHARS = new String['Z' - 'A' + 1];
+  private static final double LOG2 = Math.log(2);
 
-  static
-  {
-    for (char c = 'A'; c <= 'Z'; c++)
-    {
-      CHARS[c - 'A'] = String.valueOf(c);
-    }
-  }
+  public static final String PROFILE = "P";
 
   public static final ProfilesI calculate(List<SequenceI> list, int start,
           int end)
@@ -192,6 +181,52 @@ public class AAFrequency
   }
 
   /**
+   * Returns the full set of profiles for a hidden Markov model. The underlying
+   * data is the raw probabilities of a residue being emitted at each node,
+   * however the profiles returned by this function contain the percentage
+   * chance of a residue emission.
+   * 
+   * @param hmm
+   * @param width
+   *          The width of the Profile array (Profiles) to be returned.
+   * @param start
+   *          The alignment column on which the first profile is based.
+   * @param end
+   *          The alignment column on which the last profile is based.
+   * @param removeBelowBackground
+   *          if true, symbols with a match emission probability less than
+   *          background frequency are ignored
+   * @return
+   */
+  public static ProfilesI calculateHMMProfiles(final HiddenMarkovModel hmm,
+          int width, int start, int end, boolean removeBelowBackground,
+          boolean infoLetterHeight)
+  {
+    ProfileI[] result = new ProfileI[width];
+    char[] symbols = hmm.getSymbols().toCharArray();
+    int symbolCount = symbols.length;
+    for (int column = start; column < end; column++)
+    {
+      ResidueCount counts = new ResidueCount();
+      for (char symbol : symbols)
+      {
+        int value = getAnalogueCount(hmm, column, symbol,
+                removeBelowBackground, infoLetterHeight);
+        counts.put(symbol, value);
+      }
+      int maxCount = counts.getModalCount();
+      String maxResidue = counts.getResiduesForCount(maxCount);
+      int gapCount = counts.getGapCount();
+      ProfileI profile = new Profile(symbolCount, gapCount, maxCount,
+              maxResidue);
+      profile.setCounts(counts);
+
+      result[column] = profile;
+    }
+    return new Profiles(result);
+  }
+
+  /**
    * Make an estimate of the profile size we are going to compute i.e. how many
    * different characters may be present in it. Overestimating has a cost of
    * using more memory than necessary. Underestimating has a cost of needing to
@@ -289,9 +324,80 @@ public class AAFrequency
   }
 
   /**
-   * Derive the gap count annotation row.
+   * Derive the information annotations to be added to the alignment for
+   * display. This does not recompute the raw data, but may be called on a
+   * change in display options, such as 'ignore below background frequency',
+   * which may in turn result in a change in the derived values.
+   * 
+   * @param information
+   *          the annotation row to add annotations to
+   * @param profiles
+   *          the source information data
+   * @param startCol
+   *          start column (inclusive)
+   * @param endCol
+   *          end column (exclusive)
+   * @param ignoreGaps
+   *          if true, normalise residue percentages
+   * @param showSequenceLogo
+   *          if true include all information symbols, else just show modal
+   *          residue
+   */
+  public static float completeInformation(AlignmentAnnotation information,
+          ProfilesI profiles, int startCol, int endCol)
+  {
+    // long now = System.currentTimeMillis();
+    if (information == null || information.annotations == null)
+    {
+      /*
+       * called with a bad alignment annotation row 
+       * wait for it to be initialised properly
+       */
+      return 0;
+    }
+
+    float max = 0f;
+    SequenceI hmmSeq = information.sequenceRef;
+
+    int seqLength = hmmSeq.getLength();
+    if (information.annotations.length < seqLength)
+    {
+      return 0;
+    }
+
+    HiddenMarkovModel hmm = hmmSeq.getHMM();
+
+    for (int column = startCol; column < endCol; column++)
+    {
+      if (column >= seqLength)
+      {
+        // hmm consensus sequence is shorter than the alignment
+        break;
+      }
+      
+      float value = hmm.getInformationContent(column);
+      boolean isNaN = Float.isNaN(value);
+      if (!isNaN)
+      {
+        max = Math.max(max, value);
+      }
+
+      String description = isNaN ? null
+              : String.format("%.4f bits", value);
+      information.annotations[column] = new Annotation(
+              Character.toString(
+                      Character.toUpperCase(hmmSeq.getCharAt(column))),
+              description, ' ', value);
+    }
+
+    information.graphMax = max;
+    return max;
+  }
+
+  /**
+   * Derive the occupancy count annotation
    * 
-   * @param gaprow
+   * @param occupancy
    *          the annotation row to add annotations to
    * @param profiles
    *          the source consensus data
@@ -300,11 +406,11 @@ public class AAFrequency
    * @param endCol
    *          end column (exclusive)
    */
-  public static void completeGapAnnot(AlignmentAnnotation gaprow,
+  public static void completeGapAnnot(AlignmentAnnotation occupancy,
           ProfilesI profiles, int startCol, int endCol, long nseq)
   {
-    if (gaprow == null || gaprow.annotations == null
-            || gaprow.annotations.length < endCol)
+    if (occupancy == null || occupancy.annotations == null
+            || occupancy.annotations.length < endCol)
     {
       /*
        * called with a bad alignment annotation row 
@@ -313,8 +419,8 @@ public class AAFrequency
       return;
     }
     // always set ranges again
-    gaprow.graphMax = nseq;
-    gaprow.graphMin = 0;
+    occupancy.graphMax = nseq;
+    occupancy.graphMin = 0;
     double scale = 0.8 / nseq;
     for (int i = startCol; i < endCol; i++)
     {
@@ -325,7 +431,7 @@ public class AAFrequency
          * happens if sequences calculated over were 
          * shorter than alignment width
          */
-        gaprow.annotations[i] = null;
+        occupancy.annotations[i] = null;
         return;
       }
 
@@ -333,7 +439,8 @@ public class AAFrequency
 
       String description = "" + gapped;
 
-      gaprow.annotations[i] = new Annotation("", description, '\0', gapped,
+      occupancy.annotations[i] = new Annotation("", description, '\0',
+              gapped,
               jalview.util.ColorUtils.bleachColour(Color.DARK_GRAY,
                       (float) scale * gapped));
     }
@@ -470,6 +577,7 @@ public class AAFrequency
     return result;
   }
 
+
   /**
    * Extract a sorted extract of cDNA codon profile data. The returned array
    * contains
@@ -748,4 +856,118 @@ public class AAFrequency
     }
     return scale;
   }
+
+  /**
+   * Returns the sorted HMM profile for the given column of the alignment. The
+   * returned array contains
+   * 
+   * <pre>
+   *    [profileType=0, numberOfValues, 100, charValue1, percentage1, charValue2, percentage2, ...]
+   * in descending order of percentage value
+   * </pre>
+   * 
+   * @param hmm
+   * @param column
+   * @param removeBelowBackground
+   *          if true, ignores residues with probability less than their
+   *          background frequency
+   * @param infoHeight
+   *          if true, uses the log ratio 'information' measure to scale the
+   *          value
+   * @return
+   */
+  public static int[] extractHMMProfile(HiddenMarkovModel hmm, int column,
+          boolean removeBelowBackground, boolean infoHeight)
+  {
+    if (hmm == null)
+    {
+      return null;
+    }
+    String alphabet = hmm.getSymbols();
+    int size = alphabet.length();
+    char symbols[] = new char[size];
+    int values[] = new int[size];
+    int totalCount = 0;
+
+    for (int i = 0; i < size; i++)
+    {
+      char symbol = alphabet.charAt(i);
+      symbols[i] = symbol;
+      int value = getAnalogueCount(hmm, column, symbol,
+              removeBelowBackground, infoHeight);
+      values[i] = value;
+      totalCount += value;
+    }
+
+    /*
+     * sort symbols by increasing emission probability
+     */
+    QuickSort.sort(values, symbols);
+
+    int[] profile = new int[3 + size * 2];
+
+    profile[0] = AlignmentAnnotation.SEQUENCE_PROFILE;
+    profile[1] = size;
+    profile[2] = 100;
+
+    /*
+     * order symbol/count profile by decreasing emission probability
+     */
+    if (totalCount != 0)
+    {
+      int arrayPos = 3;
+      for (int k = size - 1; k >= 0; k--)
+      {
+        Float percentage;
+        int value = values[k];
+        if (removeBelowBackground)
+        {
+          percentage = ((float) value) / totalCount * 100f;
+        }
+        else
+        {
+          percentage = value / 100f;
+        }
+        int intPercent = Math.round(percentage);
+        profile[arrayPos] = symbols[k];
+        profile[arrayPos + 1] = intPercent;
+        arrayPos += 2;
+      }
+    }
+    return profile;
+  }
+
+  /**
+   * Converts the emission probability of a residue at a column in the alignment
+   * to a 'count', suitable for rendering as an annotation value
+   * 
+   * @param hmm
+   * @param column
+   * @param symbol
+   * @param removeBelowBackground
+   *          if true, returns 0 for any symbol with a match emission
+   *          probability less than the background frequency
+   * @infoHeight if true, uses the log ratio 'information content' to scale the
+   *             value
+   * @return
+   */
+  static int getAnalogueCount(HiddenMarkovModel hmm, int column,
+          char symbol, boolean removeBelowBackground, boolean infoHeight)
+  {
+    double value = hmm.getMatchEmissionProbability(column, symbol);
+    double freq = ResidueProperties.backgroundFrequencies
+            .get(hmm.getAlphabetType()).get(symbol);
+    if (value < freq && removeBelowBackground)
+    {
+      return 0;
+    }
+
+    if (infoHeight)
+    {
+      value = value * (Math.log(value / freq) / LOG2);
+    }
+
+    value = value * 10000d;
+    return Math.round((float) value);
+  }
 }
index f5626ce..5c2d936 100644 (file)
@@ -75,11 +75,11 @@ public class AlignmentAnnotationUtils
      * Build a lookup, by calcId (annotation source), of all annotation types in
      * each graph group.
      */
-    Map<String, Map<Integer, List<String>>> groupLabels = new HashMap<String, Map<Integer, List<String>>>();
+    Map<String, Map<Integer, List<String>>> groupLabels = new HashMap<>();
 
     // trackers for which calcId!label combinations we have dealt with
-    List<String> addedToShown = new ArrayList<String>();
-    List<String> addedToHidden = new ArrayList<String>();
+    List<String> addedToShown = new ArrayList<>();
+    List<String> addedToHidden = new ArrayList<>();
 
     for (AlignmentAnnotation aa : annotations)
     {
@@ -99,7 +99,7 @@ public class AlignmentAnnotationUtils
         /*
          * Build a 'composite label' for types in line graph groups.
          */
-        final List<String> labelAsList = new ArrayList<String>();
+        final List<String> labelAsList = new ArrayList<>();
         final String displayLabel = aa.label;
         labelAsList.add(displayLabel);
         if (aa.graph == AlignmentAnnotation.LINE_GRAPH
@@ -239,4 +239,42 @@ public class AlignmentAnnotationUtils
     return (anns == null ? Collections.<AlignmentAnnotation> emptyList()
             : Arrays.asList(anns));
   }
+
+  /**
+   * replace an existing sequence associated annotation with another, creating
+   * association as necessary.
+   * 
+   * @param newAnnot
+   *          - annotation row associated with a sequence to be propagated to
+   *          its reference annotation
+   * @param typeName
+   *          - label used to match existing row
+   * @param calcId
+   *          - calcId for existing row
+   */
+  public static void replaceAnnotationOnAlignmentWith(
+          AlignmentAnnotation newAnnot, String typeName, String calcId)
+  {
+    if (newAnnot.sequenceRef != null)
+    {
+      SequenceI dsseq = newAnnot.sequenceRef;
+      while (dsseq.getDatasetSequence() != null)
+      {
+        dsseq = dsseq.getDatasetSequence();
+      }
+      // look for same annotation on dataset and lift this one over
+      List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
+              typeName);
+      if (dsan != null && dsan.size() > 0)
+      {
+        for (AlignmentAnnotation dssan : dsan)
+        {
+          dsseq.removeAlignmentAnnotation(dssan);
+        }
+      }
+      AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
+      dsseq.addAlignmentAnnotation(dssan);
+      dssan.adjustForAlignment();
+    }
+  }
 }
index 81bddc2..f2b2222 100755 (executable)
@@ -22,6 +22,8 @@ package jalview.analysis;
 
 import jalview.analysis.scoremodels.PIDModel;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
@@ -32,6 +34,7 @@ import jalview.datamodel.SequenceNode;
 import jalview.util.QuickSort;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -51,40 +54,68 @@ import java.util.List;
  * from the first tobesorted position in the alignment. e.g. (a,tb2,b,tb1,c,tb3
  * becomes a,tb1,tb2,tb3,b,c)
  */
-public class AlignmentSorter
+public class AlignmentSorter implements ApplicationSingletonI
 {
+
+  private AlignmentSorter()
+  {
+    // private singleton
+  }
+
+  public static AlignmentSorter getInstance()
+  {
+    return (AlignmentSorter) ApplicationSingletonProvider
+            .getInstance(AlignmentSorter.class);
+  }
+
+  /**
+   * types of feature ordering: Sort by score : average score - or total score -
+   * over all features in region Sort by feature label text: (or if null -
+   * feature type text) - numerical or alphabetical Sort by feature density:
+   * based on counts - ignoring individual text or scores for each feature
+   */
+  public static final String FEATURE_SCORE = "average_score";
+
+  public static final String FEATURE_LABEL = "text";
+
+  public static final String FEATURE_DENSITY = "density";
+
   /*
    * todo: refactor searches to follow a basic pattern: (search property, last
    * search state, current sort direction)
    */
-  static boolean sortIdAscending = true;
+  boolean sortIdAscending = true;
 
-  static int lastGroupHash = 0;
+  int lastGroupHash = 0;
 
-  static boolean sortGroupAscending = true;
+  boolean sortGroupAscending = true;
 
-  static AlignmentOrder lastOrder = null;
+  AlignmentOrder lastOrder = null;
 
-  static boolean sortOrderAscending = true;
+  boolean sortOrderAscending = true;
 
-  static TreeModel lastTree = null;
+  TreeModel lastTree = null;
 
-  static boolean sortTreeAscending = true;
+  boolean sortTreeAscending = true;
 
-  /*
+  /**
    * last Annotation Label used for sort by Annotation score
    */
-  private static String lastSortByAnnotation;
+  private String lastSortByAnnotation;
 
-  /*
-   * string hash of last arguments to sortByFeature
-   * (sort order toggles if this is unchanged between sorts)
+  /**
+   * string hash of last arguments to sortByFeature (sort order toggles if this
+   * is unchanged between sorts)
    */
-  private static String sortByFeatureCriteria;
+  private String sortByFeatureCriteria;
 
-  private static boolean sortByFeatureAscending = true;
+  private boolean sortByFeatureAscending = true;
 
-  private static boolean sortLengthAscending;
+  private boolean sortLengthAscending;
+
+  private static boolean sortEValueAscending;
+
+  private static boolean sortBitScoreAscending;
 
   /**
    * Sorts sequences in the alignment by Percentage Identity with the given
@@ -115,114 +146,90 @@ public class AlignmentSorter
     }
 
     QuickSort.sort(scores, seqs);
-
     setReverseOrder(align, seqs);
   }
 
   /**
-   * Reverse the order of the sort
+   * Sorts by ID. Numbers are sorted before letters.
    * 
    * @param align
-   *          DOCUMENT ME!
-   * @param seqs
-   *          DOCUMENT ME!
+   *          The alignment object to sort
    */
-  private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
+  public static void sortByID(AlignmentI align)
   {
-    int nSeq = seqs.length;
-
-    int len = 0;
+    int nSeq = align.getHeight();
 
-    if ((nSeq % 2) == 0)
-    {
-      len = nSeq / 2;
-    }
-    else
-    {
-      len = (nSeq + 1) / 2;
-    }
+    String[] ids = new String[nSeq];
+    SequenceI[] seqs = new SequenceI[nSeq];
 
-    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> asq = align.getSequences();
-    synchronized (asq)
+    for (int i = 0; i < nSeq; i++)
     {
-      for (int i = 0; i < len; i++)
-      {
-        // SequenceI tmp = seqs[i];
-        asq.set(i, seqs[nSeq - i - 1]);
-        asq.set(nSeq - i - 1, seqs[i]);
-      }
+      ids[i] = align.getSequenceAt(i).getName();
+      seqs[i] = align.getSequenceAt(i);
     }
-  }
 
-  /**
-   * Sets the Alignment object with the given sequences
-   * 
-   * @param align
-   *          Alignment object to be updated
-   * @param tmp
-   *          sequences as a vector
-   */
-  private static void setOrder(AlignmentI align, List<SequenceI> tmp)
-  {
-    setOrder(align, vectorSubsetToArray(tmp, align.getSequences()));
+    QuickSort.sort(ids, seqs);
+    AlignmentSorter as = getInstance();
+    as.sortIdAscending = !as.sortIdAscending;
+    set(align, seqs, as.sortIdAscending);
   }
 
   /**
-   * Sets the Alignment object with the given sequences
+   * Sorts by sequence length
    * 
    * @param align
-   *          DOCUMENT ME!
-   * @param seqs
-   *          sequences as an array
+   *          The alignment object to sort
    */
-  public static void setOrder(AlignmentI align, SequenceI[] seqs)
+  public static void sortByLength(AlignmentI align)
   {
-    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> algn = align.getSequences();
-    synchronized (algn)
-    {
-      List<SequenceI> tmp = new ArrayList<>();
+    int nSeq = align.getHeight();
 
-      for (int i = 0; i < seqs.length; i++)
-      {
-        if (algn.contains(seqs[i]))
-        {
-          tmp.add(seqs[i]);
-        }
-      }
+    float[] length = new float[nSeq];
+    SequenceI[] seqs = new SequenceI[nSeq];
 
-      algn.clear();
-      // User may have hidden seqs, then clicked undo or redo
-      for (int i = 0; i < tmp.size(); i++)
-      {
-        algn.add(tmp.get(i));
-      }
+    for (int i = 0; i < nSeq; i++)
+    {
+      seqs[i] = align.getSequenceAt(i);
+      length[i] = (seqs[i].getEnd() - seqs[i].getStart());
     }
+
+    QuickSort.sort(length, seqs);
+    AlignmentSorter as = getInstance();
+    as.sortLengthAscending = !as.sortLengthAscending;
+    set(align, seqs, as.sortLengthAscending);
   }
 
   /**
-   * Sorts by ID. Numbers are sorted before letters.
+   * Sorts by sequence evalue. Currently moves all sequences without an evalue to
+   * the top of the alignment.
    * 
    * @param align
-   *          The alignment object to sort
+   *                The alignment object to sort
    */
-  public static void sortByID(AlignmentI align)
+  public static void sortByEValue(AlignmentI align)
   {
     int nSeq = align.getHeight();
 
-    String[] ids = new String[nSeq];
+    double[] evalue = new double[nSeq];
     SequenceI[] seqs = new SequenceI[nSeq];
 
     for (int i = 0; i < nSeq; i++)
     {
-      ids[i] = align.getSequenceAt(i).getName();
       seqs[i] = align.getSequenceAt(i);
+      AlignmentAnnotation[] ann = seqs[i].getAnnotation("Search Scores");
+      if (ann != null)
+      {
+        evalue[i] = ann[0].getEValue();
+      }
+      else
+      {
+        evalue[i] = -1;
+      }
     }
 
-    QuickSort.sort(ids, seqs);
+    QuickSort.sort(evalue, seqs);
 
-    if (sortIdAscending)
+    if (sortEValueAscending)
     {
       setReverseOrder(align, seqs);
     }
@@ -231,31 +238,40 @@ public class AlignmentSorter
       setOrder(align, seqs);
     }
 
-    sortIdAscending = !sortIdAscending;
+    sortEValueAscending = !sortEValueAscending;
   }
 
   /**
-   * Sorts by sequence length
+   * Sorts by sequence bit score. Currently moves all sequences without a bit
+   * score to the top of the alignment
    * 
    * @param align
-   *          The alignment object to sort
+   *                The alignment object to sort
    */
-  public static void sortByLength(AlignmentI align)
+  public static void sortByBitScore(AlignmentI align)
   {
     int nSeq = align.getHeight();
 
-    float[] length = new float[nSeq];
+    double[] score = new double[nSeq];
     SequenceI[] seqs = new SequenceI[nSeq];
 
     for (int i = 0; i < nSeq; i++)
     {
       seqs[i] = align.getSequenceAt(i);
-      length[i] = (seqs[i].getEnd() - seqs[i].getStart());
+      AlignmentAnnotation[] ann = seqs[i].getAnnotation("Search Scores");
+      if (ann != null)
+      {
+        score[i] = ann[0].getEValue();
+      }
+      else
+      {
+        score[i] = -1;
+      }
     }
 
-    QuickSort.sort(length, seqs);
+    QuickSort.sort(score, seqs);
 
-    if (sortLengthAscending)
+    if (sortBitScoreAscending)
     {
       setReverseOrder(align, seqs);
     }
@@ -264,7 +280,7 @@ public class AlignmentSorter
       setOrder(align, seqs);
     }
 
-    sortLengthAscending = !sortLengthAscending;
+    sortBitScoreAscending = !sortBitScoreAscending;
   }
 
   /**
@@ -281,14 +297,16 @@ public class AlignmentSorter
     // ORDERS BY GROUP SIZE
     List<SequenceGroup> groups = new ArrayList<>();
 
-    if (groups.hashCode() != lastGroupHash)
+    AlignmentSorter as = getInstance();
+
+    if (groups.hashCode() != as.lastGroupHash)
     {
-      sortGroupAscending = true;
-      lastGroupHash = groups.hashCode();
+      as.sortGroupAscending = true;
+      as.lastGroupHash = groups.hashCode();
     }
     else
     {
-      sortGroupAscending = !sortGroupAscending;
+      as.sortGroupAscending = !as.sortGroupAscending;
     }
 
     // SORTS GROUPS BY SIZE
@@ -315,7 +333,7 @@ public class AlignmentSorter
 
     // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
     // /////////////////////////////////////////////
-    List<SequenceI> seqs = new ArrayList<>();
+    List<SequenceI> tmp = new ArrayList<>();
 
     for (int i = 0; i < groups.size(); i++)
     {
@@ -324,68 +342,10 @@ public class AlignmentSorter
 
       for (int j = 0; j < orderedseqs.length; j++)
       {
-        seqs.add(orderedseqs[j]);
-      }
-    }
-
-    if (sortGroupAscending)
-    {
-      setOrder(align, seqs);
-    }
-    else
-    {
-      setReverseOrder(align,
-              vectorSubsetToArray(seqs, align.getSequences()));
-    }
-  }
-
-  /**
-   * Select sequences in order from tmp that is present in mask, and any
-   * remaining sequences in mask not in tmp
-   * 
-   * @param tmp
-   *          thread safe collection of sequences
-   * @param mask
-   *          thread safe collection of sequences
-   * 
-   * @return intersect(tmp,mask)+intersect(complement(tmp),mask)
-   */
-  private static SequenceI[] vectorSubsetToArray(List<SequenceI> tmp,
-          List<SequenceI> mask)
-  {
-    // or?
-    // tmp2 = tmp.retainAll(mask);
-    // return tmp2.addAll(mask.removeAll(tmp2))
-
-    ArrayList<SequenceI> seqs = new ArrayList<>();
-    int i, idx;
-    boolean[] tmask = new boolean[mask.size()];
-
-    for (i = 0; i < mask.size(); i++)
-    {
-      tmask[i] = true;
-    }
-
-    for (i = 0; i < tmp.size(); i++)
-    {
-      SequenceI sq = tmp.get(i);
-      idx = mask.indexOf(sq);
-      if (idx > -1 && tmask[idx])
-      {
-        tmask[idx] = false;
-        seqs.add(sq);
-      }
-    }
-
-    for (i = 0; i < tmask.length; i++)
-    {
-      if (tmask[i])
-      {
-        seqs.add(mask.get(i));
+        tmp.add(orderedseqs[j]);
       }
     }
-
-    return seqs.toArray(new SequenceI[seqs.size()]);
+    set(align, tmp, as.sortGroupAscending);
   }
 
   /**
@@ -401,24 +361,17 @@ public class AlignmentSorter
     // Get an ordered vector of sequences which may also be present in align
     List<SequenceI> tmp = order.getOrder();
 
-    if (lastOrder == order)
-    {
-      sortOrderAscending = !sortOrderAscending;
-    }
-    else
-    {
-      sortOrderAscending = true;
-    }
+    AlignmentSorter as = getInstance();
 
-    if (sortOrderAscending)
+    if (as.lastOrder == order)
     {
-      setOrder(align, tmp);
+      as.sortOrderAscending = !as.sortOrderAscending;
     }
     else
     {
-      setReverseOrder(align,
-              vectorSubsetToArray(tmp, align.getSequences()));
+      as.sortOrderAscending = true;
     }
+    set(align, tmp, as.sortOrderAscending);
   }
 
   /**
@@ -473,26 +426,19 @@ public class AlignmentSorter
   {
     List<SequenceI> tmp = getOrderByTree(align, tree);
 
-    // tmp should properly permute align with tree.
-    if (lastTree != tree)
-    {
-      sortTreeAscending = true;
-      lastTree = tree;
-    }
-    else
-    {
-      sortTreeAscending = !sortTreeAscending;
-    }
+    AlignmentSorter as = getInstance();
 
-    if (sortTreeAscending)
+    // tmp should properly permute align with tree.
+    if (as.lastTree != tree)
     {
-      setOrder(align, tmp);
+      as.sortTreeAscending = true;
+      as.lastTree = tree;
     }
     else
     {
-      setReverseOrder(align,
-              vectorSubsetToArray(tmp, align.getSequences()));
+      as.sortTreeAscending = !as.sortTreeAscending;
     }
+    set(align, tmp, as.sortTreeAscending);
   }
 
   /**
@@ -658,9 +604,12 @@ public class AlignmentSorter
     }
 
     jalview.util.QuickSort.sort(scores, seqs);
-    if (lastSortByAnnotation != scoreLabel)
+
+    AlignmentSorter as = getInstance();
+
+    if (as.lastSortByAnnotation != scoreLabel)
     {
-      lastSortByAnnotation = scoreLabel;
+      as.lastSortByAnnotation = scoreLabel;
       setOrder(alignment, seqs);
     }
     else
@@ -670,18 +619,6 @@ public class AlignmentSorter
   }
 
   /**
-   * types of feature ordering: Sort by score : average score - or total score -
-   * over all features in region Sort by feature label text: (or if null -
-   * feature type text) - numerical or alphabetical Sort by feature density:
-   * based on counts - ignoring individual text or scores for each feature
-   */
-  public static String FEATURE_SCORE = "average_score";
-
-  public static String FEATURE_LABEL = "text";
-
-  public static String FEATURE_DENSITY = "density";
-
-  /**
    * Sort sequences by feature score or density, optionally restricted by
    * feature types, feature groups, or alignment start/end positions.
    * <p>
@@ -810,6 +747,8 @@ public class AlignmentSorter
       }
     }
 
+    boolean doSort = false;
+
     if (FEATURE_SCORE.equals(method))
     {
       if (hasScores == 0)
@@ -835,7 +774,7 @@ public class AlignmentSorter
           }
         }
       }
-      QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+      doSort = true;
     }
     else if (FEATURE_DENSITY.equals(method))
     {
@@ -847,9 +786,13 @@ public class AlignmentSorter
         // System.err.println("Sorting on Density: seq "+seqs[i].getName()+
         // " Feats: "+featureCount+" Score : "+scores[i]);
       }
-      QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+      doSort = true;
+    }
+    if (doSort)
+    {
+      QuickSort.sortByDouble(scores, seqs,
+              getInstance().sortByFeatureAscending);
     }
-
     setOrder(alignment, seqs);
   }
 
@@ -884,16 +827,177 @@ public class AlignmentSorter
     /*
      * if resorting on the same criteria, toggle sort order
      */
-    if (sortByFeatureCriteria == null
-            || !scoreCriteria.equals(sortByFeatureCriteria))
+    AlignmentSorter as = getInstance();
+    if (as.sortByFeatureCriteria == null
+            || !scoreCriteria.equals(as.sortByFeatureCriteria))
+    {
+      as.sortByFeatureAscending = true;
+    }
+    else
+    {
+      as.sortByFeatureAscending = !as.sortByFeatureAscending;
+    }
+    as.sortByFeatureCriteria = scoreCriteria;
+  }
+
+  /**
+   * Set the alignment's sequences list to contain the sequences from a
+   * temporary list, first adding all the elements from the tmp list, then adding all sequences in the alignment that
+   * are not in the list. Option to do the final sort either in order or in reverse order.
+   * 
+   * @param align The alignment being sorted
+   * @param tmp
+   *          the temporary sequence list
+   * @param ascending
+   *          false for reversed order; only sequences already in
+   *          the alignment will be used (which is actually already guaranteed
+   *          by vectorSubsetToArray)
+   */
+  private static void set(AlignmentI align, List<SequenceI> tmp,
+          boolean ascending)
+  {
+    set(align, vectorSubsetToArray(align.getSequences(), tmp), ascending);
+  }
+
+  /**
+   * Set the alignment's sequences list to contain these sequences, either in
+   * this order or its reverse.
+   * 
+   * @param align
+   * @param seqs
+   *          the new sequence array
+   * @param ascending
+   *          false for reversed order; if ascending, only sequences already in
+   *          the alignment will be used; if descending, then a direct 1:1
+   *          replacement is made
+   */
+  private static void set(AlignmentI align, SequenceI[] seqs,
+          boolean ascending)
+  {
+    if (ascending)
     {
-      sortByFeatureAscending = true;
+      setOrder(align, seqs);
     }
     else
     {
-      sortByFeatureAscending = !sortByFeatureAscending;
+      setReverseOrder(align, seqs);
+    }
+
+  }
+
+  /**
+   * Replace the alignment's sequences with values in an array, clearing the
+   * alignment's sequence list and filtering for sequences that are actually in
+   * the alignment already.
+   * 
+   * @param align
+   *          the Alignment
+   * @param seqs
+   *          the array of replacement values, of any length
+   */
+  public static void setOrder(AlignmentI align, SequenceI[] seqs)
+  {
+    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
+    {
+      List<SequenceI> tmp = new ArrayList<>();
+
+      for (int i = 0; i < seqs.length; i++)
+      {
+        if (seqList.contains(seqs[i]))
+        {
+          tmp.add(seqs[i]);
+        }
+      }
+
+      seqList.clear();
+      // User may have hidden seqs, then clicked undo or redo
+      for (int i = 0; i < tmp.size(); i++)
+      {
+        seqList.add(tmp.get(i));
+      }
+    }
+  }
+
+  /**
+   * Replace the alignment's sequences or a subset of those sequences with
+   * values in an array in reverse order. All sequences are replaced; no check
+   * is made that these sequences are in the alignment already.
+   * 
+   * @param align
+   *          the Alignment
+   * @param seqs
+   *          the array of replacement values, length must be less than or equal
+   *          to Alignment.sequences.size()
+   */
+  private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
+  {
+    int nSeq = seqs.length;
+
+    int len = (nSeq + (nSeq % 2)) / 2;
+    // int len = 0;
+    //
+    // if ((nSeq % 2) == 0)
+    // {
+    // len = nSeq / 2;
+    // }
+    // else
+    // {
+    // len = (nSeq + 1) / 2;
+    // }
+
+    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
+    {
+      for (int i = 0; i < len; i++)
+      {
+        // SequenceI tmp = seqs[i];
+        seqList.set(i, seqs[nSeq - i - 1]);
+        seqList.set(nSeq - i - 1, seqs[i]);
+      }
+    }
+  }
+
+  /**
+   * Create and array of reordered sequences in order first from tmp that are
+   * present in seqList already, then, after that, any remaining sequences in
+   * seqList not in tmp. Any sequences in tmp that are not in seqList already
+   * are discarded.
+   * 
+   * @param seqList
+   *          thread safe collection of sequences originally in the alignment
+   * @param tmp
+   *          thread safe collection of sequences or subsequences possibly in
+   *          seqList
+   * 
+   * @return intersect(tmp,seqList)+intersect(complement(tmp),seqList)
+   */
+  private static SequenceI[] vectorSubsetToArray(List<SequenceI> seqList,
+          List<SequenceI> tmp)
+  {
+    ArrayList<SequenceI> seqs = new ArrayList<>();
+    int n = seqList.size();
+    BitSet bs = new BitSet(n);
+    bs.set(0, n);
+    for (int i = 0, nt = tmp.size(); i < nt; i++)
+    {
+      SequenceI sq = tmp.get(i);
+      int idx = seqList.indexOf(sq);
+      if (idx >= 0 && bs.get(idx))
+      {
+        seqs.add(sq);
+        bs.clear(idx);
+      }
+    }
+
+    for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
+    {
+      seqs.add(seqList.get(i));
     }
-    sortByFeatureCriteria = scoreCriteria;
+
+    return seqs.toArray(new SequenceI[seqs.size()]);
   }
 
 }
index fdc74e2..6ac59ba 100644 (file)
@@ -1464,28 +1464,31 @@ public class AlignmentUtils
       final List<AlignmentAnnotation> result = new ArrayList<>();
       for (AlignmentAnnotation dsann : datasetAnnotations)
       {
-        /*
-         * Find matching annotations on the alignment. If none is found, then
-         * add this annotation to the list of 'addable' annotations for this
-         * sequence.
-         */
-        final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
-                .findAnnotations(seq, dsann.getCalcId(), dsann.label);
-        if (!matchedAlignmentAnnotations.iterator().hasNext())
+        if (dsann.annotations != null) // ignore non-positional annotation
         {
-          result.add(dsann);
-          if (labelForCalcId != null)
+          /*
+           * Find matching annotations on the alignment. If none is found, then
+           * add this annotation to the list of 'addable' annotations for this
+           * sequence.
+           */
+          final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
+                  .findAnnotations(seq, dsann.getCalcId(), dsann.label);
+          if (!matchedAlignmentAnnotations.iterator().hasNext())
           {
-            labelForCalcId.put(dsann.getCalcId(), dsann.label);
+            result.add(dsann);
+            if (labelForCalcId != null)
+            {
+              labelForCalcId.put(dsann.getCalcId(), dsann.label);
+            }
           }
         }
-      }
-      /*
-       * Save any addable annotations for this sequence
-       */
-      if (!result.isEmpty())
-      {
-        candidates.put(seq, result);
+        /*
+         * Save any addable annotations for this sequence
+         */
+        if (!result.isEmpty())
+        {
+          candidates.put(seq, result);
+        }
       }
     }
   }
index 9b90ca5..87f854a 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -31,12 +35,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
-import jalview.ws.SequenceFetcherFactory;
-import jalview.ws.seqfetcher.ASequenceFetcher;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import jalview.ws.SequenceFetcher;
 
 /**
  * Functions for cross-referencing sequence databases.
@@ -405,7 +404,6 @@ public class CrossRef
   private void retrieveCrossRef(List<DBRefEntry> sourceRefs, SequenceI seq,
           List<DBRefEntry> xrfs, boolean fromDna, AlignedCodonFrame cf)
   {
-    ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher();
     SequenceI[] retrieved = null;
     SequenceI dss = seq.getDatasetSequence() == null ? seq
             : seq.getDatasetSequence();
@@ -421,7 +419,7 @@ public class CrossRef
     }
     try
     {
-      retrieved = sftch.getSequences(sourceRefs, !fromDna);
+      retrieved = SequenceFetcher.getInstance().getSequences(sourceRefs, !fromDna);
     } catch (Exception e)
     {
       System.err.println(
index 5dcf212..a2b0472 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
@@ -28,7 +34,6 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
 import jalview.datamodel.FeatureProperties;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.Mapping;
@@ -41,12 +46,6 @@ import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.ShiftList;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
 public class Dna
 {
   private static final String STOP_ASTERIX = "*";
index f8cfcbf..33c6a23 100644 (file)
@@ -31,6 +31,7 @@ import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.util.Comparison;
+import jalview.util.Platform;
 import jalview.util.MapList;
 
 import java.util.ArrayList;
@@ -151,7 +152,7 @@ public class Finder implements FinderI
 
     String searchString = matchCase ? theSearchString
             : theSearchString.toUpperCase(Locale.ROOT);
-    Regex searchPattern = new Regex(searchString);
+  Regex searchPattern = Platform.newRegex(searchString);
     searchPattern.setIgnoreCase(!matchCase);
 
     SequenceGroup selection = viewport.getSelectionGroup();
index 4b68d93..bfd4d6a 100644 (file)
@@ -23,6 +23,7 @@ package jalview.analysis;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
 
 import com.stevesoft.pat.Regex;
 
@@ -90,7 +91,7 @@ public class ParseProperties
           String[] ScoreDescriptions, String regex, boolean repeat)
   {
     int count = 0;
-    Regex pattern = new Regex(regex);
+    Regex pattern = Platform.newRegex(regex);
     if (pattern.numSubs() > ScoreNames.length)
     {
       // Check that we have enough labels and descriptions for any parsed
index fdca89d..5a246f8 100755 (executable)
  */
 package jalview.analysis;
 
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 
+import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.Vector;
+import static java.lang.String.format;
 
 public class SeqsetUtils
 {
+  public static class SequenceInfo {
+    private String name;
+    private int start;
+    private int end;
+    private Optional<String> description = Optional.empty();
+    private Optional<List<SequenceFeature>> features = Optional.empty();
+    private Optional<List<PDBEntry>> pdbId = Optional.empty();
+    private Optional<SequenceI> dataset = Optional.empty();
+    private Optional<HiddenMarkovModel> hmm = Optional.empty();
+    private Optional<AlignmentAnnotation[]> searchScores = Optional.empty();
+
+    private SequenceInfo(String name, int start, int end) {
+      this.name = name;
+      this.start = start;
+      this.end = end;
+    }
+  }
 
   /**
    * Store essential properties of a sequence in a hashtable for later recovery
-   * Keys are Name, Start, End, SeqFeatures, PdbId
+   * Keys are Name, Start, End, SeqFeatures, PdbId, HMM
    * 
    * @param seq
    *          SequenceI
    * @return Hashtable
    */
-  public static Hashtable SeqCharacterHash(SequenceI seq)
+  public static SequenceInfo SeqCharacterHash(SequenceI seq)
   {
-    Hashtable sqinfo = new Hashtable();
-    sqinfo.put("Name", seq.getName());
-    sqinfo.put("Start", Integer.valueOf(seq.getStart()));
-    sqinfo.put("End", Integer.valueOf(seq.getEnd()));
-    if (seq.getDescription() != null)
-    {
-      sqinfo.put("Description", seq.getDescription());
-    }
-
-    Vector<SequenceFeature> sfeat = new Vector<SequenceFeature>();
-    List<SequenceFeature> sfs = seq.getFeatures().getAllFeatures();
-    sfeat.addAll(sfs);
-
-    if (seq.getDatasetSequence() == null)
+    SequenceInfo sqinfo = new SequenceInfo(seq.getName(), seq.getStart(), seq.getEnd());
+    sqinfo.description = Optional.ofNullable(seq.getDescription());
+    sqinfo.dataset = Optional.ofNullable(seq.getDatasetSequence());
+    if (!sqinfo.dataset.isPresent())
     {
-      sqinfo.put("SeqFeatures", sfeat);
-      sqinfo.put("PdbId",
-              (seq.getAllPDBEntries() != null) ? seq.getAllPDBEntries()
-                      : new Vector<PDBEntry>());
+      ArrayList<SequenceFeature> feats = new ArrayList<>(
+          seq.getFeatures().getAllFeatures());
+      sqinfo.features = Optional.of(feats);
+      sqinfo.pdbId = Optional.of(Objects.requireNonNullElse(
+          seq.getAllPDBEntries(), new ArrayList<>()));
     }
-    else
+    if (seq.hasHMMProfile())
     {
-      sqinfo.put("datasetSequence",
-              (seq.getDatasetSequence() != null) ? seq.getDatasetSequence()
-                      : new Sequence("THISISAPLACEHOLDER", ""));
+      sqinfo.hmm = Optional.of(seq.getHMM());
     }
+    sqinfo.searchScores = Optional.ofNullable(seq.getAnnotation("Search Scores"));
     return sqinfo;
   }
 
@@ -82,60 +100,44 @@ public class SeqsetUtils
    *          Hashtable
    * @return boolean true if name was not updated from sqinfo Name entry
    */
-  public static boolean SeqCharacterUnhash(SequenceI sq, Hashtable sqinfo)
+  public static boolean SeqCharacterUnhash(SequenceI sq, SequenceInfo sqinfo)
   {
-    boolean namePresent = true;
     if (sqinfo == null)
     {
       return false;
     }
-    String oldname = (String) sqinfo.get("Name");
-    Integer start = (Integer) sqinfo.get("Start");
-    Integer end = (Integer) sqinfo.get("End");
-    Vector<SequenceFeature> sfeatures = (Vector<SequenceFeature>) sqinfo
-            .get("SeqFeatures");
-    Vector<PDBEntry> pdbid = (Vector<PDBEntry>) sqinfo.get("PdbId");
-    String description = (String) sqinfo.get("Description");
-    Sequence seqds = (Sequence) sqinfo.get("datasetSequence");
-    if (oldname == null)
-    {
-      namePresent = false;
-    }
-    else
+    if (sqinfo.name != null)
     {
-      sq.setName(oldname);
+      sq.setName(sqinfo.name);
     }
-    if (pdbid != null && pdbid.size() > 0)
-    {
-      sq.setPDBId(pdbid);
-    }
-
-    if ((start != null) && (end != null))
+    sq.setStart(sqinfo.start);
+    sq.setEnd(sqinfo.end);
+    if (sqinfo.pdbId.isPresent() && !sqinfo.pdbId.get().isEmpty())
+      sq.setPDBId(new Vector<>(sqinfo.pdbId.get()));
+    if (sqinfo.features.isPresent() && !sqinfo.features.get().isEmpty())
+      sq.setSequenceFeatures(sqinfo.features.get());
+    if (sqinfo.description.isPresent())
+      sq.setDescription(sqinfo.description.get());
+    if (sqinfo.dataset.isPresent())
     {
-      sq.setStart(start.intValue());
-      sq.setEnd(end.intValue());
-    }
-
-    if (sfeatures != null && !sfeatures.isEmpty())
-    {
-      sq.setSequenceFeatures(sfeatures);
-    }
-    if (description != null)
-    {
-      sq.setDescription(description);
+      if (sqinfo.features.isPresent())
+      {
+        Console.warn("Setting dataset sequence for a sequence which has " +
+            "sequence features. Dataset sequence features will not be visible.");
+        assert false;
+      }
+      sq.setDatasetSequence(sqinfo.dataset.get());
     }
-    if ((seqds != null) && !(seqds.getName().equals("THISISAPLACEHOLDER")
-            && seqds.getLength() == 0))
+    if (sqinfo.hmm.isPresent())
+      sq.setHMM(new HiddenMarkovModel(sqinfo.hmm.get(), sq));
+    if (sqinfo.searchScores.isPresent())
     {
-      if (sfeatures != null)
+      for (AlignmentAnnotation score : sqinfo.searchScores.get())
       {
-        System.err.println(
-                "Implementation error: setting dataset sequence for a sequence which has sequence features.\n\tDataset sequence features will not be visible.");
+        sq.addAlignmentAnnotation(score);
       }
-      sq.setDatasetSequence(seqds);
     }
-
-    return namePresent;
+    return sqinfo.name != null;
   }
 
   /**
@@ -148,7 +150,7 @@ public class SeqsetUtils
    */
   public static String unique_name(int i)
   {
-    return new String("Sequence" + i);
+    return String.format("Sequence%d", i);
   }
 
   /**
@@ -165,12 +167,12 @@ public class SeqsetUtils
    * @see deuniquify to recover original names (and properties) for renamed
    *      sequences
    */
-  public static Hashtable uniquify(SequenceI[] sequences,
+  public static Map<String, SequenceInfo> uniquify(SequenceI[] sequences,
           boolean write_names)
   {
     // Generate a safely named sequence set and a hash to recover the sequence
     // names
-    Hashtable map = new Hashtable();
+    HashMap<String, SequenceInfo> map = new HashMap<>();
     // String[] un_names = new String[sequences.length];
 
     for (int i = 0; i < sequences.length; i++)
@@ -198,7 +200,8 @@ public class SeqsetUtils
    *          SequenceI[]
    * @return boolean
    */
-  public static boolean deuniquify(Hashtable map, SequenceI[] sequences)
+  public static boolean deuniquify(Map<String, SequenceInfo> map,
+      SequenceI[] sequences)
   {
     return deuniquify(map, sequences, true);
   }
@@ -217,26 +220,25 @@ public class SeqsetUtils
    *          map.
    * @return boolean
    */
-  public static boolean deuniquify(Hashtable map, SequenceI[] sequences,
-          boolean quiet)
+  public static boolean deuniquify(Map<String, SequenceInfo> map,
+      SequenceI[] sequences, boolean quiet)
   {
     jalview.analysis.SequenceIdMatcher matcher = new SequenceIdMatcher(
             sequences);
     SequenceI msq = null;
-    Enumeration keys = map.keys();
-    Vector unmatched = new Vector();
+    Iterator<String> keys = map.keySet().iterator();
+    Vector<SequenceI> unmatched = new Vector<>();
     for (int i = 0, j = sequences.length; i < j; i++)
     {
       unmatched.addElement(sequences[i]);
     }
-    while (keys.hasMoreElements())
+    while (keys.hasNext())
     {
-      Object key = keys.nextElement();
-      if (key instanceof String)
-      {
+      String key = keys.next();
+      try {
         if ((msq = matcher.findIdMatch((String) key)) != null)
         {
-          Hashtable sqinfo = (Hashtable) map.get(key);
+          SequenceInfo sqinfo = map.get(key);
           unmatched.removeElement(msq);
           SeqCharacterUnhash(msq, sqinfo);
         }
@@ -244,21 +246,27 @@ public class SeqsetUtils
         {
           if (!quiet)
           {
-            System.err.println("Can't find '" + ((String) key)
-                    + "' in uniquified alignment");
+            Console.warn(format("Can't find '%s' in uniquified alignment",
+                key));
           }
         }
+      } catch (ClassCastException ccastex) {
+        if (!quiet)
+        {
+          Console.error("Unexpected object in SeqSet map : "+ key.getClass());
+        }
       }
     }
     if (unmatched.size() > 0 && !quiet)
     {
-      System.err.println("Did not find matches for :");
-      for (Enumeration i = unmatched.elements(); i
-              .hasMoreElements(); System.out
-                      .println(((SequenceI) i.nextElement()).getName()))
+      StringBuilder sb = new StringBuilder("Did not find match for sequences: ");
+      Enumeration<SequenceI> i = unmatched.elements();
+      sb.append(i.nextElement().getName());
+      for (; i.hasMoreElements();)
       {
-        ;
+        sb.append(", " + i.nextElement().getName());
       }
+      Console.warn(sb.toString());
       return false;
     }
 
index ebc9a26..8700ec0 100644 (file)
@@ -22,6 +22,8 @@ package jalview.analysis.scoremodels;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.analysis.ScoreModelI;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.io.DataSourceType;
 import jalview.io.FileParse;
 import jalview.io.ScoreMatrixFile;
@@ -33,18 +35,8 @@ import java.util.Map;
 /**
  * A class that can register and serve instances of ScoreModelI
  */
-public class ScoreModels
+public class ScoreModels implements ApplicationSingletonI
 {
-  private final ScoreMatrix BLOSUM62;
-
-  private final ScoreMatrix PAM250;
-
-  private final ScoreMatrix DNA;
-
-  private static ScoreModels instance;
-
-  private Map<String, ScoreModelI> models;
-
   /**
    * Answers the singleton instance of this class, with lazy initialisation
    * (built-in score models are loaded on the first call to this method)
@@ -53,11 +45,7 @@ public class ScoreModels
    */
   public static ScoreModels getInstance()
   {
-    if (instance == null)
-    {
-      instance = new ScoreModels();
-    }
-    return instance;
+    return (ScoreModels) ApplicationSingletonProvider.getInstance(ScoreModels.class);
   }
 
   /**
@@ -84,6 +72,14 @@ public class ScoreModels
     registerScoreModel(new FeatureDistanceModel());
   }
 
+  private final ScoreMatrix BLOSUM62;
+
+  private final ScoreMatrix PAM250;
+
+  private final ScoreMatrix DNA;
+
+  private Map<String, ScoreModelI> models;
+
   /**
    * Tries to load a score matrix from the given resource file, and if
    * successful, registers it.
@@ -153,7 +149,7 @@ public class ScoreModels
    */
   public void reset()
   {
-    instance = new ScoreModels();
+    ApplicationSingletonProvider.removeInstance(this.getClass());
   }
 
   /**
diff --git a/src/jalview/api/AlignCalcListener.java b/src/jalview/api/AlignCalcListener.java
new file mode 100644 (file)
index 0000000..9ce385d
--- /dev/null
@@ -0,0 +1,39 @@
+package jalview.api;
+
+import java.util.EventListener;
+
+/**
+ * A listener class which receives state updates of {@link AlignCalcWorkerI}.
+ * It can be registered with an {@link AlignCalcManagerI2}.
+ * 
+ * @author mmwarowny
+ *
+ */
+public interface AlignCalcListener extends EventListener
+{
+  /**
+   * Called when the worker is scheduler for execution with
+   * {@link AlignCalcManagerI2#startWorker(AlignCalcWorkerI)}.
+   */
+  default void workerQueued(AlignCalcWorkerI worker) {}
+  
+  /**
+   * Called when the worker starts calculations.
+   */
+  default void workerStarted(AlignCalcWorkerI worker) {}
+  
+  /**
+   * Called when the worker finishes successfully.
+   */
+  default void workerCompleted(AlignCalcWorkerI worker) {}
+  
+  /**
+   * Called when the worker is cancelled.
+   */
+  default void workerCancelled(AlignCalcWorkerI worker) {}
+  
+  /**
+   * Called when the worker finishes with an exception.
+   */
+  default void workerExceptional(AlignCalcWorkerI worker, Throwable throwable) {}
+}
diff --git a/src/jalview/api/AlignCalcManagerI.java b/src/jalview/api/AlignCalcManagerI.java
deleted file mode 100644 (file)
index 18605b8..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.api;
-
-import jalview.datamodel.AlignmentAnnotation;
-
-import java.util.List;
-
-public interface AlignCalcManagerI
-{
-
-  /**
-   * tell manager that a worker is initialised and has started to run
-   * 
-   * @param worker
-   */
-  void notifyStart(AlignCalcWorkerI worker);
-
-  /**
-   * tell manager that a thread running worker's run() loop is ready to start
-   * processing data
-   * 
-   * @param worker
-   * @return true if worker should start processing, false if another thread is
-   *         in progress
-   */
-  boolean notifyWorking(AlignCalcWorkerI worker);
-
-  /**
-   * notify manager that the worker has completed, and results may be ready to
-   * collect
-   * 
-   * @param worker
-   */
-  void workerComplete(AlignCalcWorkerI worker);
-
-  /**
-   * indicate that a worker like this cannot run on the platform and shouldn't
-   * be started again
-   * 
-   * @param worker
-   */
-  void disableWorker(AlignCalcWorkerI worker);
-
-  /**
-   * indicate that a worker like this may be run on the platform.
-   * 
-   * @param worker
-   *          of class to be removed from the execution blacklist
-   */
-  void enableWorker(AlignCalcWorkerI worker);
-
-  /**
-   * Answers true if the worker is disabled from running
-   * 
-   * @param worker
-   * @return
-   */
-  boolean isDisabled(AlignCalcWorkerI worker);
-
-  /**
-   * launch a new worker
-   * 
-   * @param worker
-   */
-  void startWorker(AlignCalcWorkerI worker);
-
-  /**
-   * 
-   * @param worker
-   * @return true if the worker is currently running
-   */
-  boolean isWorking(AlignCalcWorkerI worker);
-
-  /**
-   * if any worker thread is operational, return true!
-   * 
-   * @return
-   */
-  boolean isWorking();
-
-  /**
-   * register a restartable worker
-   * 
-   * @param worker
-   */
-  void registerWorker(AlignCalcWorkerI worker);
-
-  /**
-   * restart any registered workers
-   */
-  void restartWorkers();
-
-  /**
-   * 
-   * @param alignmentAnnotation
-   * @return true if a currently registered and working worker indicates its
-   *         involvement with the given alignmentAnnotation
-   */
-  boolean workingInvolvedWith(AlignmentAnnotation alignmentAnnotation);
-
-  /**
-   * kick any known instances of the given worker class to update their
-   * annotation
-   * 
-   * @param workerClass
-   */
-  void updateAnnotationFor(Class<? extends AlignCalcWorkerI> workerClass);
-
-  /**
-   * return any registered workers of the given class
-   * 
-   * @param workerClass
-   * @return null or one or more workers of the given class
-   */
-  List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
-          Class<? extends AlignCalcWorkerI> workerClass);
-
-  /**
-   * work out if there is an instance of a worker that is *waiting* to start
-   * calculating
-   * 
-   * @param workingClass
-   * @return true if workingClass is already waiting to calculate. false if it
-   *         is calculating, or not queued.
-   */
-  boolean isPending(AlignCalcWorkerI workingClass);
-
-  /**
-   * deregister and otherwise remove any registered and working instances of the
-   * given worker type
-   * 
-   * @param typeToRemove
-   */
-  void removeRegisteredWorkersOfClass(
-          Class<? extends AlignCalcWorkerI> typeToRemove);
-
-  /**
-   * Removes the worker that produces the given annotation, provided it is
-   * marked as 'deletable'. Some workers may need to continue to run as the
-   * results of their calculations are needed, e.g. for colour schemes.
-   * 
-   * @param ann
-   */
-  void removeWorkerForAnnotation(AlignmentAnnotation ann);
-}
diff --git a/src/jalview/api/AlignCalcManagerI2.java b/src/jalview/api/AlignCalcManagerI2.java
new file mode 100644 (file)
index 0000000..0bd6c3c
--- /dev/null
@@ -0,0 +1,127 @@
+package jalview.api;
+
+import java.util.List;
+
+import jalview.datamodel.AlignmentAnnotation;
+
+/**
+ * An abstract manager which controls the execution of interactive jobs.
+ * There is always one instance of AlignCancManager per AlignmenViewport
+ * which runs the jobs, notifies observers and ensures no race conditions.
+ * 
+ * @author mmwarowny
+ *
+ */
+public interface AlignCalcManagerI2
+{
+  /**
+   * Registers the worker with the manager and immediately schedules it
+   * for execution.
+   */
+  void registerWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Returns the list of all registered workers or an empty list if 
+   * there are none
+   */
+  List<AlignCalcWorkerI> getWorkers();
+  
+  /**
+   * Returns the list of all workers of a given class or an empty list
+   * if there are none. The classes are compared using 
+   * {@ink Class#equals(Object)} rather than {@code instanceof}.
+   */
+  List<AlignCalcWorkerI> getWorkersOfClass(Class<? extends AlignCalcWorkerI> cls);
+  
+  /**
+   * Removes the worker from the scheduler. It does not cancel workers
+   * already scheduled for execution.
+   */
+  void removeWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Removes all workers which are involved with the given annotation.
+   */
+  void removeWorkerForAnnotation(AlignmentAnnotation annot);
+  
+  /**
+   * Alias of removeWorkerForAnnotation
+   */
+  default void removeWorkersForAnnotation(AlignmentAnnotation annot) {
+    removeWorkerForAnnotation(annot);
+  }
+  
+  /**
+   * Removes all workers of a given class. The classes are compared using
+   * {@link Class#equals(Object)}. 
+   */
+  void removeWorkersOfClass(Class<? extends AlignCalcWorkerI> cls);
+  
+  /**
+   * Disables a worker so it won't be run during the following restarts.
+   */
+  void disableWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Restores the previously disabled worker back to operation.
+   */
+  void enableWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Checks whether the worker is disabled either due to failure or
+   * disabling it manually.
+   */
+  boolean isDisabled(AlignCalcWorkerI worker);
+  
+  /**
+   * Checks whether the given worker is currently running.
+   */
+  boolean isWorking(AlignCalcWorkerI worker);
+  
+  /**
+   * Checks whether the currently running worker (if any) is processing
+   * the given annotation.
+   */
+  boolean isWorkingWithAnnotation(AlignmentAnnotation annot);
+  
+  /**
+   * Checks whether this manager is running a worker.
+   */
+  boolean isWorking();
+  
+  /**
+   * Scheduler the worker for one-time execution. The worker does not need
+   * to be registered with this manager and will be scheduler regardless
+   * of being disabled. If the worker has already been scheduled, the
+   * previous one will be cancelled.
+   */
+  void startWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Schedules all registered and not-disabled workers for next execution.
+   */
+  void restartWorkers();
+  
+  /**
+   * Cancels the execution of the worker. Note, if the worker is already
+   * running, this method may, but doesn't have to, interrupt it in
+   * the middle of the work.
+   */
+  void cancelWorker(AlignCalcWorkerI worker);
+  
+  /**
+   * Connect the listener of the worker state changes.
+   */
+  void addAlignCalcListener(AlignCalcListener listener);
+  
+  /**
+   * Remove previously registered worker listener.
+   */
+  void removeAlignCalcListener(AlignCalcListener listener);
+  
+  /**
+   * Stops the manager from running new jobs and cleans-up all
+   * resources such as threads and thread pools.
+   */
+  void shutdown();
+}
index 85157c4..b51b94e 100644 (file)
  */
 package jalview.api;
 
+import java.util.concurrent.Callable;
+
 import jalview.datamodel.AlignmentAnnotation;
 
 /**
  * Interface describing a worker that calculates alignment annotation(s). The
  * main (re-)calculation should be performed by the inherited run() method.
  */
-public interface AlignCalcWorkerI extends Runnable
+public interface AlignCalcWorkerI
 {
   /**
    * Answers true if this worker updates the given annotation (regardless of its
@@ -39,8 +41,8 @@ public interface AlignCalcWorkerI extends Runnable
 
   /**
    * Updates the display of calculated annotation values (does not recalculate
-   * the values). This allows ÃŸquick redraw of annotations when display settings
-   * are changed.
+   * the values). This allows a quick redraw of annotations when display
+   * settings are changed.
    */
   void updateAnnotation();
 
@@ -48,7 +50,13 @@ public interface AlignCalcWorkerI extends Runnable
    * Removes any annotation(s) managed by this worker from the alignment
    */
   void removeAnnotation();
-
+  
+  /**
+   * The main calculation happens here
+   * @throws Throwable 
+   */
+  public void run() throws Throwable;
+  
   /**
    * Answers true if the worker should be deleted entirely when its annotation
    * is deleted from the display, or false if it should continue to run. Some
index d15c5fb..20a9d02 100644 (file)
@@ -56,7 +56,7 @@ public interface AlignViewportI extends ViewStyleI
    * 
    * @return
    */
-  public ViewportRanges getRanges();
+  ViewportRanges getRanges();
 
   /**
    * calculate the height for visible annotation, revalidating bounds where
@@ -64,7 +64,7 @@ public interface AlignViewportI extends ViewStyleI
    * 
    * @return total height of annotation
    */
-  public int calcPanelHeight();
+  int calcPanelHeight();
 
   /**
    * Answers true if the viewport has at least one column selected
@@ -88,6 +88,12 @@ public interface AlignViewportI extends ViewStyleI
 
   boolean isNormaliseSequenceLogo();
 
+  boolean isShowInformationHistogram();
+
+  boolean isShowHMMSequenceLogo();
+
+  boolean isNormaliseHMMSequenceLogo();
+
   ColourSchemeI getGlobalColourScheme();
 
   /**
@@ -115,6 +121,8 @@ public interface AlignViewportI extends ViewStyleI
 
   boolean isIgnoreGapsConsensus();
 
+  boolean isIgnoreBelowBackground();
+
   boolean isCalculationInProgress(AlignmentAnnotation alignmentAnnotation);
 
   AlignmentAnnotation getAlignmentQualityAnnot();
@@ -159,7 +167,7 @@ public interface AlignViewportI extends ViewStyleI
    * 
    * @return
    */
-  AlignCalcManagerI getCalcManager();
+  AlignCalcManagerI2 getCalcManager();
 
   /**
    * get the percentage gaps allowed in a conservation calculation
@@ -500,9 +508,21 @@ public interface AlignViewportI extends ViewStyleI
   @Override
   void setProteinFontAsCdna(boolean b);
 
-  TreeModel getCurrentTree();
+  void setHmmProfiles(ProfilesI info);
 
-  void setCurrentTree(TreeModel tree);
+  ProfilesI getHmmProfiles();
+
+  /**
+   * Registers and starts a worker thread to calculate Information Content
+   * annotation, if it is not already registered
+   * 
+   * @param ap
+   */
+  void initInformationWorker(AlignmentViewPanel ap);
+
+  boolean isInfoLetterHeight();
+
+  public abstract TreeModel getCurrentTree();
 
   /**
    * Answers a data bean containing data for export as configured by the
@@ -513,6 +533,8 @@ public interface AlignViewportI extends ViewStyleI
    */
   AlignmentExportData getAlignExportData(AlignExportSettingsI options);
 
+  public abstract void setCurrentTree(TreeModel tree);
+
   /**
    * @param update
    *          - set the flag for updating structures on next repaint
index c8a835a..8379781 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.api;
 
+import jalview.datamodel.features.FeatureMatcherSetI;
+
 import java.util.Comparator;
 
 /**
@@ -70,6 +72,15 @@ public interface FeatureSettingsModelI extends Comparator<String>
   FeatureColourI getFeatureColour(String type);
 
   /**
+   * Returns any filters defined for the feature type, or null if not known
+   * 
+   * @param type
+   * @return
+   */
+
+  FeatureMatcherSetI getFeatureFilters(String type);
+
+  /**
    * Returns the transparency value, from 0 (fully transparent) to 1 (fully
    * opaque)
    * 
diff --git a/src/jalview/api/JalviewJSApi.java b/src/jalview/api/JalviewJSApi.java
new file mode 100644 (file)
index 0000000..895fd15
--- /dev/null
@@ -0,0 +1,412 @@
+package jalview.api;
+
+import java.net.URL;
+
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+
+/**
+ * JAL-3369 JalviewJS API BH 2019.07.17
+ * 
+ * @author hansonr
+ *
+ */
+public interface JalviewJSApi
+{
+
+  /**
+   * bind a pdb file to a sequence in the given AlignFrame.
+   * 
+   * @param sequenceId
+   *          - sequenceId within the dataset or null
+   * @param pdbId
+   *          - the four-character PDB ID
+   * @param pdbFile
+   *          - pdb file - either a URL or a valid PDB file or null.
+   * @param alFrame
+   *          - null or specific AlignFrame. This specifies the dataset that
+   *          will be searched for a seuqence called sequenceId
+   * 
+   * @return true if binding was success
+   */
+  public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile,
+          AlignFrame alFrame);
+
+  /**
+   * Get alignment as format with or without the jalview start-end sequence
+   * suffix appended.
+   * 
+   * @param format
+   * @param addSuffix
+   *          (default false)
+   * @param alf
+   *          (default current)
+   * 
+   * @return
+   */
+  public String getAlignment(String format, boolean addSuffix,
+          AlignFrame alf);
+
+  /**
+   * Get an array of sequence IDs reflecting the order of the alignment in the
+   * specified alignment frame
+   * 
+   * @param alf
+   *          (default current)
+   * @return array of sequence IDs
+   */
+  public String[] getAlignmentOrder(AlignFrame alf);
+
+  /**
+   * Get alignment view alf's annotation as an annotation file
+   * 
+   * @param alf
+   *          (default current)
+   * @return annotation
+   */
+  public String getAnnotation(AlignFrame alf);
+
+  /**
+   * Get the URL for the location where the code is found; typically ending in
+   * "swingjs/j2s".
+   * 
+   * @return web page URL
+   */
+  public URL getCodeBase();
+
+  AlignFrame getCurrentAlignFrame();
+
+  /**
+   * Get the URL for the hosting web page.
+   * 
+   * @return web page URL
+   */
+  public URL getDocumentBase();
+
+  /**
+   * Get the array of feature groups for an alignment frame.
+   * 
+   * @param alf
+   *          AlignFrame to get feature groups for (default current)
+   * @return
+   */
+  public String[] getFeatureGroups(AlignFrame alf);
+
+  /**
+   * Get the array of feature groups for an alignment frame with a specific
+   * on/off state.
+   * 
+   * @param visible
+   *          (default off)
+   * @param alf
+   *          align frame (default current)
+   * 
+   * @return
+   */
+  public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf);
+
+  /**
+   * Get the sequence features in the alignment frame in the given format
+   * (Jalview or GFF). Two additional options can be added to the format, each
+   * starting with a semicolon:
+   * 
+   * ;includeNonpositional (default) or ;positionalOnly
+   * 
+   * ;includeComplement
+   * 
+   * @param format
+   *          case-insensitive "Jalview" or "GFF" (default "GFF")
+   * @param alf
+   *          (default current)
+   * @return formatted sequence features
+   */
+  public String getFeatures(String format, AlignFrame alf);
+
+  /**
+   * Get an applet parameter as a string.
+   * 
+   * @param name
+   * @return value or null
+   */
+  public String getParameter(String name);
+
+  /**
+   * Get an applet parameter object value.
+   * 
+   * @param name
+   * @return value or null
+   */
+  public Object getParameterAsObject(String name);
+
+  /**
+   * @param alf
+   *          AlignFrame containing selection
+   * @return String list of selected sequence IDs, each terminated by current
+   *         default separator sequence
+   * 
+   */
+  public SequenceI[] getSelectedSequences(AlignFrame alf);
+
+  // BH incompatibility here -- JalviewLite created an AlignFrame; Jalview
+  // creates an AlignmentPanel
+  // /**
+  // * create a new view and return the AlignFrame instance
+  // *
+  // * @return
+  // */
+  //
+  // public AlignFrame newView();
+  //
+  // /**
+  // * create a new view named name and return the AlignFrame instance
+  // *
+  // * @param name
+  // * @return
+  // */
+  //
+  // public AlignFrame newView(String name);
+  //
+  // /**
+  // * create a new view on alf and return the AlignFrame instance
+  // *
+  // * @param alf
+  // * @return
+  // */
+  // public AlignFrame newViewFrom(AlignFrame alf);
+  //
+  // /**
+  // * create a new view named name on alf
+  // *
+  // * @param alf
+  // * @param name
+  // * @return
+  // */
+  // public AlignFrame newViewFrom(AlignFrame alf, String name);
+  //
+
+  /**
+   * get sequences selected in alf and return their alignment in format 'format'
+   * either with or without suffix
+   * 
+   * @param format
+   *          - format of alignment file
+   * @param alf
+   *          - where selection is
+   * @param suffix
+   *          - true to append /start-end string to each sequence ID
+   * 
+   * @return selected sequences as flat file or empty string if there was no
+   *         current selection
+   */
+  public String getSelectedSequencesAsAlignment(String format,
+          boolean addSuffix, AlignFrame alf);
+
+  /**
+   * 
+   * @param sequenceId
+   *          id of sequence to highlight
+   * @param position
+   *          integer position [ tobe implemented or range ] on sequence
+   * @param alignedPosition
+   *          false, blank or something else - indicate if position is an
+   *          alignment column or unaligned sequence position
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void highlight(String sequenceId, String position,
+          String alignedPosition, AlignFrame alf);
+
+  /**
+   * 
+   * @param data
+   *          alignment data as a string
+   * @param title
+   *          window title
+   * @param width
+   *          desired width or 0 for default width
+   * @param height
+   *          desired height or 0 for default height
+   * @return null or new alignment frame
+   */
+
+  public AlignFrame loadAlignment(String data, String title, int width,
+          int height);
+
+  /**
+   * add the given features or annotation to the given alignment view
+   * 
+   * @param annotation
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void loadAnnotation(String annotation, AlignFrame alf);
+
+  /**
+   * Parse the given string as a jalview feature or GFF annotation file and
+   * optionally enable feature display on the given AlignFrame.
+   * 
+   * @param features
+   *          - gff or features file
+   * @param autoenabledisplay
+   *          - when true, feature display will be enabled if any features can
+   *          be parsed from the string.
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return true if data parsed as features
+   */
+  public boolean loadFeatures(String features, boolean autoenabledisplay,
+          AlignFrame alf);
+
+  /**
+   * Load a score file.
+   * 
+   * @param sScoreFile
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return
+   */
+  public boolean loadScoreFile(String sScoreFile, AlignFrame alf);
+
+  /**
+   * public static method for JalviewJS API to open a PCAPanel without
+   * necessarily using a dialog.
+   * 
+   * @param modelName
+   * @param alf
+   *          may be null
+   * 
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  public Object openPcaPanel(String modelName, AlignFrame alf);
+
+  /**
+   * Open a new Tree panel on the desktop statically. Params are standard (not
+   * set by Groovy). No dialog is opened.
+   * 
+   * @param treeType
+   * @param modelName
+   * @param alf
+   *          align frame (default current)
+   * 
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  public Object openTreePanel(String treeType, String modelName,
+          AlignFrame alf);
+
+  /**
+   * re-order the given alignment using the given array of sequence IDs
+   * 
+   * @param ids
+   *          array of sequence IDs
+   * @param undoName
+   *          - string to use when referring to ordering action in undo buffer
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return 'true' if alignment was actually reordered. empty string if
+   *         alignment did not contain sequences.
+   */
+  public boolean orderAlignment(String[] ids, String undoName,
+          AlignFrame alf);
+
+  /**
+   * process commandline arguments after the JavaScript application has started
+   *
+   * @param args
+   * @return
+   */
+  public Object parseArguments(String[] args);
+
+  boolean parseFeaturesFile(String filename, AlignFrame alf);
+
+  // Bob's additions:
+
+  /**
+   * remove any callback using the given listener function and associated with
+   * the given AlignFrame (or null for all callbacks);
+   * 
+   * @param listener
+   *          (may be null);
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void removeSelectionListener(String listener, AlignFrame af);
+
+  /**
+   * adjust horizontal/vertical scroll to make the given location the top left
+   * hand corner for the given view
+   * 
+   * @param topRow
+   *          -1 for current top row
+   * @param leftHandColumn
+   *          -1 for current left-hand column
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf);
+  /**
+   * select regions of the given alignment frame
+   * 
+   * @param alf
+   *          alignment frame (default current)
+   * @param toselect
+   *          String separated list { column range, seq1...seqn sequence ids }
+   * @param sep
+   *          separator between toselect fields
+   */
+  public void select(String[] sequenceIds, String[] columns,
+          AlignFrame alf);
+  
+  /**
+   * Set the state (visible or not) of the selected feature groups.
+   * 
+   * @param groups
+   * @param state
+   * @param alf
+   *          (default current)
+   */
+  public void setFeatureGroupState(String[] groups, boolean state,
+          AlignFrame alf);
+
+  /**
+   * Register a JavaScript function to handle alignment selection events. Events
+   * are generated when the user completes a selection event, or when the user
+   * deselects all selected regions. The method is called back with the
+   * following arguments:
+   * 
+   * the appletID (such as "Jalview1")
+   * 
+   * the source alignment frame
+   * 
+   * the SelectionSource object (for example, an AlignViewport)
+   * 
+   * the sequence set ID
+   * 
+   * an array of sequence IDs
+   * 
+   * an array of columns (single number or hyphenated range)
+   * 
+   * @param listener
+   *          name of JavaScript function to be called
+   * 
+   * @param alf
+   *          alignment frame (default ALL)
+   */
+  public void setSelectionListener(String listener, AlignFrame alf);
+
+  public void showOverview();
+
+  /**
+   * 
+   * @param pdbID
+   * @param fileType
+   * @param alf
+   *          align frame (default current)
+   */
+  public void showStructure(String pdbID, String fileType, AlignFrame alf);
+
+}
diff --git a/src/jalview/api/PollableAlignCalcWorkerI.java b/src/jalview/api/PollableAlignCalcWorkerI.java
new file mode 100644 (file)
index 0000000..25074ec
--- /dev/null
@@ -0,0 +1,18 @@
+package jalview.api;
+
+public interface PollableAlignCalcWorkerI extends AlignCalcWorkerI
+{
+  @Override
+  public default void run() throws Throwable
+  {
+    startUp();
+  }
+  
+  public void startUp() throws Throwable;
+  
+  public boolean poll() throws Throwable;
+  
+  public void cancel();
+  
+  public void done();
+}
index b860a36..7cf6529 100644 (file)
@@ -773,8 +773,7 @@ public class APopupMenu extends java.awt.PopupMenu
 
           ap.alignFrame.addHistoryItem(editCommand);
 
-          ap.av.firePropertyChange("alignment", null,
-                  ap.av.getAlignment().getSequences());
+          ap.av.notifyAlignment();
         }
       }
     }
@@ -811,8 +810,7 @@ public class APopupMenu extends java.awt.PopupMenu
 
         ap.alignFrame.addHistoryItem(caseCommand);
 
-        ap.av.firePropertyChange("alignment", null,
-                ap.av.getAlignment().getSequences());
+        ap.av.notifyAlignment();
 
       }
     }
index 2a2fc70..63ba101 100644 (file)
@@ -236,6 +236,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             alignPanel);
     viewport.updateConservation(alignPanel);
     viewport.updateConsensus(alignPanel);
+    viewport.initInformationWorker(alignPanel);
 
     displayNonconservedMenuItem.setState(viewport.getShowUnconserved());
     followMouseOverFlag.setState(viewport.isFollowHighlight());
@@ -882,7 +883,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (source == autoCalculate)
     {
-      viewport.autoCalculateConsensus = autoCalculate.getState();
+      viewport.setAutoCalculateConsensusAndConservation(autoCalculate.getState());
     }
     else if (source == sortByTree)
     {
@@ -1324,6 +1325,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       sortGroupMenuItem_actionPerformed();
     }
+    else if (source == sortEValueMenuItem)
+    {
+      sortEValueMenuItem_actionPerformed();
+    }
+    else if (source == sortBitScoreMenuItem)
+    {
+      sortBitScoreMenuItem_actionPerformed();
+    }
     else if (source == removeRedundancyMenuItem)
     {
       removeRedundancyMenuItem_actionPerformed();
@@ -1700,8 +1709,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
                                           // viewport.getColumnSelection().getHiddenColumns()
                                           // != null;
     updateEditMenuBar();
-    originalSource.firePropertyChange("alignment", null,
-            originalSource.getAlignment().getSequences());
+    originalSource.notifyAlignment();
   }
 
   /**
@@ -1733,8 +1741,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
                                           // != null;
 
     updateEditMenuBar();
-    originalSource.firePropertyChange("alignment", null,
-            originalSource.getAlignment().getSequences());
+    originalSource.notifyAlignment();
   }
 
   AlignmentViewport getOriginatingSource(CommandI command)
@@ -2083,8 +2090,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight() - 1); // BH
                                                                              // 2019.04.18
     viewport.getAlignment().getWidth();
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2158,8 +2164,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.setSelectionGroup(null);
     viewport.getAlignment().deleteGroup(sg);
 
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
     if (viewport.getAlignment().getHeight() < 1)
     {
@@ -2371,7 +2376,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
         }
       }
 
-      viewport.firePropertyChange("alignment", null, al.getSequences());
+      viewport.notifyAlignment();
     }
   }
 
@@ -2415,7 +2420,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
     ranges.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null, al.getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2449,7 +2454,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     ranges.setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null, al.getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2773,6 +2778,26 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   }
 
+  public void sortEValueMenuItem_actionPerformed()
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByEValue(viewport.getAlignment());
+    addHistoryItem(new OrderCommand("Group Sort", oldOrder,
+            viewport.getAlignment()));
+    alignPanel.paintAlignment(true, false);
+
+  }
+
+  public void sortBitScoreMenuItem_actionPerformed()
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByBitScore(viewport.getAlignment());
+    addHistoryItem(new OrderCommand("Group Sort", oldOrder,
+            viewport.getAlignment()));
+    alignPanel.paintAlignment(true, false);
+
+  }
+
   public void removeRedundancyMenuItem_actionPerformed()
   {
     new RedundancyPanel(alignPanel);
@@ -3123,6 +3148,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   MenuItem sortGroupMenuItem = new MenuItem();
 
+  MenuItem sortEValueMenuItem = new MenuItem();
+
+  MenuItem sortBitScoreMenuItem = new MenuItem();
+
   MenuItem removeRedundancyMenuItem = new MenuItem();
 
   MenuItem pairwiseAlignmentMenuItem = new MenuItem();
index d64cd75..7369c3c 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
  * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
  * 
  * This file is part of Jalview.
  * 
@@ -54,10 +54,16 @@ public class AlignViewport extends AlignmentViewport
 
   private AnnotationColumnChooser annotationColumnSelectionState;
 
+  java.awt.Frame nullFrame;
+
+  protected FeatureSettings featureSettings = null;
+
+  private float heightScale = 1, widthScale = 1;
+
   public AlignViewport(AlignmentI al, JalviewLite applet)
   {
     super(al);
-    calculator = new jalview.workers.AlignCalcManager();
+    calculator = new jalview.workers.AlignCalcManager2();
     this.applet = applet;
 
     // we always pad gaps
@@ -196,7 +202,7 @@ public class AlignViewport extends AlignmentViewport
                 .getColourScheme(this, alignment, colour));
         if (residueShading != null)
         {
-          residueShading.setConsensus(hconsensus);
+          residueShading.setConsensus(consensusProfiles);
         }
       }
 
@@ -207,15 +213,8 @@ public class AlignViewport extends AlignmentViewport
       }
     }
     initAutoAnnotation();
-
   }
 
-  java.awt.Frame nullFrame;
-
-  protected FeatureSettings featureSettings = null;
-
-  private float heightScale = 1, widthScale = 1;
-
   /**
    * {@inheritDoc}
    */
@@ -295,17 +294,6 @@ public class AlignViewport extends AlignmentViewport
             .getStructureSelectionManager(applet);
   }
 
-  @Override
-  public boolean isNormaliseSequenceLogo()
-  {
-    return normaliseSequenceLogo;
-  }
-
-  public void setNormaliseSequenceLogo(boolean state)
-  {
-    normaliseSequenceLogo = state;
-  }
-
   /**
    * 
    * @return true if alignment characters should be displayed
@@ -351,7 +339,7 @@ public class AlignViewport extends AlignmentViewport
     if (mappedCommand != null)
     {
       mappedCommand.doCommand(null);
-      firePropertyChange("alignment", null, getAlignment().getSequences());
+      notifyAlignment();
 
       // ap.scalePanelHolder.repaint();
       // ap.repaint();
index fd75296..5eb39a4 100644 (file)
@@ -555,8 +555,8 @@ public class AlignmentPanel extends Panel
     // this is called after loading new annotation onto alignment
     if (alignFrame.getSize().height == 0)
     {
-      System.out.println(
-              "adjustAnnotationHeight frame size zero NEEDS FIXING");
+      // panel not laid out yet?
+      return;
     }
     fontChanged();
     validateAnnotationDimensions(true);
index a0102b9..9cd2b51 100755 (executable)
@@ -534,7 +534,7 @@ public class AnnotationLabels extends Panel
                     MessageManager.getString("label.ignore_gaps_consensus"),
                     (aa[selectedRow].groupRef != null)
                             ? aa[selectedRow].groupRef
-                                    .getIgnoreGapsConsensus()
+                                                                                               .getIgnoreGapsConsensus()
                             : ap.av.isIgnoreGapsConsensus());
             final AlignmentAnnotation aaa = aa[selectedRow];
             cbmi.addItemListener(new ItemListener()
index 34e8b44..46b4015 100755 (executable)
@@ -51,6 +51,17 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.beans.PropertyChangeEvent;
 
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.AnnotationRenderer;
+import jalview.renderer.AwtRenderPanelI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 public class AnnotationPanel extends Panel
         implements AwtRenderPanelI, AdjustmentListener, ActionListener,
         MouseListener, MouseMotionListener, ViewportListenerI
index 71b3ac7..473b3a5 100755 (executable)
@@ -71,7 +71,7 @@ public class OverviewPanel extends Panel implements Runnable,
 
     od = new OverviewDimensionsShowHidden(av.getRanges(),
             (av.isShowAnnotation()
-                    && av.getSequenceConsensusHash() != null));
+                                               && av.getSequenceConsensusHash() != null));
 
     oviewCanvas = new OverviewCanvas(od, av);
     setLayout(new BorderLayout());
index bd36b0d..6488c61 100644 (file)
@@ -227,8 +227,7 @@ public class RedundancyPanel extends SliderPanel
       ap.alignFrame.addHistoryItem(cut);
 
       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
   }
@@ -243,8 +242,7 @@ public class RedundancyPanel extends SliderPanel
     {
       ap.av.getHistoryList().remove(command);
       ap.alignFrame.updateEditMenuBar();
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
     ap.paintAlignment(true, true);
index 70366e4..dd68f88 100644 (file)
@@ -132,8 +132,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     if (editCommand != null && editCommand.getSize() > 0)
     {
       ap.alignFrame.addHistoryItem(editCommand);
-      av.firePropertyChange("alignment", null,
-              av.getAlignment().getSequences());
+      av.notifyAlignment();
     }
 
     startseq = -1;
index feeaf83..8c72f64 100644 (file)
  */
 package jalview.appletgui;
 
-import java.awt.Frame;
 import java.awt.Graphics;
 import java.awt.Insets;
-import java.awt.Label;
 import java.awt.Panel;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
index 2e5f938..5280d84 100755 (executable)
@@ -715,8 +715,10 @@ public class TreeCanvas extends Panel
     ap.updateAnnotation();
     if (av.getCodingComplement() != null)
     {
-      ((AlignmentViewport) av.getCodingComplement()).firePropertyChange(
-              "alignment", null, ap.av.getAlignment().getSequences());
+      ((AlignmentViewport) av.getCodingComplement()).notifyAlignment();
+      // Technically, the property change is not the same because av is not necessarily getCodingComplement(), 
+      // but this is the appletgui, so I am not going to worry about it. BH
+      //.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
     }
   }
 
similarity index 99%
rename from src/jalview/javascript/JSFunctionExec.java
rename to src/jalview/appletgui/js/JSFunctionExec.java
index 29f3fa9..247552b 100644 (file)
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.bin.JalviewLite;
 
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 
@@ -455,7 +455,7 @@ public interface JalviewLiteJsApi
    *          - separator separated list of PDB file URIs that this viewer is
    *          handling. These files must be in the same order they appear in
    *          Jmol (e.g. first one is frame 1, second is frame 2, etc).
-   * @see jalview.javascript.MouseOverStructureListener
+   * @see jalview.appletgui.js.MouseOverStructureListener
    */
   public abstract void setStructureListener(String listener,
           String modelSet);
similarity index 97%
rename from src/jalview/javascript/JsCallBack.java
rename to src/jalview/appletgui/js/JsCallBack.java
index 76d6e8d..2fe4dac 100644 (file)
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 public interface JsCallBack
 {
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 import jalview.bin.JalviewLite;
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 import jalview.bin.JalviewLite;
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.appletgui.AlignFrame;
+import jalview.appletgui.js.JsCallBack;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.SequenceI;
 import jalview.ext.jmol.JmolCommands;
diff --git a/src/jalview/bin/AppletParams.java b/src/jalview/bin/AppletParams.java
new file mode 100644 (file)
index 0000000..726734d
--- /dev/null
@@ -0,0 +1,479 @@
+
+package jalview.bin;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jalview.gui.Preferences;
+
+/**
+ * Collection of all known applet tags from JalviewLite.
+ * Three cases; can be one or more of these:
+ * 
+ * CASE I. args[] name and value for ArgsParser
+ * 
+ * CASE II. applet parameter for JalviewJSApp
+ * 
+ * CASE III. mapped to a Preference
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+@SuppressWarnings("serial")
+public class AppletParams extends HashMap<String, Object>
+{
+
+  private final static String[] params = { 
+      "alignpdbfiles",
+      "ANNOTATIONCOLOUR_MAX", "ANNOTATIONCOLOUR_MIN",
+      "annotations",
+      "APPLICATION_URL", "automaticScrolling", "centrecolumnlabels",
+      "debug", "defaultColour", "defaultColourNuc", "defaultColourProt",
+      "embedded", "enableSplitFrame", "externalstructureviewer", "features",
+      "file", "file2", "format", "heightScale", "hidefeaturegroups",
+      "jalviewhelpurl", "jnetfile", "jpredfile", "label", "linkLabel_",
+      "linkLabel_1", "linkURL_", "nojmol", "normaliseLogo",
+      "normaliseSequenceLogo", "oninit", "PDBFILE", "PDBSEQ",
+      "relaxedidmatch", "resolvetocodebase", "RGB", "scaleProteinAsCdna",
+      "scoreFile", "separator", "sequence", "showAnnotation", "showbutton",
+      "showConsensus", "showConsensusHistogram", "showConservation",
+      "showfeaturegroups", "showFeatureSettings", "showFullId",
+      "showGroupConsensus", "showGroupConservation", "showOccupancy",
+      "showQuality", "showSequenceLogo", "showTreeBootstraps",
+      "showTreeDistances", "showUnconserved", "showUnlinkedTreeNodes",
+      "sortBy", "sortByTree", "tree", "treeFile", "upperCase",
+      "userDefinedColour", "widthScale", "windowHeight", "windowWidth",
+      "wrap", };
+
+  public String getParam(String param, String def)
+  {
+    Object val = get(param);
+    return (val != null ? val.toString() : def);
+  }
+
+  // <applet
+  // code="jalview.bin.JalviewLite" width="140" height="35"
+  // archive="jalviewApplet.jar,JmolApplet-14.6.4_2016.10.26.jar,java-json.jar,json_simple-1.1.jar">
+  // <param name="permissions" value="sandbox"/>
+  // <param name="file" value="uniref50.fa"/>
+  // <param name="treeFile" value="ferredoxin.nw"/>
+  // <param name="userDefinedColour" value="C=yellow; R,K,H=FF5555;
+  // D,E=5555FF"/>
+  // <param name="sortByTree" value="True"/>
+  // <param name="showSequenceLogo" value="true"/>
+  // <param name="showGroupConsensus" value="true"/>
+  // <param name="showFullId" value="false"/>
+  // <param name="linkLabel_1" value="Uniprot"/>
+  // <param name="linkUrl_1"
+  // value="http://www.uniprot.org/uniprot/$SEQUENCE_ID$"/>
+  // <param name="linkLabel_2" value="EMBL-EBI Search"/>
+  // <param name="linkUrl_2"
+  // value="http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$"/>
+  // <param name="APPLICATION_URL"
+  // value="http://www.jalview.org/services/launchApp"/>
+  // </applet>
+  //
+  public AppletParams(String outerHTML)
+  {
+    String[] tokens = outerHTML.split("<param");
+    outerHTML = tokens[0];
+    String code = getAttr(outerHTML, "code");
+    if (!code.equals("jalview.bin.JalviewLite"))
+    {
+      return;
+    }
+    for (int i = tokens.length; --i > 0;)
+    {
+      String param = tokens[i];
+      String key = getAttr(param, "name");
+      if (key != null)
+      {
+        String value = getAttr(param, "value");
+        System.out.println("AppletParams " + key + " = \"" + value + "\"");
+        put(key, value);
+      }
+    }
+    put("_width", getAttr(outerHTML, "width"));
+    put("_height", getAttr(outerHTML, "height"));
+    put("_id", getAttr(outerHTML, "id"));
+    put("_name", getAttr(outerHTML, "name"));
+    put("_archive", getAttr(outerHTML, "archive"));
+    put("_code", code);
+  }
+
+  public AppletParams()
+  {
+  }
+
+  public static AppletParams getAppletParams(Map<String, Object> map,
+          List<String> vargs)
+  {
+    AppletParams appletParams = new AppletParams();
+    String resourcePath = getString(map, "resourcePath");
+    if (resourcePath == null)
+      resourcePath = "";
+    if (resourcePath.length() > 0 && !resourcePath.endsWith("/"))
+    {
+      resourcePath += "/";
+    }
+    for (int i = params.length; --i >= 0;)
+    {
+      String prefName = params[i];
+      Object value = map.get(prefName);
+      if (value != null)
+        addParam(vargs, prefName, value, appletParams, resourcePath);
+    }
+    return appletParams;
+  }
+
+  private static String getString(Map<String, Object> map, String key)
+  {
+    Object o = map.get(key);  
+    return (o == null ? null : o.toString());
+  }
+
+  public static AppletParams getAppletParams(String[] args,
+          List<String> vargs)
+  {
+    AppletParams appletParams = new AppletParams();
+    String resourcePath = "";
+    for (int i = args.length; --i > 0;) // > 0 is correct, not >=0
+    {
+      if (args[i].startsWith("name=\"Info.resourcePath\""))
+      {
+        resourcePath = getAttr(args[i], "value");
+        if (resourcePath.length() > 0 && !resourcePath.endsWith("/"))
+        {
+          resourcePath += "/";
+        }
+        break;
+      }
+    }
+    for (int i = 1; i < args.length; i++)
+    {
+      String arg = args[i].trim();
+      if (arg.startsWith("name="))
+      {
+        String prefName = getAttr(arg, "name");
+        String value = getAttr(arg, "value");
+        addParam(vargs, prefName, value, appletParams, resourcePath);
+      }
+    }
+    return appletParams;
+  }
+
+  private static void addParam(List<String> vargs, String prefName,
+          Object value, AppletParams appletParams, String resourcePath)
+  {
+
+    // note that Application arguments ARE case-sensitive, but
+    // Applet.getParameter() is not.
+
+    // prefName // CASE III
+
+    String argName = null;  // CASE I                
+
+    String appletName = prefName.toLowerCase();   // CASE II
+
+    // by nulling one or more of these names, that route will not be used.
+    
+    switch (appletName)
+    {
+
+    case "file":
+      argName = "open";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+    case "file2":
+      argName = "open2";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+    case "oninit":
+    case "hidefeaturegroups":
+      // applet parameter only
+      // setting argName to null indicates that we want
+      // JalviewJSApp to take care of this using getParameter or getParameterAsObject      
+     prefName = argName = null;
+      break;
+    case "tree":
+    case "treefile":
+      // setting appletName to null indicates that we want
+      // Jalview.doMain to taken care of this as Jalview args
+      argName = "tree";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+
+    case "features":
+    case "jnetfile":
+    case "jpredfile":
+    case "pdbfile":
+    case "scorefile":
+    case "sequence":
+    case "annotations":
+      prefName = argName = null;
+      value = resourcePath + value;
+      break;
+
+      // non-loading preferences
+
+    case "defaultcolour":
+      prefName = Preferences.DEFAULT_COLOUR;
+      break;
+    case "defaultcolournuc":
+      prefName = Preferences.DEFAULT_COLOUR_NUC;
+      break;
+    case "defaultcolourprot":
+      prefName = Preferences.DEFAULT_COLOUR_PROT;
+      break;
+    case "annotationcolour_max":
+      prefName = Preferences.ANNOTATIONCOLOUR_MAX;
+      break;
+    case "annotationcolour_min":
+      prefName = Preferences.ANNOTATIONCOLOUR_MIN;
+      break;
+    case "enablesplitframe":
+      prefName = Preferences.ENABLE_SPLIT_FRAME;
+      break;
+    case "centrecolumnlabels":
+      prefName = Preferences.CENTRE_COLUMN_LABELS;
+      break;
+    case "sortby":
+      prefName = Preferences.SORT_ALIGNMENT; // id, etc.
+      break;
+    case "normalisesequencelogo":
+      prefName = Preferences.NORMALISE_CONSENSUS_LOGO;
+      break;
+    case "relaxedidmatch":
+      prefName = Preferences.RELAXEDSEQIDMATCHING;
+      break;
+    case "scaleproteinascdna":
+      prefName = Preferences.SCALE_PROTEIN_TO_CDNA;
+      break;
+    case "userdefinedcolour":
+      argName = "colour";
+      prefName = Preferences.USER_DEFINED_COLOURS;
+      break;
+    case "wrap":
+      prefName = Preferences.WRAP_ALIGNMENT;
+      break;
+    case "sortbytree":
+      argName = prefName;
+      prefName = Preferences.SORT_BY_TREE;
+      value = checkTF(value);
+      break;
+
+    // implemented; not tested:
+
+    case "pdbseq":
+    case "alignpdbfiles":
+      prefName = null;
+      break;
+    case "format":
+      argName = prefName;
+      break;
+    case "separator":
+      argName = prefName;
+      break;
+
+    // TODO: probably not relevant?
+
+    case "rgb":
+      prefName = null; // TODO no background for application?
+      break;
+    case "externalstructureviewer":
+      break;
+    case "application_url":
+      break;
+    case "automaticscrolling":
+      break;
+    case "heightscale":
+      break;
+    case "jalviewhelpurl":
+      break;
+    case "label":
+      break;
+    case "linklabel_":
+      prefName = "linkLabel_";
+      break;
+    case "linklabel_1":
+      prefName = "linkLabel_1";
+      break;
+    case "linkurl_":
+      prefName = "linkURL_";
+      break;
+
+    // unknown:
+
+    case "nojmol":
+    case "normaliselogo":
+    case "resolvetocodebase":
+    case "uppercase":
+    case "widthscale":
+    case "windowheight":
+    case "windowwidth":
+      prefName = null;
+      break;
+
+    // TRUE/FALSE
+
+    case "debug":
+    case "embedded":
+    case "showbutton":
+      value = checkTF(value);
+      break;
+    case "showannotation":
+      prefName = Preferences.SHOW_ANNOTATIONS;
+      value = checkTF(value);
+      break;
+    case "showconsensus":
+      prefName = Preferences.SHOW_CONSENSUS_LOGO;
+      value = checkTF(value);
+      break;
+    case "showconsensushistogram":
+      prefName = Preferences.SHOW_CONSENSUS_HISTOGRAM;
+      value = checkTF(value);
+      break;
+    case "showconservation":
+      prefName = Preferences.SHOW_CONSERVATION;
+      value = checkTF(value);
+      break;
+    case "showgroupconsensus":
+      prefName = Preferences.SHOW_GROUP_CONSENSUS;
+      value = checkTF(value);
+      break;
+    case "showgroupconservation":
+      prefName = Preferences.SHOW_GROUP_CONSERVATION;
+      value = checkTF(value);
+      break;
+    case "showoccupancy":
+      prefName = Preferences.SHOW_OCCUPANCY;
+      value = checkTF(value);
+      break;
+    case "showquality":
+      prefName = Preferences.SHOW_QUALITY;
+      value = checkTF(value);
+      break;
+    case "showsequencelogo":
+      prefName = Preferences.SHOW_CONSENSUS_LOGO;
+      value = checkTF(value);
+      break;
+    case "showunconserved":
+      prefName = Preferences.SHOW_UNCONSERVED;
+      value = checkTF(value);
+      break;
+    case "showfeaturegroups":
+    case "showfeaturesettings":
+    case "showfullid":
+    case "showtreebootstraps":
+    case "showtreedistances":
+    case "showunlinkedtreenodes":
+      value = checkTF(value);
+      break;
+    default:
+      if (appletName.startsWith("pdbfile")
+              || appletName.startsWith("sequence") && Character
+                      .isDigit(appletName.charAt(appletName.length() - 1)))
+      {
+        // could be pdbFile2, for example
+        prefName = argName = null;
+        value = resourcePath + value;
+        break;
+      }
+      // or one of the app preference names
+      break;
+    }
+
+    // CASE I.  args[] name and value for ArgsParser
+    //
+    // If given an argument name, 
+    // put name and value into application args
+    if (value != null && argName != null)
+    {
+      vargs.add(argName);
+      if (value != "true")
+      {
+        vargs.add(value.toString());
+      }
+    }
+    
+    // CASE II.   applet parameter for JalviewJSApp
+    
+    if (value == null)
+    {
+      value = "false";
+    }
+    System.out.println("AppletParams propName=" + prefName + " argName="
+            + argName + " appletName=" + appletName + " value=" + value);    
+    if (appletName != null)
+    {
+      appletParams.put(appletName, value);
+    }
+    
+    // CASE III.  mapped to a Preference
+    
+    if (prefName != null)
+    {
+      Cache.setPropertyNoSave(prefName, value.toString());
+    }
+  }
+
+  /**
+   * Check for a single-argument option.
+   * 
+   * @param value
+   * @return "true" or null
+   */
+  private static String checkTF(Object value)
+  {
+    return (("" + value).toLowerCase() == "true" ? "true" : null);
+  }
+
+  /**
+   * Crude applet innerHTML parser
+   * 
+   * @param tag
+   * @param attr
+   * @return
+   */
+  private static String getAttr(String tag, String attr)
+  {
+    int pt = tag.indexOf(attr + "=\"");
+    if (pt < 0)
+    {
+      System.out
+              .println("AppletParams did not read " + attr + " in " + tag);
+      return null;
+    }
+    // <param name="sortByTree" value="True"/>
+    int pt1 = pt + attr.length() + 2;
+    int pt2 = tag.indexOf("\"", pt1);
+    return (pt < 0 ? null : tag.substring(pt1, pt2));
+  }
+
+  public static void main(String[] args)
+  {
+    new AppletParams("<applet\r\n"
+            + "    code=\"jalview.bin.JalviewLite\" width=\"140\" height=\"35\"\r\n"
+            + "    archive=\"jalviewApplet.jar,JmolApplet-14.6.4_2016.10.26.jar,java-json.jar,json_simple-1.1.jar\">  \r\n"
+            + "  <param name=\"permissions\" value=\"sandbox\"/>\r\n"
+            + "  <param name=\"file\" value=\"uniref50.fa\"/>\r\n"
+            + "  <param name=\"treeFile\" value=\"ferredoxin.nw\"/>\r\n"
+            + "  <param name=\"userDefinedColour\" value=\"C=yellow; R,K,H=FF5555; D,E=5555FF\"/>\r\n"
+            + "  <param name=\"sortByTree\" value=\"True\"/>\r\n"
+            + "  <param name=\"showSequenceLogo\" value=\"true\"/>\r\n"
+            + "  <param name=\"showGroupConsensus\" value=\"true\"/>\r\n"
+            + "  <param name=\"showFullId\" value=\"false\"/>\r\n"
+            + "    <param name=\"linkLabel_1\" value=\"Uniprot\"/>\r\n"
+            + "    <param name=\"linkUrl_1\" value=\"http://www.uniprot.org/uniprot/$SEQUENCE_ID$\"/>\r\n"
+            + "    <param name=\"linkLabel_2\" value=\"EMBL-EBI Search\"/>\r\n"
+            + "    <param name=\"linkUrl_2\" value=\"http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$\"/>\r\n"
+            + "    <param name=\"APPLICATION_URL\" value=\"http://www.jalview.org/services/launchApp\"/>\r\n"
+            + "     </applet>");
+  }
+
+}
\ No newline at end of file
diff --git a/src/jalview/bin/ApplicationSingletonProvider.java b/src/jalview/bin/ApplicationSingletonProvider.java
new file mode 100644 (file)
index 0000000..450809a
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import jalview.util.Platform;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class to hold singleton objects, whose scope (context) is
+ * <ul>
+ * <li>the Java runtime (JVM) when running as Java</li>
+ * <li>one 'applet', when running as JalviewJS</li>
+ * </ul>
+ * This allows separation of multiple JS applets running on the same browser
+ * page, each with their own 'singleton' instances.
+ * <p>
+ * Instance objects are held in a separate Map (keyed by Class) for each
+ * context. For Java, this is just a single static Map. For SwingJS, the map is
+ * stored as a field {@code _swingjsSingletons} of
+ * {@code Thread.currentThread.getThreadGroup()}, as a proxy for the applet.
+ * <p>
+ * Note that when an applet is stopped, its ThreadGroup is removed, allowing any
+ * singleton references to be garbage collected.
+ * 
+ * @author hansonr
+ */
+public class ApplicationSingletonProvider
+{
+  /**
+   * A tagging interface to mark classes whose singleton instances may be served
+   * by {@code ApplicationSingletonProvider}, giving a distinct instance per JS
+   * 'applet'.
+   * <p>
+   * A class whose singleton should have global scope (be shared across all
+   * applets on a page) should <em>not</em> use this mechanism, but just provide
+   * a single instance (class static member) in the normal way.
+   */
+  public interface ApplicationSingletonI
+  {
+  }
+  
+  /*
+   * Map used to hold singletons in JVM context
+   */
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> singletons = new HashMap<>();
+
+  /**
+   * private constructor for non-instantiable class
+   */
+  private ApplicationSingletonProvider()
+  {
+  }
+
+  /**
+   * Returns the singletons map for the current context (JVM for Java,
+   * ThreadGroup for JS), creating the map on the first request for each JS
+   * ThreadGroup
+   * 
+   * @return
+   */
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> getContextMap()
+  {
+    @SuppressWarnings("unused")
+    ThreadGroup g = (Platform.isJS()
+            ? Thread.currentThread().getThreadGroup()
+            : null);
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = singletons;
+    /** @j2sNative map = g._swingjsSingletons; */
+    if (map == null)
+    {
+      map = new HashMap<>();
+      /** @j2sNative g._swingjsSingletons = map; */
+    }
+
+    return map;
+  }
+
+  /**
+   * Answers the singleton instance of the given class for the current context
+   * (JVM or SwingJS 'applet'). If no instance yet exists, one is created, by
+   * calling the class's no-argument constructor. Answers null if any error
+   * occurs (or occurred previously for the same class).
+   * 
+   * @param c
+   * @return
+   */
+  public static ApplicationSingletonI getInstance(Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map.containsKey(c))
+    {
+      /*
+       * singleton already created _or_ creation failed (null value stored)
+       */
+      return map.get(c);
+    }
+
+    /*
+     * create and save the singleton
+     */
+    ApplicationSingletonI o = map.get(c);
+    try
+    {
+      Constructor<? extends ApplicationSingletonI> con = c
+              .getDeclaredConstructor();
+      con.setAccessible(true);
+      o = con.newInstance();
+    } catch (IllegalAccessException | InstantiationException
+            | IllegalArgumentException | InvocationTargetException
+            | NoSuchMethodException | SecurityException e)
+    {
+      Console.error("Failed to create singleton for " + c.toString()
+              + ", error was: " + e.toString());
+      e.printStackTrace();
+    }
+
+    /*
+     * store the new singleton; note that a
+     * null value is saved if construction failed
+     */
+    getContextMap().put(c, o);
+    return o;
+  }
+
+  /**
+   * Removes the current singleton instance of the given class from the current
+   * application context. This has the effect of ensuring that a new instance is
+   * created the next time one is requested.
+   * 
+   * @param c
+   */
+  public static void removeInstance(
+          Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map != null)
+    {
+      map.remove(c);
+    }
+  }
+}
index c927f1f..c5c08f6 100644 (file)
  */
 package jalview.bin;
 
+import jalview.util.Platform;
+
 import java.net.URLDecoder;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Notes: this argParser does not distinguish between parameter switches,
@@ -34,19 +37,105 @@ import java.util.Vector;
  */
 public class ArgsParser
 {
-  Vector<String> vargs = null;
+
+  // BH 2019 - new
+
+  public static final String NOCALCULATION = "nocalculation";
+
+  public static final String NOMENUBAR = "nomenubar";
+
+  public static final String NOSTATUS = "nostatus";
+
+  public static final String SHOWOVERVIEW = "showoverview";
+
+  //
+  public static final String ANNOTATIONS = "annotations";
+
+  public static final String COLOUR = "colour";
+
+  public static final String FEATURES = "features";
+
+  public static final String GROOVY = "groovy";
+
+  public static final String GROUPS = "groups";
+
+  public static final String HEADLESS = "headless";
+
+  public static final String JABAWS = "jabaws";
+
+  public static final String NOANNOTATION = "no-annotation";
+
+  public static final String NOANNOTATION2 = "noannotation"; // BH 2019.05.07
+
+  public static final String NODISPLAY = "nodisplay";
+
+  public static final String NOGUI = "nogui";
+
+  public static final String NONEWS = "nonews";
+
+  public static final String NOQUESTIONNAIRE = "noquestionnaire";
+
+  public static final String NOSORTBYTREE = "nosortbytree";
+
+  public static final String NOUSAGESTATS = "nousagestats";
+
+  public static final String OPEN = "open";
+
+  public static final String OPEN2 = "open2"; // BH added -- for applet
+                                              // compatibility; not fully
+                                              // implemented
+
+  public static final String PROPS = "props";
+
+  public static final String QUESTIONNAIRE = "questionnaire";
+
+  public static final String SETPROP = "setprop";
+
+  public static final String SORTBYTREE = "sortbytree";
+
+  public static final String TREE = "tree";
+
+  public static final String VDOC = "vdoc";
+
+  public static final String VSESS = "vsess";
+
+  private List<String> vargs = null;
+
+  private boolean isApplet;
+
+  private AppletParams appletParams;
+
+  public boolean isApplet()
+  {
+    return isApplet;
+  }
 
   public ArgsParser(String[] args)
   {
-    vargs = new Vector<String>();
-    for (int i = 0; i < args.length; i++)
+    vargs = new ArrayList<>();
+    isApplet = (args.length > 0 && args[0].startsWith("<applet"));
+    if (isApplet)
     {
-      String arg = args[i].trim();
-      if (arg.charAt(0) == '-')
+      appletParams = AppletParams.getAppletParams(args, vargs);
+    }
+    else
+    {
+      if (Platform.isJS())
+
       {
-        arg = arg.substring(1);
+        isApplet = true;
+        appletParams = AppletParams
+                .getAppletParams(Platform.getAppletInfoAsMap(), vargs);
+      }
+      for (int i = 0; i < args.length; i++)
+      {
+        String arg = args[i].trim();
+        if (arg.charAt(0) == '-')
+        {
+          arg = arg.substring(1);
+        }
+        vargs.add(arg);
       }
-      vargs.addElement(arg);
     }
   }
 
@@ -67,9 +156,9 @@ public class ArgsParser
     String dc = null, ret = null;
     if (index != -1)
     {
-      ret = vargs.elementAt(index + 1).toString();
-      vargs.removeElementAt(index);
-      vargs.removeElementAt(index);
+      ret = vargs.get(index + 1).toString();
+      vargs.remove(index);
+      vargs.remove(index);
       if (utf8decode && ret != null)
       {
         try
@@ -95,7 +184,7 @@ public class ArgsParser
   {
     if (vargs.contains(arg))
     {
-      vargs.removeElement(arg);
+      vargs.remove(arg);
       return true;
     }
     else
@@ -114,4 +203,13 @@ public class ArgsParser
     return vargs.size();
   }
 
+  public Object getAppletValue(String key, String def, boolean asString)
+  {
+    Object value;
+    return (appletParams == null ? null
+            : (value = appletParams.get(key.toLowerCase())) == null
+                    ? def : asString ? "" + value
+                    : value);
+  }
+
 }
index 370a243..8a06b7b 100755 (executable)
@@ -42,10 +42,12 @@ import java.util.Locale;
 import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
+import java.util.regex.Pattern;
 
 import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
 
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.PDBEntry;
 import jalview.gui.Preferences;
 import jalview.gui.UserDefinedColours;
@@ -223,8 +225,24 @@ import jalview.ws.sifts.SiftsSettings;
  * @author $author$
  * @version $Revision$
  */
-public class Cache
+public class Cache implements ApplicationSingletonI
 {
+  private Cache()
+  {
+    // private singleton
+  }
+
+  /**
+   * In Java, this will be a static field instance, which will be
+   * application-specific; in JavaScript it will be an applet-specific instance
+   * tied to the applet's ThreadGroup.
+   * 
+   * @return
+   */
+  public static Cache getInstance()
+  {
+    return (Cache) ApplicationSingletonProvider.getInstance(Cache.class);
+  }
   /**
    * property giving log4j level for CASTOR loggers
    */
@@ -243,9 +261,8 @@ public class Cache
   /**
    * Sifts settings
    */
-  public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = System
-          .getProperty("user.home") + File.separatorChar
-          + ".sifts_downloads" + File.separatorChar;
+  public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = Platform.getUserPath(".sifts_downloads/");
+  
 
   private final static String DEFAULT_CACHE_THRESHOLD_IN_DAYS = "2";
 
@@ -254,8 +271,7 @@ public class Cache
   /**
    * Identifiers.org download settings
    */
-  private static final String ID_ORG_FILE = System.getProperty("user.home")
-          + File.separatorChar + ".identifiers.org.ids.json";
+  private static final String ID_ORG_FILE = Platform.getUserPath(".identifiers.org.ids.json");
 
   /**
    * Allowed values are PDB or mmCIF
@@ -278,7 +294,6 @@ public class Cache
   /**
    * Initialises the Jalview Application Log
    */
-
   public final static String JALVIEW_LOGGER_NAME = "JalviewLogger";
 
   // save the proxy properties set at startup
@@ -304,7 +319,7 @@ public class Cache
   public static char[] proxyAuthPassword = null;
 
   /** Jalview Properties */
-  public static Properties applicationProperties = new Properties()
+  private Properties applicationProperties = new Properties()
   {
     // override results in properties output in alphabetical order
     @Override
@@ -324,24 +339,30 @@ public class Cache
 
   private final static String JS_PROPERTY_PREFIX = "jalview_";
 
+
   /**
-   * 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)
   {
+    getInstance().loadPropertiesImpl(propsFile);
+
+  }
+
+  private void loadPropertiesImpl(String propsFile)
+  {
     propertiesFile = propsFile;
     String releasePropertiesFile = null;
     boolean defaultProperties = false;
     if (propsFile == null && !propsAreReadOnly)
     {
+      // TODO: @bsoares - for 2.12 testing: check test,develop,release props are located correctly
       String channelPrefsFilename = ChannelProperties
               .getProperty("preferences.filename");
       String releasePrefsFilename = ".jalview_properties";
-      propertiesFile = System.getProperty("user.home") + File.separatorChar
-              + channelPrefsFilename;
-      releasePropertiesFile = System.getProperty("user.home")
-              + File.separatorChar + releasePrefsFilename;
+      propertiesFile = Platform.getUserPath(channelPrefsFilename);
+      releasePropertiesFile = Platform.getUserPath(releasePrefsFilename);
       defaultProperties = true;
     }
     else
@@ -599,7 +620,7 @@ public class Cache
     return url;
   }
 
-  public static void loadBuildProperties(boolean reportVersion)
+  public void loadBuildProperties(boolean reportVersion)
   {
     String codeInstallation = getProperty("INSTALLATION");
     boolean printVersion = codeInstallation == null;
@@ -662,7 +683,7 @@ public class Cache
     }
   }
 
-  private static void deleteBuildProperties()
+  private void deleteBuildProperties()
   {
     applicationProperties.remove("LATEST_VERSION");
     applicationProperties.remove("VERSION");
@@ -674,28 +695,27 @@ 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
    */
   public static String getProperty(String key)
   {
-    String prop = applicationProperties.getProperty(key);
-    if (prop == null && Platform.isJS())
-    {
-      prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
-              + "_" + JS_PROPERTY_PREFIX + key);
-    }
+    String prop = getInstance().applicationProperties.getProperty(key);
+    // if (prop == null && Platform.isJS())
+    // {
+    // prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
+    // + "_" + JS_PROPERTY_PREFIX + key);
+    // }
     return prop;
   }
 
   /**
-   * 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)
@@ -728,8 +748,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)
   {
@@ -741,41 +761,56 @@ 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)
    */
   public static Object setProperty(String key, String obj)
   {
-    Object oldValue = null;
-    try
-    {
-      oldValue = applicationProperties.setProperty(key, obj);
-      if (propertiesFile != null && !propsAreReadOnly)
-      {
-        FileOutputStream out = new FileOutputStream(propertiesFile);
-        applicationProperties.store(out, "---JalviewX Properties File---");
-        out.close();
-      }
-    } catch (Exception ex)
-    {
-      System.out.println(
-              "Error setting property: " + key + " " + obj + "\n" + ex);
-    }
-    return oldValue;
+    return getInstance().setPropertyImpl(key, obj, true);
+  }
+
+  /**
+   * Removes the specified property from the jalview properties file
+   * 
+   * @param key
+   */
+  public static void removeProperty(String key)
+  {
+    getInstance().removePropertyImpl(key, true);
+  }
+
+  /**
+   * Removes the named property for the running application, without saving the
+   * properties file
+   * 
+   * BH noting that ColourMenuHelper calls this. If the intent is to save, then
+   * simply chanet that call to removeProperty(key).
+   * 
+   * @param key
+   */
+  public static void removePropertyNoSave(String key)
+  {
+
+    getInstance().
+
+            removePropertyImpl(key, false);
   }
 
   /**
-   * remove the specified property from the jalview properties file
+   * Removes the named property, and optionally saves the current properties to
+   * file
    * 
-   * @param string
+   * @param key
+   * @param andSave
    */
-  public static void removeProperty(String string)
+  private void removePropertyImpl(String key, boolean andSave)
   {
-    applicationProperties.remove(string);
-    saveProperties();
+    applicationProperties.remove(key);
+    if (andSave)
+      saveProperties();
   }
 
   /**
@@ -783,6 +818,15 @@ public class Cache
    */
   public static void saveProperties()
   {
+    getInstance().savePropertiesImpl();
+  }
+
+  /**
+   * save the properties to the jalview properties path
+   */
+  private void savePropertiesImpl()
+
+  {
     if (!propsAreReadOnly)
     {
       try
@@ -1005,9 +1049,9 @@ public class Cache
    * @param property
    * @param colour
    */
-  public static void setColourProperty(String property, Color colour)
+  public static void setColourPropertyNoSave(String property, Color colour)
   {
-    setProperty(property, jalview.util.Format.getHexString(colour));
+    setPropertyNoSave(property, jalview.util.Format.getHexString(colour));
   }
 
   /**
@@ -1035,11 +1079,16 @@ public class Cache
   public static Date getDateProperty(String propertyName)
   {
     String val = getProperty(propertyName);
+    
     if (val != null)
     {
       try
       {
-        return date_format.parse(val);
+        if ((val = val.trim()).indexOf(",") < 0 && val.indexOf("-") >= 0 && val.indexOf(" ") == val.lastIndexOf(" ")) {
+          val = val.replace(" ",", ").replace('-',' ');
+        }
+        Date date =  date_format.parse(val);
+        return date;
       } catch (Exception ex)
       {
         System.err.println("Invalid or corrupt date in property '"
@@ -1088,11 +1137,11 @@ public class Cache
     }
     if (value == null || value.trim().length() < 1)
     {
-      Cache.applicationProperties.remove(propName);
+      getInstance().applicationProperties.remove(propName);
     }
     else
     {
-      Cache.applicationProperties.setProperty(propName, value);
+      getInstance().applicationProperties.setProperty(propName, value);
     }
   }
 
@@ -1100,7 +1149,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)
   {
@@ -1142,7 +1191,7 @@ public class Cache
       }
       else
       {
-        applicationProperties
+        getInstance().applicationProperties
                 .remove(UserDefinedColours.USER_DEFINED_COLOURS);
       }
     }
@@ -1233,7 +1282,53 @@ public class Cache
   {
     // consider returning more human friendly info
     // eg 'built from Source' or update channel
-    return Cache.getDefault("INSTALLATION", "unknown");
+        return Cache.getDefault("INSTALLATION", "unknown");
+  }
+
+  /**
+   * 
+   * For AppletParams and Preferences ok_actionPerformed and
+   * startupFileTextfield_mouseClicked
+   * 
+   * Sets a property value for the running application, without saving it to the
+   * properties file
+   * 
+   * @param key
+   * @param obj
+   */
+  public static void setPropertyNoSave(String key, String obj)
+  {
+    getInstance().setPropertyImpl(key, obj, false);
+  }
+
+  /**
+   * Sets a property value, and optionally also saves the current properties to
+   * file
+   * 
+   * @param key
+   * @param obj
+   * @param andSave
+   * @return
+   */
+  private Object setPropertyImpl(
+          String key, String obj, boolean andSave)
+  {
+    Object oldValue = null;
+    try
+    {
+      oldValue = applicationProperties.setProperty(key, obj);
+      if (andSave && !propsAreReadOnly && propertiesFile != null)
+      {
+        FileOutputStream out = new FileOutputStream(propertiesFile);
+        applicationProperties.store(out, "---JalviewX Properties File---");
+        out.close();
+      }
+    } catch (Exception ex)
+    {
+      System.out.println(
+              "Error setting property: " + key + " " + obj + "\n" + ex);
+    }
+    return oldValue;
   }
 
   public static String getStackTraceString(Throwable t)
index 1428906..7a8c786 100755 (executable)
  */
 package jalview.bin;
 
+import java.util.Locale;
+
+import java.awt.GraphicsEnvironment;
 import java.awt.Color;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -57,9 +61,13 @@ import com.threerings.getdown.util.LaunchUtil;
 //import edu.stanford.ejalbert.launching.IBrowserLaunching;
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.ext.so.SequenceOntology;
 import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
 import jalview.gui.Desktop;
+import jalview.gui.Preferences;
 import jalview.gui.PromptUserConfig;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.BioJsHTMLOutput;
@@ -97,9 +105,31 @@ import jalview.ws.jws2.Jws2Discoverer;
  * @author $author$
  * @version $Revision$
  */
-public class Jalview
+public class Jalview implements ApplicationSingletonI
 {
-  static
+  // for testing those nasty messages you cannot ever find.
+  // static
+  // {
+  // System.setOut(new PrintStream(new ByteArrayOutputStream())
+  // {
+  // @Override
+  // public void println(Object o)
+  // {
+  // if (o != null)
+  // {
+  // System.err.println(o);
+  // }
+  // }
+  //
+  // });
+  // }
+  public static Jalview getInstance()
+  {
+    return (Jalview) ApplicationSingletonProvider
+            .getInstance(Jalview.class);
+  }
+
+  private Jalview()
   {
     Platform.getURLCommandArguments();
     Platform.addJ2SDirectDatabaseCall("https://www.jalview.org");
@@ -108,18 +138,47 @@ public class Jalview
     Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk");
   }
 
-  /*
-   * singleton instance of this class
-   */
-  private static Jalview instance;
+
+  private boolean headless;
 
   private Desktop desktop;
 
-  public static AlignFrame currentAlignFrame;
+  public AlignFrame currentAlignFrame;
+
+  public String appletResourcePath;
+
+  public String j2sAppletID;
+
+  private boolean noCalculation, noMenuBar, noStatus;
+
+  private boolean noAnnotation;
+
+  public boolean getStartCalculations()
+  {
+    return !noCalculation;
+  }
+
+  public boolean getAllowMenuBar()
+  {
+    return !noMenuBar;
+  }
+
+  public boolean getShowStatus()
+  {
+    return !noStatus;
+  }
+
+  public boolean getShowAnnotation()
+  {
+    return !noAnnotation;
+  }
 
   static
   {
-    if (!Platform.isJS())
+    if (Platform.isJS())
+    {
+       Platform.getURLCommandArguments();
+    } else
     /**
      * Java only
      * 
@@ -204,10 +263,7 @@ public class Jalview
 
   }
 
-  public static Jalview getInstance()
-  {
-    return instance;
-  }
+  private final static boolean doPlatformLogging = false;
 
   /**
    * main class for Jalview application
@@ -217,50 +273,17 @@ public class Jalview
    */
   public static void main(String[] args)
   {
-    // setLogging(); // BH - for event debugging in JavaScript
-    instance = new Jalview();
-    instance.doMain(args);
-  }
-
-  private static void logClass(String name)
-  {
-    // BH - for event debugging in JavaScript
-    ConsoleHandler consoleHandler = new ConsoleHandler();
-    consoleHandler.setLevel(Level.ALL);
-    Logger logger = Logger.getLogger(name);
-    logger.setLevel(Level.ALL);
-    logger.addHandler(consoleHandler);
-  }
-
-  @SuppressWarnings("unused")
-  private static void setLogging()
-  {
-
-    /**
-     * @j2sIgnore
-     * 
-     */
+    if (doPlatformLogging)
     {
-      System.out.println("not in js");
+      Platform.startJavaLogging();
     }
 
-    // BH - for event debugging in JavaScript (Java mode only)
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      Logger.getLogger("").setLevel(Level.ALL);
-      logClass("java.awt.EventDispatchThread");
-      logClass("java.awt.EventQueue");
-      logClass("java.awt.Component");
-      logClass("java.awt.focus.Component");
-      logClass("java.awt.focus.DefaultKeyboardFocusManager");
-    }
+    getInstance().doMain(args);
 
   }
+  
+
+  
 
   /**
    * @param args
@@ -268,11 +291,17 @@ public class Jalview
   void doMain(String[] args)
   {
 
-    if (!Platform.isJS())
+    boolean isJS = Platform.isJS();
+    if (!isJS)
     {
       System.setSecurityManager(null);
     }
 
+    /*
+     * @j2sNative J2S.db._DirectDatabaseCalls["compbio.dundee.ac.uk"]=null;
+     * @j2sNative J2S.db._DirectDatabaseCalls["jalview.org"]=null;
+     * 
+     */
     System.out
             .println("Java version: " + System.getProperty("java.version"));
     System.out.println("Java Home: " + System.getProperty("java.home"));
@@ -296,48 +325,65 @@ public class Jalview
     }
 
     // report Jalview version
-    Cache.loadBuildProperties(true);
+    Cache.getInstance().loadBuildProperties(true);
 
     ArgsParser aparser = new ArgsParser(args);
-    boolean headless = false;
+    headless = false;
 
     String usrPropsFile = aparser.getValue("props");
     Cache.loadProperties(usrPropsFile); // must do this before
-    if (usrPropsFile != null)
+    boolean allowServices = true;
+    
+    if (isJS)
     {
-      System.out.println(
-              "CMD [-props " + usrPropsFile + "] executed successfully!");
+      j2sAppletID = Platform.getAppID(null);
+      Preferences.setAppletDefaults();
+      Cache.loadProperties(usrPropsFile); // again, because we
+      // might be changing defaults here?
+      appletResourcePath = (String) aparser.getAppletValue("resourcepath",
+              null, true);
     }
 
-    if (!Platform.isJS())
+    else
     /**
      * Java only
      * 
      * @j2sIgnore
      */
     {
+      if (usrPropsFile != null)
+      {
+        System.out.println(
+                "CMD [-props " + usrPropsFile + "] executed successfully!");
+      }
       if (aparser.contains("help") || aparser.contains("h"))
       {
         showUsage();
         System.exit(0);
       }
+      // BH note: Only -nodisplay is official; others are deprecated?
       if (aparser.contains("nodisplay") || aparser.contains("nogui")
-              || aparser.contains("headless"))
+              || aparser.contains("headless")
+              || GraphicsEnvironment.isHeadless())
       {
-        System.setProperty("java.awt.headless", "true");
+        if (!isJS) {
+          // BH Definitely not a good idea in JavaScript; 
+          // probably should not be here for Java, either.  
+          System.setProperty("java.awt.headless", "true");
+        }
         headless = true;
       }
       // anything else!
 
       // allow https handshakes to download intermediate certs if necessary
       System.setProperty("com.sun.security.enableAIAcaIssuers", "true");
-
-      final String jabawsUrl = aparser.getValue("jabaws");
-      if (jabawsUrl != null)
+      final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
+      allowServices = !("none".equals(jabawsUrl));
+      if (allowServices && jabawsUrl != null)
       {
         try
         {
-          Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
+          Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
           System.out.println(
                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
         } catch (MalformedURLException e)
@@ -348,7 +394,7 @@ public class Jalview
       }
     }
 
-    String defs = aparser.getValue("setprop");
+    String defs = aparser.getValue(ArgsParser.SETPROP);
     while (defs != null)
     {
       int p = defs.indexOf('=');
@@ -359,13 +405,10 @@ public class Jalview
       else
       {
         System.out.println("Executing setprop argument: " + defs);
-        if (Platform.isJS())
+        if (isJS)
         {
           Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
         }
-        // DISABLED FOR SECURITY REASONS
-        // TODO: add a property to allow properties to be overriden by cli args
-        // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
       }
       defs = aparser.getValue("setprop");
     }
@@ -376,7 +419,6 @@ public class Jalview
     }
     System.setProperty("http.agent",
             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
-
     try
     {
       Console.initLogger();
@@ -396,16 +438,16 @@ public class Jalview
      * configure 'full' SO model if preferences say to, else use the default (full SO)
      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
      */
-    boolean soDefault = !Platform.isJS();
+    boolean soDefault = !isJS;
     if (Cache.getDefault("USE_FULL_SO", soDefault))
     {
-      SequenceOntologyFactory.setInstance(new SequenceOntology());
+      SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
     }
 
     if (!headless)
     {
       Desktop.nosplash = aparser.contains("nosplash");
-      desktop = new Desktop();
+      desktop = Desktop.getInstance();
       desktop.setInBatchMode(true); // indicate we are starting up
 
       try
@@ -428,14 +470,22 @@ public class Jalview
 
       desktop.setVisible(true);
 
-      if (!Platform.isJS())
+      if (isJS)
+      {
+        Cache.setProperty("SHOW_JWS2_SERVICES", "false");
+      }
+      if (allowServices && !aparser.contains("nowebservicediscovery"))
+      {
+        desktop.startServiceDiscovery();
+      }
+
+      if (!isJS)
       /**
        * Java only
        * 
        * @j2sIgnore
        */
       {
-
         /**
          * Check to see that the JVM version being run is suitable for the Java
          * version this Jalview was compiled for. Popup a warning if not.
@@ -463,11 +513,6 @@ public class Jalview
                     null, options, options[0]);
           }
         }
-
-        if (!aparser.contains("nowebservicediscovery"))
-        {
-          desktop.startServiceDiscovery();
-        }
         if (!aparser.contains("nousagestats"))
         {
           startUsageStats(desktop);
@@ -534,45 +579,120 @@ public class Jalview
               + ") may lead to problems. This installation of Jalview should be used with Java "
               + LaunchUtils.getJavaCompileVersion() + ".");
     }
+    parseArguments(aparser, true);
+  }
 
-    // Move any new getdown-launcher-new.jar into place over old
-    // getdown-launcher.jar
-    String appdirString = System.getProperty("getdownappdir");
-    if (appdirString != null && appdirString.length() > 0)
+  /**
+   * Parse all command-line String[] arguments as well as all JavaScript-derived
+   * parameters from Info.
+   * 
+   * We allow for this method to be run from JavaScript. Basically allowing
+   * simple scripting.
+   * 
+   * @param aparser
+   * @param isStartup
+   */
+  public void parseArguments(ArgsParser aparser, boolean isStartup)
+  {
+
+    String groovyscript = null; // script to execute after all loading is
+    boolean isJS = Platform.isJS();
+    if (!isJS)
+    /** @j2sIgnore */
     {
-      final File appdir = new File(appdirString);
-      new Thread()
+      // Move any new getdown-launcher-new.jar into place over old
+      // getdown-launcher.jar
+      String appdirString = System.getProperty("getdownappdir");
+      if (appdirString != null && appdirString.length() > 0)
       {
-        @Override
-        public void run()
+        final File appdir = new File(appdirString);
+        new Thread()
         {
-          LaunchUtil.upgradeGetdown(
-                  new File(appdir, "getdown-launcher-old.jar"),
-                  new File(appdir, "getdown-launcher.jar"),
-                  new File(appdir, "getdown-launcher-new.jar"));
-        }
-      }.start();
-    }
+          @Override
+          public void run()
+          {
+            LaunchUtil.upgradeGetdown(
+                    new File(appdir, "getdown-launcher-old.jar"),
+                    new File(appdir, "getdown-launcher.jar"),
+                    new File(appdir, "getdown-launcher-new.jar"));
+          }
+        }.start();
+      }
 
-    String file = null, data = null;
-    FileFormatI format = null;
-    DataSourceType protocol = null;
-    FileLoader fileLoader = new FileLoader(!headless);
+      // completed one way or another
+      // extract groovy argument and execute if necessary
+      groovyscript = aparser.getValue("groovy", true);
+    }
 
-    String groovyscript = null; // script to execute after all loading is
-    // completed one way or another
-    // extract groovy argument and execute if necessary
-    groovyscript = aparser.getValue("groovy", true);
-    file = aparser.getValue("open", true);
+    String file = aparser.getValue("open", true);
 
-    if (file == null && desktop == null)
+    if (!isJS && file == null && desktop == null)
     {
       System.out.println("No files to open!");
       System.exit(1);
     }
+    setDisplayParameters(aparser);
+    
+    // time to open a file.
     long progress = -1;
+    DataSourceType protocol = null;
+    FileLoader fileLoader = new FileLoader(!headless);
+    FileFormatI format = null;
     // Finally, deal with the remaining input data.
-    if (file != null)
+    AlignFrame af = null;
+
+    JalviewJSApp jsApp = (isJS ? new JalviewJSApp(this, aparser) : null);
+
+    if (file == null)
+    {
+      if (isJS)
+      {
+        // JalviewJS allows sequence1 sequence2 ....
+        
+      }
+      else if (!headless && Cache.getDefault("SHOW_STARTUP_FILE", true))
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
+    {
+      file = Cache.getDefault("STARTUP_FILE",
+              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")
+              || 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", "_2_7");
+        file.replace("2_7.jar", "2_7.jvp");
+        // and remove the stale setting
+        Cache.removeProperty("STARTUP_FILE");
+      }
+
+      protocol = AppletFormatAdapter.checkProtocol(file);
+
+      if (file.endsWith(".jar"))
+      {
+        format = FileFormat.Jalview;
+      }
+      else
+      {
+        try
+        {
+          format = new IdentifyFile().identify(file, protocol);
+        } catch (FileFormatException e)
+        {
+          // TODO what?
+        }
+      }
+
+      af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format);
+       }
+    }
+    else
     {
       if (!headless)
       {
@@ -603,305 +723,414 @@ public class Jalview
         }
       }
 
+      // JS Only argument to provide a format parameter to specify what format to use
+      String fileFormat = (isJS
+              ? (String) aparser.getAppletValue("format", null, true)
+              : null);
       protocol = AppletFormatAdapter.checkProtocol(file);
 
       try
       {
-        format = new IdentifyFile().identify(file, protocol);
+        format = (fileFormat != null
+                ? FileFormats.getInstance().forName(fileFormat)
+                : null);
+        if (format == null)
+        {
+          format = new IdentifyFile().identify(file, protocol);
+        }
       } catch (FileFormatException e1)
       {
         // TODO ?
       }
 
-      AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
+      af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, protocol,
               format);
       if (af == null)
       {
-        System.out.println("error");
+        System.out.println("jalview error - AlignFrame was not created");
       }
       else
       {
-        setCurrentAlignFrame(af);
-        data = aparser.getValue("colour", true);
-        if (data != null)
+        
+        // JalviewLite interface for JavaScript allows second file open
+        String file2 = aparser.getValue(ArgsParser.OPEN2, true);
+        if (file2 != null)
         {
-          data.replaceAll("%20", " ");
-
-          ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
-                  af.getViewport(), af.getViewport().getAlignment(), data);
-
-          if (cs != null)
+          protocol = AppletFormatAdapter.checkProtocol(file2);
+          try
           {
-            System.out.println(
-                    "CMD [-color " + data + "] executed successfully!");
+            format = new IdentifyFile().identify(file2, protocol);
+          } catch (FileFormatException e1)
+          {
+            // TODO ?
           }
-          af.changeColour(cs);
-        }
-
-        // Must maintain ability to use the groups flag
-        data = aparser.getValue("groups", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD groups[-" + data + "]  executed successfully!");
-        }
-        data = aparser.getValue("features", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-features " + data + "]  executed successfully!");
-        }
-
-        data = aparser.getValue("annotations", true);
-        if (data != null)
-        {
-          af.loadJalviewDataFile(data, null, null, null);
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-annotations " + data + "] executed successfully!");
-        }
-        // set or clear the sortbytree flag.
-        if (aparser.contains("sortbytree"))
-        {
-          af.getViewport().setSortByTree(true);
-          if (af.getViewport().getSortByTree())
+          AlignFrame af2 = new FileLoader(!headless)
+                  .LoadFileWaitTillLoaded(file2, protocol, format);
+          if (af2 == null)
           {
-            System.out.println("CMD [-sortbytree] executed successfully!");
+            System.out.println("error");
           }
-        }
-        if (aparser.contains("no-annotation"))
-        {
-          af.getViewport().setShowAnnotation(false);
-          if (!af.getViewport().isShowAnnotation())
+          else
           {
-            System.out.println("CMD no-annotation executed successfully!");
+            AlignViewport.openLinkedAlignmentAs(af,
+                    af.getViewport().getAlignment(),
+                    af2.getViewport().getAlignment(), "",
+                    AlignViewport.SPLIT_FRAME);
+            System.out.println(
+                    "CMD [-open2 " + file2 + "] executed successfully!");
           }
         }
-        if (aparser.contains("nosortbytree"))
+        // af is loaded - so set it as current frame
+        setCurrentAlignFrame(af);
+
+        setFrameDependentProperties(aparser, af);
+        
+        if (isJS)
         {
-          af.getViewport().setSortByTree(false);
-          if (!af.getViewport().getSortByTree())
-          {
-            System.out
-                    .println("CMD [-nosortbytree] executed successfully!");
-          }
+          jsApp.initFromParams(af);
         }
-        data = aparser.getValue("tree", true);
-        if (data != null)
+        else
+        /**
+         * Java only
+         * 
+         * @j2sIgnore
+         */
         {
-          try
+          if (groovyscript != null)
           {
-            System.out.println(
-                    "CMD [-tree " + data + "] executed successfully!");
-            NewickFile nf = new NewickFile(data,
-                    AppletFormatAdapter.checkProtocol(data));
-            af.getViewport()
-                    .setCurrentTree(af.showNewickTree(nf, data).getTree());
-          } catch (IOException ex)
-          {
-            System.err.println("Couldn't add tree " + data);
-            ex.printStackTrace(System.err);
+            // Execute the groovy script after we've done all the rendering
+            // stuff
+            // and before any images or figures are generated.
+            System.out.println("Executing script " + groovyscript);
+            executeGroovyScript(groovyscript, af);
+            System.out.println("CMD groovy[" + groovyscript
+                    + "] executed successfully!");
+            groovyscript = null;
           }
         }
-        // TODO - load PDB structure(s) to alignment JAL-629
-        // (associate with identical sequence in alignment, or a specified
-        // sequence)
-        if (groovyscript != null)
-        {
-          // Execute the groovy script after we've done all the rendering stuff
-          // and before any images or figures are generated.
-          System.out.println("Executing script " + groovyscript);
-          executeGroovyScript(groovyscript, af);
-          System.out.println("CMD groovy[" + groovyscript
-                  + "] executed successfully!");
-          groovyscript = null;
+        if (!isJS || !isStartup) {
+          createOutputFiles(aparser, format);
         }
-        String imageName = "unnamed.png";
-        while (aparser.getSize() > 1)
-        {
-          String outputFormat = aparser.nextValue();
-          file = aparser.nextValue();
+      }
+      if (headless)
+      {
+        af.getViewport().getCalcManager().shutdown();
+      }
+    }
+    // extract groovy arguments before anything else.
+    // Once all other stuff is done, execute any groovy scripts (in order)
+    if (!isJS && groovyscript != null)
+    {
+      if (Cache.groovyJarsPresent())
+      {
+        // TODO: DECIDE IF THIS SECOND PASS AT GROOVY EXECUTION IS STILL REQUIRED !!
+        System.out.println("Executing script " + groovyscript);
+        executeGroovyScript(groovyscript, af);
+        System.out.println("CMD groovy[" + groovyscript
+                    + "] executed successfully!");
 
-          if (outputFormat.equalsIgnoreCase("png"))
-          {
-            af.createPNG(new File(file));
-            imageName = (new File(file)).getName();
-            System.out.println("Creating PNG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("svg"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            af.createSVG(imageFile);
-            System.out.println("Creating SVG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("html"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
-            htmlSVG.exportHTML(file);
+      }
+      else
+      {
+        System.err.println(
+                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
+                        + groovyscript);
+      }
+    }
 
-            System.out.println("Creating HTML image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("biojsmsa"))
-          {
-            if (file == null)
-            {
-              System.err.println("The output html file must not be null");
-              return;
-            }
-            try
-            {
-              BioJsHTMLOutput.refreshVersionInfo(
-                      BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
-            } catch (URISyntaxException e)
-            {
-              e.printStackTrace();
-            }
-            BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
-            bjs.exportHTML(file);
-            System.out
-                    .println("Creating BioJS MSA Viwer HTML file: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("imgMap"))
-          {
-            af.createImageMap(new File(file), imageName);
-            System.out.println("Creating image map: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("eps"))
-          {
-            File outputFile = new File(file);
-            System.out.println(
-                    "Creating EPS file: " + outputFile.getAbsolutePath());
-            af.createEPS(outputFile);
-            continue;
-          }
-          FileFormatI outFormat = null;
-          try
-          {
-            outFormat = FileFormats.getInstance().forName(outputFormat);
-          } catch (Exception formatP)
-          {
-            System.out.println("Couldn't parse " + outFormat
-                    + " as a valid Jalview format string.");
-          }
-          if (outFormat != null)
-          {
-            if (!outFormat.isWritable())
-            {
-              System.out.println(
-                      "This version of Jalview does not support alignment export as "
-                              + outputFormat);
-            }
-            else
-            {
-              af.saveAlignment(file, outFormat);
-              if (af.isSaveAlignmentSuccessful())
-              {
-                System.out.println("Written alignment in "
-                        + outFormat.getName() + " format to " + file);
-              }
-              else
-              {
-                System.out.println("Error writing file " + file + " in "
-                        + outFormat.getName() + " format!!");
-              }
-            }
-          }
+    // and finally, turn off batch mode indicator - if the desktop still exists
+    if (desktop != null)
+    {
+      if (progress != -1)
+      {
+        desktop.setProgressBar(null, progress);
+      }
+      desktop.setInBatchMode(false);
+    }
+    
+    if (jsApp != null) {
+      jsApp.callInitCallback();
+    }
+  }
+  
+  /**
+   * Set general display parameters irrespective of file loading or headlessness.
+   * 
+   * @param aparser
+   */
+  private void setDisplayParameters(ArgsParser aparser)
+  {
+    if (aparser.contains(ArgsParser.NOMENUBAR))
+    {
+      noMenuBar = true;
+      System.out.println("CMD [nomenu] executed successfully!");
+    }
 
-        }
+    if (aparser.contains(ArgsParser.NOSTATUS))
+    {
+      noStatus = true;
+      System.out.println("CMD [nostatus] executed successfully!");
+    }
 
-        while (aparser.getSize() > 0)
-        {
-          System.out.println("Unknown arg: " + aparser.nextValue());
-        }
-      }
+    if (aparser.contains(ArgsParser.NOANNOTATION)
+            || aparser.contains(ArgsParser.NOANNOTATION2))
+    {
+      noAnnotation = true;
+      System.out.println("CMD no-annotation executed successfully!");
     }
-    AlignFrame startUpAlframe = null;
-    // We'll only open the default file if the desktop is visible.
-    // And the user
-    // ////////////////////
+    if (aparser.contains(ArgsParser.NOCALCULATION))
+    {
+      noCalculation = true;
+      System.out.println("CMD [nocalculation] executed successfully!");
+    }
+  }
 
-    if (!Platform.isJS() && !headless && file == null
-            && Cache.getDefault("SHOW_STARTUP_FILE", true))
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
+  private void setFrameDependentProperties(ArgsParser aparser,
+          AlignFrame af)
+  {
+    String data = aparser.getValue(ArgsParser.COLOUR, true);
+    if (data != null)
     {
-      file = Cache.getDefault("STARTUP_FILE",
-              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")
-              || file.equals(
-                      "http://www.jalview.org/examples/exampleFile_2_7.jar"))
+      data.replaceAll("%20", " ");
+
+      ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+              af.getViewport(), af.getViewport().getAlignment(), data);
+
+      if (cs != null)
       {
-        file.replace("http:", "https:");
-        // hardwire upgrade of the startup file
-        file.replace("_2_3", "_2_7");
-        file.replace("2_7.jar", "2_7.jvp");
-        // and remove the stale setting
-        Cache.removeProperty("STARTUP_FILE");
+        System.out.println(
+                "CMD [-color " + data + "] executed successfully!");
       }
+      af.changeColour(cs);
+    }
 
-      protocol = AppletFormatAdapter.checkProtocol(file);
+    // Must maintain ability to use the groups flag
+    data = aparser.getValue(ArgsParser.GROUPS, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD groups[-" + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.FEATURES, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-features " + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
+    if (data != null)
+    {
+      af.loadJalviewDataFile(data, null, null, null);
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-annotations " + data + "] executed successfully!");
+    }
 
-      if (file.endsWith(".jar"))
+    // JavaScript feature
+
+    if (aparser.contains(ArgsParser.SHOWOVERVIEW))
+    {
+      af.overviewMenuItem_actionPerformed(null);
+      System.out.println("CMD [showoverview] executed successfully!");
+    }
+
+    // set or clear the sortbytree flag.
+    if (aparser.contains(ArgsParser.SORTBYTREE))
+    {
+      af.getViewport().setSortByTree(true);
+      if (af.getViewport().getSortByTree())
       {
-        format = FileFormat.Jalview;
+        System.out.println("CMD [-sortbytree] executed successfully!");
       }
-      else
+    }
+
+    boolean doUpdateAnnotation = false;
+    /**
+     * we do this earlier in JalviewJS because of a complication with
+     * SHOWOVERVIEW
+     * 
+     * For now, just fixing this in JalviewJS.
+     *
+     * 
+     * @j2sIgnore
+     * 
+     */
+    {
+      if (noAnnotation)
       {
-        try
+        af.getViewport().setShowAnnotation(false);
+        if (!af.getViewport().isShowAnnotation())
         {
-          format = new IdentifyFile().identify(file, protocol);
-        } catch (FileFormatException e)
-        {
-          // TODO what?
+          doUpdateAnnotation = true;
         }
       }
 
-      startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
-              format);
-      // extract groovy arguments before anything else.
     }
 
-    // Once all other stuff is done, execute any groovy scripts (in order)
-    if (groovyscript != null)
+    if (aparser.contains(ArgsParser.NOSORTBYTREE))
     {
-      if (Cache.groovyJarsPresent())
+      af.getViewport().setSortByTree(false);
+      if (!af.getViewport().getSortByTree())
       {
-        System.out.println("Executing script " + groovyscript);
-        executeGroovyScript(groovyscript, startUpAlframe);
+        doUpdateAnnotation = true;
+        System.out
+                .println("CMD [-nosortbytree] executed successfully!");
       }
-      else
+    }
+    if (doUpdateAnnotation)
+    { // BH 2019.07.24
+      af.setMenusForViewport();
+      af.alignPanel.updateLayout();
+    }
+
+    data = aparser.getValue(ArgsParser.TREE, true);
+    if (data != null)
+    {
+      try
       {
-        System.err.println(
-                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
-                        + groovyscript);
+        NewickFile nf = new NewickFile(data,
+                AppletFormatAdapter.checkProtocol(data));
+        af.getViewport()
+                .setCurrentTree(af.showNewickTree(nf, data).getTree());
+        System.out.println(
+                "CMD [-tree " + data + "] executed successfully!");
+      } catch (IOException ex)
+      {
+        System.err.println("Couldn't add tree " + data);
+        ex.printStackTrace(System.err);
       }
     }
-    // and finally, turn off batch mode indicator - if the desktop still exists
-    if (desktop != null)
+    // TODO - load PDB structure(s) to alignment JAL-629
+    // (associate with identical sequence in alignment, or a specified
+    // sequence)
+
+  }
+
+  /**
+   * Writes an output file for each format (if any) specified in the
+   * command-line arguments. Supported formats are currently
+   * <ul>
+   * <li>png</li>
+   * <li>svg</li>
+   * <li>html</li>
+   * <li>biojsmsa</li>
+   * <li>imgMap</li>
+   * <li>eps</li>
+   * </ul>
+   * A format parameter should be followed by a parameter specifying the output
+   * file name. {@code imgMap} parameters should follow those for the
+   * corresponding alignment image output.
+   * 
+   * @param aparser
+   * @param format
+   */
+  private void createOutputFiles(ArgsParser aparser, FileFormatI format)
+  {
+    // logic essentially the same as 2.11.2/2.11.3 but uses a switch instead
+    AlignFrame af = currentAlignFrame;
+    while (aparser.getSize() >= 2)
     {
-      if (progress != -1)
+      String outputFormat = aparser.nextValue();
+      File imageFile;
+      String fname;
+      switch (outputFormat.toLowerCase(Locale.ROOT))
       {
-        desktop.setProgressBar(null, progress);
+      case "png":
+        imageFile = new File(aparser.nextValue());
+        af.createPNG(imageFile);
+        System.out.println(
+                "Creating PNG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "svg":
+        imageFile = new File(aparser.nextValue());
+        af.createSVG(imageFile);
+        System.out.println(
+                "Creating SVG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "eps":
+        imageFile = new File(aparser.nextValue());
+        System.out.println(
+                "Creating EPS file: " + imageFile.getAbsolutePath());
+        af.createEPS(imageFile);
+        continue;
+      case "biojsmsa":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
+        try
+        {
+          BioJsHTMLOutput.refreshVersionInfo(
+                  BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
+        } catch (URISyntaxException e)
+        {
+          e.printStackTrace();
+        }
+        BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
+        bjs.exportHTML(fname);
+        System.out.println("Creating BioJS MSA Viwer HTML file: " + fname);
+        continue;
+      case "html":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
+        HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
+        htmlSVG.exportHTML(fname);
+        System.out.println("Creating HTML image: " + fname);
+        continue;
+      case "imgmap":
+        imageFile = new File(aparser.nextValue());
+        af.alignPanel.makePNGImageMap(imageFile, "unnamed.png");
+        System.out.println(
+                "Creating image map: " + imageFile.getAbsolutePath());
+        continue;
+      default:
+        // fall through - try to parse as an alignment data export format
+        FileFormatI outFormat = null;
+        try
+        {
+          outFormat = FileFormats.getInstance().forName(outputFormat);
+        } catch (Exception formatP)
+        {
+        }
+        if (outFormat == null)
+        {
+          System.out.println("Couldn't parse " + outputFormat
+                  + " as a valid Jalview format string.");
+          continue;
+        }
+        if (!outFormat.isWritable())
+        {
+          System.out.println(
+                  "This version of Jalview does not support alignment export as "
+                          + outputFormat);
+          continue;
+        }
+        // record file as it was passed to Jalview so it is recognisable to the CLI
+        // caller
+        String file;
+        fname = new File(file = aparser.nextValue()).getAbsolutePath();
+        // JBPNote - yuck - really wish we did have a bean returned from this which gave
+        // success/fail like before !
+        af.saveAlignment(fname, outFormat);
+        if (!af.isSaveAlignmentSuccessful())
+        {
+          System.out.println("Written alignment in " + outputFormat
+                  + " format to " + file);
+          continue;
+        }
+        else
+        {
+          System.out.println("Error writing file " + file + " in "
+                  + outputFormat + " format!!");
+        }
       }
-      desktop.setInBatchMode(false);
+    }
+    // ??? Should report - 'ignoring' extra args here...
+    while (aparser.getSize() > 0)
+    {
+      System.out.println("Ignoring extra argument: " + aparser.nextValue());
     }
   }
 
@@ -1100,11 +1329,11 @@ public class Jalview
                 ChannelProperties.getProperty("app_name"));
         System.setProperty("apple.awt.application.appearance", "system");
         if (SystemInfo.isMacFullWindowContentSupported
-                && Desktop.desktop != null)
+            && Desktop.getInstance() != null)
         {
-          Desktop.desktop.getRootPane()
+         Desktop.getInstance().getRootPane()
                   .putClientProperty("apple.awt.fullWindowContent", true);
-          Desktop.desktop.getRootPane()
+          Desktop.getInstance().getRootPane()
                   .putClientProperty("apple.awt.transparentTitleBar", true);
         }
 
@@ -1125,7 +1354,6 @@ public class Jalview
     }
     return set;
   }
-
   private static boolean setQuaquaLookAndFeel()
   {
     return setSpecificLookAndFeel("quaqua",
@@ -1207,7 +1435,7 @@ public class Jalview
     /**
      * start a User Config prompt asking if we can log usage statistics.
      */
-    PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
+    PromptUserConfig prompter = new PromptUserConfig(Desktop.getDesktopPane(),
             "USAGESTATS", "Jalview Usage Statistics",
             "Do you want to help make Jalview better by enabling "
                     + "the collection of usage statistics with Google Analytics ?"
@@ -1369,8 +1597,8 @@ public class Jalview
   }
 
   /**
-   * Quit method delegates to Desktop.quit - unless running in headless mode
-   * when it just ends the JVM
+   * Quit method delegates to Desktop.quit - unless running in headless mode when
+   * it just ends the JVM
    */
   public void quit()
   {
@@ -1386,11 +1614,30 @@ public class Jalview
 
   public static AlignFrame getCurrentAlignFrame()
   {
-    return Jalview.currentAlignFrame;
+    return Jalview.getInstance().currentAlignFrame;
   }
 
   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
   {
-    Jalview.currentAlignFrame = currentAlignFrame;
+    Jalview.getInstance().currentAlignFrame = currentAlignFrame;
+  }
+  
+  public void notifyWorker(AlignCalcWorkerI worker, String status)
+  {
+    // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
+    // + " " + status);
+  }
+
+
+  private static boolean isInteractive = true;
+
+  public static boolean isInteractive()
+  {
+    return isInteractive;
+  }
+
+  public static void setInteractive(boolean tf)
+  {
+    isInteractive = tf;
   }
 }
index b0a9ba0..7ce59b7 100644 (file)
@@ -41,16 +41,37 @@ public class JalviewJS2
   static
   {
     /**
-     * @j2sNative
+     * @ could do it this way:
      * 
-     *            J2S.thisApplet.__Info.args =
-     *            ["open","examples/uniref50.fa","features",
-     *            "examples/exampleFeatures.txt"];
+     * j2sNative
+     * 
+     * J2S.thisApplet.__Info.args = [ "open","examples/uniref50.fa",
+     * "features","examples/exampleFeatures.txt", "noannotation" ];
      */
   }
 
   public static void main(String[] args) throws Exception
   {
+    if (args.length == 0)
+    {
+      args = new String[] {
+        //  "headless",
+          "open", "examples/uniref50.fa",
+//          "features",
+//          "examples/exampleFeatures.txt"
+//          , "noannotation"
+          //, "showoverview"
+          //, "png", "test-bh.png"
+      };
+    }
+
+    // String cmds = "nodisplay -open examples/uniref50.fa -sortbytree -props
+    // test/jalview/io/testProps.jvprops -colour zappo "
+    // + "-jabaws http://www.compbio.dundee.ac.uk/jabaws -nosortbytree "
+    // + "-features examples/testdata/plantfdx.features -annotations
+    // examples/testdata/plantfdx.annotations -tree
+    // examples/testdata/uniref50_test_tree";
+    // args = cmds.split(" ");
     Jalview.main(args);
     // showFocusTimer();
   }
diff --git a/src/jalview/bin/JalviewJSApp.java b/src/jalview/bin/JalviewJSApp.java
new file mode 100644 (file)
index 0000000..feab4ae
--- /dev/null
@@ -0,0 +1,1691 @@
+package jalview.bin;
+
+import java.awt.EventQueue;
+//import java.applet.AppletContext;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.SwingUtilities;
+
+import jalview.api.JalviewJSApi;
+import jalview.api.StructureSelectionManagerProvider;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.CalculationChooser;
+import jalview.gui.Desktop;
+import jalview.gui.StructureViewer;
+import jalview.io.AnnotationFile;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.DataSourceType;
+import jalview.io.FeaturesFile;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.IdentifyFile;
+import jalview.io.JPredFile;
+import jalview.io.JnetAnnotationMaker;
+import jalview.io.NewickFile;
+import jalview.structure.SelectionListener;
+import jalview.structure.SelectionSource;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.HttpUtils;
+import jalview.util.Platform;
+
+/**
+ * Basically the JalviewLite application, but without JalviewLite
+ * 
+ * Processing all "applet parameters" and also all "applet interface" methods.
+ * 
+ * @author hansonr
+ *
+ */
+public class JalviewJSApp implements JalviewJSApi
+{
+  private ArgsParser aparser;
+
+  private String[] ret = new String[1];
+
+  // private boolean alignPDBStructures; From JalviewLite; not implemented
+
+  private String separator = "\u00AC"; // JalviewLite note: the default used to
+                                       // be '|', but many sequence IDS include
+                                       // pipes.
+
+  /**
+   * We maintain a pointer to the jalview instance here, because only with that
+   * do we have a direct connection from the JavaScript "applet" object to the
+   * proper instance of Jalview in case there are multiple applets on a page.
+   */
+  private Jalview jalview;
+
+  public class JsSelectionListener
+          implements jalview.structure.SelectionListener
+  {
+
+    AlignFrame _alf;
+
+    String _listener;
+
+    public JsSelectionListener(AlignFrame alf, String listener)
+    {
+      _alf = alf;
+      _listener = listener;
+    }
+
+    public boolean isFor(AlignFrame alf, String listener)
+    {
+      return (_alf == null || _alf == alf) && _listener.equals(listener);
+    }
+
+    @Override
+    public void selection(SequenceGroup seqsel, ColumnSelection colsel,
+            HiddenColumns hidden, SelectionSource source)
+    {
+      // System.err.println("Testing selection event relay to
+      // jsfunction:"+_listener);
+      String setid = "";
+      AlignFrame srcFrame = (_alf == null ? getCurrentAlignFrame() : _alf);
+      if (source != null)
+      {
+        if (source instanceof AlignViewport
+                && srcFrame.getViewport() != source)
+        {
+          return;
+        }
+      }
+      String[] seqs = new String[] {};
+      String[] cols = new String[] {};
+      int strt = 0, end = (srcFrame == null) ? -1
+              : srcFrame.alignPanel.av.getAlignment().getWidth();
+      if (seqsel != null && seqsel.getSize() > 0)
+      {
+        seqs = new String[seqsel.getSize()];
+        for (int i = 0; i < seqs.length; i++)
+        {
+          seqs[i] = seqsel.getSequenceAt(i).getName();
+        }
+        if (strt < seqsel.getStartRes())
+        {
+          strt = seqsel.getStartRes();
+        }
+        if (end == -1 || end > seqsel.getEndRes())
+        {
+          end = seqsel.getEndRes();
+        }
+      }
+      if (colsel != null && !colsel.isEmpty())
+      {
+        if (end == -1)
+        {
+          end = colsel.getMax() + 1;
+        }
+        cols = new String[colsel.getSelected().size()];
+        for (int i = 0; i < cols.length; i++)
+        {
+          cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
+        }
+      }
+      else
+      {
+        if (seqsel != null && seqsel.getSize() > 0)
+        {
+          // send a valid range, otherwise we send the empty selection
+          cols = new String[1];
+          cols[0] = "" + (1 + strt) + "-" + (1 + end);
+        }
+      }
+      doSendCallback(_listener,
+              new Object[]
+              { Jalview.getInstance().j2sAppletID, srcFrame, source, setid,
+                  seqs, cols });
+    }
+
+  }
+
+  public JalviewJSApp(Jalview jalview, ArgsParser aparser)
+  {
+    Platform.setAppClass(this);
+    this.jalview = jalview;
+    this.aparser = aparser;
+  }
+
+  @Override
+  public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    SequenceI seq = alf.getViewport().getAlignment().findName(sequenceId);
+    if (seq != null)
+    {
+      Vector<PDBEntry> pdbe = seq.getAllPDBEntries();
+      PDBEntry pdbentry = null;
+      if (pdbe != null && pdbe.size() > 0)
+      {
+        for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
+        {
+          pdbentry = pdbe.elementAt(pe);
+          if (!pdbentry.getId().equals(pdbId)
+                  || pdbFile != null && !pdbentry.getFile().equals(pdbFile))
+          {
+            pdbentry = null;
+          }
+        }
+      }
+      if (pdbentry == null)
+      {
+        pdbentry = new PDBEntry(pdbId, null, pdbFile);
+        if (pdbFile != null)
+        {
+          DataSourceType protocol = AppletFormatAdapter
+                  .resolveProtocol(pdbFile, FileFormat.PDB);
+          if (protocol == null)
+            return false;
+          pdbentry.setProperty("protocol", protocol);
+        }
+        seq.addPDBId(pdbentry);
+        alf.alignPanel.getStructureSelectionManager()
+                .registerPDBEntry(pdbentry);
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public String getAlignment(String format, boolean addSuffix,
+          AlignFrame alf)
+  {
+    try
+    {
+      if (alf == null)
+      {
+        alf = getCurrentAlignFrame();
+      }
+
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      String reply = new AppletFormatAdapter().formatSequences(theFormat,
+              alf.getViewport().getAlignment(), addSuffix);
+      return reply;
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+  }
+
+  @Override
+  public String[] getAlignmentOrder(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    AlignmentI alorder = alf.getViewport().getAlignment();
+    String[] order = new String[alorder.getHeight()];
+    for (int i = 0; i < order.length; i++)
+    {
+      order[i] = alorder.getSequenceAt(i).getName();
+    }
+    return order;// arrayToSeparatorList(order, sep);
+  }
+
+  @Override
+  public String getAnnotation(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    String annotation = new AnnotationFile()
+            .printAnnotationsForView(alf.getViewport());
+    return annotation;
+  }
+
+  /**
+   * Get the applet-like code base even though this is an application.
+   */
+
+  @Override
+  public URL getCodeBase()
+  {
+    return Platform.getCodeBase();
+  }
+
+  @Override
+  public AlignFrame getCurrentAlignFrame()
+  {
+    // if (jalview != Jalview.getInstance() || jalview.currentAlignFrame !=
+    // Jalview.getCurrentAlignFrame()) {
+    // /** @j2sNative debugger */
+    // }
+    return jalview.currentAlignFrame;
+  }
+
+  /**
+   * Get the applet-like document base even though this is an application.
+   */
+
+  @Override
+  public URL getDocumentBase()
+  {
+    return Platform.getDocumentBase();
+  }
+
+  @Override
+  public String[] getFeatureGroups(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return alf.getFeatureGroups();
+  }
+
+  @Override
+  public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return alf.getFeatureGroupsOfState(visible);
+  }
+
+  /**
+   * JavaScript interface to print the alignment frame
+   * 
+   * @param format
+   *          "jalview" or "gff" with or without ";includeComplement" or
+   *          ";includeNonpositional"; default with no ";" is
+   *          ";includeNonpositional"
+   * @param alf
+   * 
+   * @return
+   */
+  @Override
+  public String getFeatures(String format, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    String features;
+    FeaturesFile formatter = new FeaturesFile();
+    format = format.toLowerCase();
+    if (format.indexOf(";") < 0)
+      format += ";includenonpositional";
+    boolean nonpos = format.indexOf(";includenonpositional") >= 0;
+    boolean compl = format.indexOf(";includecomplement") >= 0;
+    if (format.startsWith("jalview"))
+    {
+      features = formatter.printJalviewFormat(
+              alf.getViewport().getAlignment().getSequencesArray(),
+              alf.alignPanel.getFeatureRenderer(), nonpos, compl);
+    }
+    else
+    {
+      features = formatter.printGffFormat(
+              alf.getViewport().getAlignment().getSequencesArray(),
+              alf.alignPanel.getFeatureRenderer(), nonpos, compl);
+    }
+
+    if (features == null)
+    {
+      features = "";
+    }
+    return features;
+
+  }
+
+  /**
+   * Get an applet parameter as a string.
+   * 
+   */
+  @Override
+  public String getParameter(String name)
+  {
+    return (String) aparser.getAppletValue(name, null, true);
+  }
+
+  /**
+   * Get an applet parameter as an Object.
+   */
+
+  @Override
+  public Object getParameterAsObject(String name)
+  {
+    return aparser.getAppletValue(name, null, false);
+  }
+
+  /**
+   * read sequence1...sequenceN as a raw alignment
+   * 
+   * @param jalviewApp
+   * @return
+   */
+  public String getPastedSequence(JalviewJSApp jalviewApp)
+  {
+    StringBuffer data = new StringBuffer("PASTE");
+    int i = 1;
+    String file = null;
+    while ((file = getParameter("sequence" + i)) != null)
+    {
+      data.append(file.toString() + "\n");
+      i++;
+    }
+    if (data.length() > 5)
+    {
+      file = data.toString();
+    }
+    return file;
+  }
+
+  /**
+   * @j2sAlias getSelectedSequences
+   * 
+   * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
+   *      .AlignFrame)
+   */
+  @Override
+  public SequenceI[] getSelectedSequences(AlignFrame alf)
+  {
+    // return getSelectedSequencesFrom(alf, null);
+    // }
+    //
+    // @Override
+    // public SequenceI[] getSelectedSequencesFrom(AlignFrame alf, String sep)
+    // {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    AlignViewport v = alf.getViewport();
+    if (v.getSelectionGroup() != null)
+    {
+      return v.getSelectionGroup().getSequencesInOrder(v.getAlignment());
+    }
+    return null;
+  }
+  // /**
+  // *
+  // * @see
+  // jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
+  // * .AlignFrame, java.lang.String)
+  // */
+  // @Override
+  // public void highlight(String sequenceId, String position,
+  // String alignedPosition)
+  // {
+  // highlightIn(null, sequenceId, position, alignedPosition);
+  // }
+
+  /**
+   * @j2sAlias getSelectedSequencesAsAlignment
+   */
+  @Override
+  public String getSelectedSequencesAsAlignment(String format,
+          boolean addSuffix, AlignFrame alf)
+  {
+
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    try
+    {
+      AlignViewport vp = alf.getViewport();
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      if (vp.getSelectionGroup() != null)
+      {
+        // JBPNote: getSelectionAsNewSequence behaviour has changed - this
+        // method now returns a full copy of sequence data
+        // TODO consider using getSequenceSelection instead here
+        String reply = new AppletFormatAdapter().formatSequences(theFormat,
+                new Alignment(vp.getSelectionAsNewSequence()), addSuffix);
+        return reply;
+      }
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+    return "";
+  }
+
+  @Override
+  public void highlight(String sequenceId, String position,
+          String alignedPosition, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    // TODO: could try to highlight in all alignments if alf==null
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            alf.getViewport().getAlignment().getSequencesArray());
+    final SequenceI sq = matcher.findIdMatch(sequenceId);
+    if (sq != null)
+    {
+      int apos = -1;
+      try
+      {
+        apos = Integer.valueOf(position).intValue();
+        apos--;
+      } catch (NumberFormatException ex)
+      {
+        return;
+      }
+      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))
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
+          }
+        });
+      }
+      else
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, pos, null);
+          }
+        });
+      }
+    }
+  }
+
+  @Override
+  public AlignFrame loadAlignment(String text, String title, int width,
+          int height)
+  {
+    AlignmentI al = null;
+
+    try
+    {
+      FileFormatI format = new IdentifyFile().identify(text,
+              DataSourceType.PASTE);
+      al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
+              format);
+      if (al.getHeight() > 0)
+      {
+        return new AlignFrame(al,
+                width > 0 ? width : AlignFrame.DEFAULT_WIDTH,
+                height > 0 ? height : AlignFrame.DEFAULT_HEIGHT, title);
+      }
+    } catch (IOException ex)
+    {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+
+  @Override
+  public void loadAnnotation(String annotation, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    if (new AnnotationFile().annotateAlignmentView(alf.getViewport(),
+            annotation, DataSourceType.PASTE))
+    {
+      alf.alignPanel.fontChanged();
+      alf.alignPanel.setScrollValues(0, 0);
+    }
+    else
+    {
+      alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
+    }
+  }
+
+  @Override
+  public boolean loadFeatures(String features, boolean autoenabledisplay,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    boolean ret = alf.parseFeaturesFile(features, DataSourceType.PASTE);
+    if (!ret)
+    {
+      return false;
+    }
+    if (autoenabledisplay)
+    {
+      alf.getViewport().setShowSequenceFeatures(true);
+      // this next was for a checkbox in JalviewLite
+      // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
+    }
+    return true;
+  }
+
+  @Override
+  public boolean loadScoreFile(String fileName, AlignFrame alf)
+  {
+    try
+    {
+      (alf == null ? getCurrentAlignFrame() : alf)
+              .loadJalviewDataFile(fileName, null, null, null);
+      return true;
+    } catch (Throwable t)
+    {
+      return false;
+    }
+  }
+
+  /**
+   * @j2sAlias openPcaPanel
+   * 
+   *           public static method for JalviewJS API to open a PCAPanel without
+   *           necessarily using a dialog.
+   * @param modelName
+   * @param alf
+   * 
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  @Override
+  public Object openPcaPanel(String modelName, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return CalculationChooser.openPcaPanel(alf, modelName, null);
+  }
+
+  /**
+   * @j2sAlias openTreePanel
+   * 
+   *           Open a new Tree panel on the desktop statically. Params are
+   *           standard (not set by Groovy). No dialog is opened.
+   * @param treeType
+   * @param modelName
+   * @param alf
+   * 
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  @Override
+  public Object openTreePanel(String treeType, String modelName,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return CalculationChooser.openTreePanel(alf, treeType, modelName, null);
+  }
+
+  @Override
+  public boolean orderAlignment(String[] ids, String undoName,
+          AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    SequenceI[] sqs = null;
+    if (ids != null && ids.length > 0)
+    {
+      jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+              alf.getViewport().getAlignment().getSequencesArray());
+      int s = 0;
+      sqs = new SequenceI[ids.length];
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          sqs[s++] = sq;
+        }
+      }
+      if (s > 0)
+      {
+        SequenceI[] sqq = new SequenceI[s];
+        System.arraycopy(sqs, 0, sqq, 0, s);
+        sqs = sqq;
+      }
+      else
+      {
+        sqs = null;
+      }
+    }
+    if (sqs == null)
+    {
+      return false;
+    }
+    ;
+    final AlignmentOrder aorder = new AlignmentOrder(sqs);
+
+    if (undoName != null && undoName.trim().length() == 0)
+    {
+      undoName = null;
+    }
+    final String _undoName = undoName;
+    // TODO: deal with synchronization here: cannot raise any events until
+    // alfter
+    // this has returned.
+    return alf.sortBy(aorder, _undoName);
+  }
+
+  /**
+   * Allow an outside entity to initiate the second half of argument parsing
+   * (only).
+   * 
+   * @param args
+   * @return null is good
+   */
+  @Override
+  public Object parseArguments(String[] args)
+  {
+
+    try
+    {
+      jalview.parseArguments(new ArgsParser(args), false);
+      return null;
+    } catch (Throwable t)
+    {
+      return t;
+    }
+  }
+
+  /**
+   * @j2sAlias parseFeatureFile
+   * 
+   * @param filename
+   * @param alf
+   * @return
+   */
+  @Override
+  public boolean parseFeaturesFile(String filename, AlignFrame alf)
+  {
+    ret[0] = filename;
+    DataSourceType protocol = resolveFileProtocol(ret);
+    if (protocol == null)
+      return false;
+    return (alf == null ? getCurrentAlignFrame() : alf)
+            .parseFeaturesFile(ret[0], protocol);
+  }
+
+  @Override
+  public void removeSelectionListener(String listener, AlignFrame alf)
+  {
+
+    List<SelectionListener> listeners = Desktop
+            .getStructureSelectionManager().getListeners();
+    for (int i = listeners.size(); --i >= 0;)
+    {
+      SelectionListener l = listeners.get(i);
+      if (l instanceof JsSelectionListener
+              && ((JsSelectionListener) l).isFor(alf, listener))
+      {
+        listeners.remove(i);
+        break;
+      }
+    }
+  }
+
+  private DataSourceType resolveFileProtocol(String[] retPath)
+  {
+    String path = retPath[0];
+    /*
+     * is it paste data?
+     */
+    if (path.startsWith("PASTE"))
+    {
+      retPath[0] = path.substring(5);
+      return DataSourceType.PASTE;
+    }
+
+    /*
+     * is it a URL?
+     */
+    if (path.indexOf("://") >= 0)
+    {
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try relative to document root
+     */
+    URL documentBase = getDocumentBase();
+    String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
+    if (HttpUtils.isValidUrl(withDocBase))
+    {
+      // if (debug)
+      // {
+      // System.err.println("Prepended document base '" + documentBase
+      // + "' to make: '" + withDocBase + "'");
+      // }
+      retPath[0] = withDocBase;
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try relative to codebase (if different to document base)
+     */
+    URL codeBase = getCodeBase();
+    String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
+    if (!withCodeBase.equals(withDocBase)
+            && HttpUtils.isValidUrl(withCodeBase))
+    {
+      // if (debug)
+      // {
+      // System.err.println("Prepended codebase '" + codeBase
+      // + "' to make: '" + withCodeBase + "'");
+      // }
+      retPath[0] = withCodeBase;
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try locating by classloader; try this last so files in the directory
+     * are resolved using document base
+     */
+    if (inArchive(getClass(), path))
+    {
+      return DataSourceType.CLASSLOADER;
+    }
+    return null;
+  }
+
+  @Override
+  public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf)
+  {
+    // TODO test
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          (alf == null ? getCurrentAlignFrame() : alf).scrollTo(topRow,
+                  leftHandColumn);
+        } catch (Exception ex)
+        {
+          System.err.println("Couldn't parse integer arguments (topRow='"
+                  + topRow + "' and leftHandColumn='" + leftHandColumn
+                  + "')");
+          ex.printStackTrace();
+        }
+      }
+    });
+  }
+
+  @Override
+  public void select(String ids[], String cols[], AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    final SequenceGroup sel = new SequenceGroup();
+    final ColumnSelection csel = new ColumnSelection();
+    AlignmentI al = alf.getViewport().getAlignment();
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            alf.getViewport().getAlignment().getSequencesArray());
+    int start = 0, end = al.getWidth(), alw = al.getWidth();
+    boolean seqsfound = true;
+    if (ids != null && ids.length > 0)
+    {
+      seqsfound = false;
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          seqsfound = true;
+          sel.addSequence(sq, false);
+        }
+      }
+    }
+    boolean inseqpos = false;
+    if (cols != null && cols.length > 0)
+    {
+      boolean seset = false;
+      for (int i = 0; i < cols.length; i++)
+      {
+        String cl = cols[i].trim();
+        if (cl.length() == 0)
+        {
+          continue;
+        }
+        int p;
+        if ((p = cl.indexOf("-")) > -1)
+        {
+          int from = -1, to = -1;
+          try
+          {
+            from = Integer.valueOf(cl.substring(0, p)).intValue();
+            from--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse first integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          try
+          {
+            to = Integer.valueOf(cl.substring(p + 1)).intValue();
+            to--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse second integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          if (from >= 0 && to >= 0)
+          {
+            // valid range
+            if (from < to)
+            {
+              int t = to;
+              to = from;
+              to = t;
+            }
+            if (!seset)
+            {
+              start = from;
+              end = to;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > from)
+              {
+                start = from;
+              }
+              if (end < to)
+              {
+                end = to;
+              }
+            }
+            for (int r = from; r <= to; r++)
+            {
+              if (r >= 0 && r < alw)
+              {
+                csel.addElement(r);
+              }
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Range '" + cl
+                    + "' deparsed as [" + from + "," + to + "]");
+          }
+        }
+        else
+        {
+          int r = -1;
+          try
+          {
+            r = Integer.valueOf(cl).intValue();
+            r--;
+          } catch (NumberFormatException ex)
+          {
+            if (cl.toLowerCase().equals("sequence"))
+            {
+              // we are in the dataset sequence's coordinate frame.
+              inseqpos = true;
+            }
+            else
+            {
+              System.err.println(
+                      "ERROR: Couldn't parse integer from point selection element of column selection string '"
+                              + cl + "'");
+              return;
+            }
+          }
+          if (r >= 0 && r <= alw)
+          {
+            if (!seset)
+            {
+              start = r;
+              end = r;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > r)
+              {
+                start = r;
+              }
+              if (end < r)
+              {
+                end = r;
+              }
+            }
+            csel.addElement(r);
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Point selection '" + cl
+                    + "' deparsed as [" + r + "]");
+          }
+        }
+      }
+    }
+    if (seqsfound)
+    {
+      // we only propagate the selection when it was the null selection, or the
+      // given sequences were found in the alignment.
+      if (inseqpos && sel.getSize() > 0)
+      {
+        // assume first sequence provides reference frame ?
+        SequenceI rs = sel.getSequenceAt(0);
+        start = rs.findIndex(start);
+        end = rs.findIndex(end);
+        List<Integer> cs = new ArrayList<>(csel.getSelected());
+        csel.clear();
+        for (Integer selectedCol : cs)
+        {
+          csel.addElement(rs.findIndex(selectedCol));
+        }
+      }
+      sel.setStartRes(start);
+      sel.setEndRes(end);
+      AlignFrame af = alf;
+      EventQueue.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          af.select(sel, csel,
+                  af.getCurrentView().getAlignment().getHiddenColumns());
+        }
+      });
+    }
+  }
+
+  //
+  // @Override
+  // public void setFeatureGroupState(String[] groups, boolean state)
+  // {
+  // setFeatureGroupState(null, groups, state);
+  // }
+  //
+  // @Override
+  // public void setFeatureGroupState(String[] groups, boolean state)
+  // { // JalviewLite API
+  // setFeatureGroupStateOn(null, groups, state);
+  // }
+  //
+  @Override
+  public void setFeatureGroupState(final String[] groups,
+          boolean state, AlignFrame alf)
+  {
+    // setFeatureGroupState(alf, groups, state);
+    // java.awt.EventQueue.invokeLater(new Runnable()
+    // {
+    // @Override
+    // public void run()
+    // {
+    // (alf == null ? getCurrentAlignFrame() : alf)
+    // .setFeatureGroupState(
+    // separatorListToArray(groups, separator), state);
+    // }
+    // });
+    // }
+    //
+    // public void setFeatureGroupState(AlignFrame alf, String[] groups, boolean
+    // state) {
+    (alf == null ? getCurrentAlignFrame() : alf)
+            .setFeatureGroupState(groups, state);
+  }
+
+  @Override
+  public void setSelectionListener(String listener, AlignFrame alf)
+  {
+    Desktop.getStructureSelectionManager()
+            .addSelectionListener(new JsSelectionListener(alf, listener));
+  }
+
+  @Override
+  public void showOverview()
+  {
+    getCurrentAlignFrame().overviewMenuItem_actionPerformed(null);
+  }
+
+  /**
+   * @j2sAlias showStructure
+   */
+  @Override
+  public void showStructure(String pdbID, String fileType, AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    PDBEntry pe = null;
+    SequenceI[] seqs = null;
+    if (pdbID == null)
+    {
+      seqs = alf.getViewport().getSequenceSelection();
+      if (seqs.length == 0)
+        seqs = alf.getViewport().getAlignment().getSequencesArray();
+      for (int i = 0; i < seqs.length; i++)
+      {
+        Vector<PDBEntry> list = seqs[i].getAllPDBEntries();
+        if (list.size() > 0)
+        {
+          pe = list.get(0);
+          break;
+        }
+      }
+    }
+    if (pe == null)
+    {
+      if (pdbID == null)
+        return;
+      pe = new PDBEntry(pdbID, null, fileType);
+      List<SequenceI> list = alf.getViewport().getAlignment()
+              .getSequences();
+      List<SequenceI> tmp = new ArrayList<SequenceI>();
+      for (int i = 0; i < list.size(); i++)
+      {
+        SequenceI seq = list.get(i);
+        if (seq.getPDBEntry(pdbID) != null)
+        {
+          tmp.add(seq);
+        }
+      }
+      seqs = tmp.toArray(new SequenceI[tmp.size()]);
+      alf.alignPanel.selectSequences(tmp);
+    }
+    StructureViewer.launchStructureViewer(alf.alignPanel, pe, seqs);
+  }
+
+  // private or package-private methods
+
+  /**
+   * form a complete URL given a path to a resource and a reference location on
+   * the same server
+   * 
+   * @param targetPath
+   *          - an absolute path on the same server as localref or a document
+   *          located relative to localref
+   * @param localref
+   *          - a URL on the same server as url
+   * @return a complete URL for the resource located by url
+   */
+  private static String resolveUrlForLocalOrAbsolute(String targetPath,
+          URL localref)
+  {
+    String resolvedPath = "";
+    if (targetPath.startsWith("/"))
+    {
+      String codebase = localref.toString();
+      String localfile = localref.getFile();
+      resolvedPath = codebase.substring(0,
+              codebase.length() - localfile.length()) + targetPath;
+      return resolvedPath;
+    }
+
+    /*
+     * get URL path and strip off any trailing file e.g.
+     * www.jalview.org/examples/index.html#applets?a=b is trimmed to
+     * www.jalview.org/examples/
+     */
+    String urlPath = localref.toString();
+    String directoryPath = urlPath;
+    int lastSeparator = directoryPath.lastIndexOf("/");
+    if (lastSeparator > 0)
+    {
+      directoryPath = directoryPath.substring(0, lastSeparator + 1);
+    }
+
+    if (targetPath.startsWith("/"))
+    {
+      /*
+       * construct absolute URL to a file on the server - this is not allowed?
+       */
+      // String localfile = localref.getFile();
+      // resolvedPath = urlPath.substring(0,
+      // urlPath.length() - localfile.length())
+      // + targetPath;
+      resolvedPath = directoryPath + targetPath.substring(1);
+    }
+    else
+    {
+      resolvedPath = directoryPath + targetPath;
+    }
+    // if (debug)
+    // {
+    // System.err.println(
+    // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
+    // }
+    return resolvedPath;
+  }
+
+  /**
+   * parse the string into a list
+   * 
+   * @param list
+   * @param separator
+   * @return elements separated by separator
+   */
+  private static String[] separatorListToArray(String list,
+          String separator)
+  {
+    // TODO use StringUtils version (slightly different...)
+    int seplen = separator.length();
+    if (list == null || list.equals("") || list.equals(separator))
+    {
+      return null;
+    }
+    Vector<String> jv = new Vector<>();
+    int cp = 0, pos;
+    while ((pos = list.indexOf(separator, cp)) > cp)
+    {
+      jv.addElement(list.substring(cp, pos));
+      cp = pos + seplen;
+    }
+    if (cp < list.length())
+    {
+      String c = list.substring(cp);
+      if (!c.equals(separator))
+      {
+        jv.addElement(c);
+      }
+    }
+    if (jv.size() > 0)
+    {
+      String[] v = new String[jv.size()];
+      for (int i = 0; i < v.length; i++)
+      {
+        v[i] = jv.elementAt(i);
+      }
+      jv.removeAllElements();
+      return v;
+    }
+    return null;
+  }
+
+  /**
+   * Discovers whether the given file is in the Applet Archive
+   * 
+   * @param f
+   *          String
+   * @return boolean
+   */
+  private static boolean inArchive(Class<?> c, String f)
+  {
+    // This might throw a security exception in certain browsers
+    // Netscape Communicator for instance.
+    try
+    {
+      boolean rtn = (c.getResourceAsStream("/" + f) != null);
+      return rtn;
+    } catch (Exception ex)
+    {
+      System.out.println("Exception checking resources: " + f + " " + ex);
+      return false;
+    }
+  }
+
+  /**
+   * Allowing for a JavaScript function here.
+   */
+  void callInitCallback()
+  {
+    Object initjscallback = getParameterAsObject("oninit");
+    if (initjscallback != null)
+    {
+      SwingUtilities.invokeLater(new Runnable() {
+
+        @Override
+        public void run()
+        {
+          try
+          {
+            doSendCallback(initjscallback, new Object[] {this});
+          } catch (Exception e)
+          {
+            System.err.println("Exception when executing _oninit callback '"
+                    + initjscallback + "'.");
+            e.printStackTrace();
+          }
+        }
+        
+      });
+    }
+  }
+
+  /**
+   * Pass the provided array prepended with Jalview.this
+   * 
+   * Appropriated from org.jmol.appletjs.Jmol
+   * 
+   * @param callback
+   *          a window function or "alert"
+   * @param data
+   * @return String return from the callback method.
+   */
+  String doSendCallback(Object callback, Object[] data)
+  {
+    Jalview me = jalview;
+
+    if (me != null && callback != null)
+    {
+      /**
+       * @j2sNative
+       * 
+       *            try{
+       * 
+       *            if (callback == "alert") { alert(data[0]); return ""; } var
+       *            o; if (typeof callback == "function") { o = callback; } else
+       *            { if (!callback)return; var tokens = callback.split("."); o
+       *            = window[tokens[0]]; for (var i = 1; i < tokens.length; i++)
+       *            o = o[tokens[i]]; } var a = [me]; for (var i = 0; i <
+       *            data.length; i++) a.push(data[i] ? data[i].booleanValue &&
+       *            (data[i] = data[i].booleanValue()) : data[i]); return
+       *            o.apply(null,a) } catch (e) { System.out.println(callback +
+       *            " failed " + e); }
+       */
+    }
+    return "";
+  }
+
+  /**
+   * Initialize from Info.key/value pairs that match the old JalviewLite applet
+   * parameters.
+   * 
+   * See http://www.jalview.org/old/v2_8/examples/appletParameters.html
+   * 
+   * Note that some of these parameters are handled as command-line arguments,
+   * as determined in ArgsParser.
+   * 
+   * @param alf
+   */
+  void initFromParams(AlignFrame alf)
+  {
+    String sep = getParameter("separator");
+    if (sep != null && sep.length() > 0)
+    {
+      separator = sep;
+    }
+    initTree(alf);
+    initScoreFile(alf);
+    initFeatures(alf);
+    initAnnotations(alf);
+    initJnetFile(alf);
+    initPdbFiles(alf);
+  }
+
+  /**
+   * Load annotations if specified by parameter. Returns true if loaded, else
+   * false.
+   * 
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initAnnotations(AlignFrame alf)
+  {
+
+    String param = getParameter("annotations");
+    if (param == null)
+      return false;
+    ret[0] = param;
+    DataSourceType protocol = resolveFileProtocol(ret);
+    param = ret[0];
+    if (!new AnnotationFile().annotateAlignmentView(alf.getViewport(),
+            param, protocol))
+    {
+      System.err.println("Annotations were not added from annotation file '"
+              + param + "'");
+      return false;
+    }
+    updateForAnnotations();
+    return true;
+  }
+
+  /**
+   * Load features file and view settings as specified by parameters. Returns
+   * true if features were loaded, else false.
+   * 
+   * @param
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initFeatures(AlignFrame alf)
+  {
+
+    // ///////////////////////////
+    // modify display of features
+    // we do this before any features have been loaded, ensuring any hidden
+    // groups are hidden when features first displayed
+    //
+    // hide specific groups
+    //
+    String param = getParameter("hidefeaturegroups");
+    if (param != null)
+    {
+      setFeatureGroupState(separatorListToArray(param, separator),
+              false, alf);
+      // setFeatureGroupStateOn(newAlignFrame, param, false);
+    }
+    // show specific groups
+    param = getParameter("showfeaturegroups");
+    if (param != null)
+    {
+      setFeatureGroupState(separatorListToArray(param, separator),
+              true, alf);
+      // setFeatureGroupStateOn(newAlignFrame, param, true);
+    }
+    // and now load features
+    param = getParameter("features");
+    if (param == null)
+    {
+      return false;
+    }
+    if (!parseFeaturesFile(param, alf))
+      return false;
+    param = getParameter("showFeatureSettings");
+    if (param != null && param.equalsIgnoreCase("true"))
+    {
+      alf.showFeatureSettingsUI();
+    }
+    return true;
+  }
+
+  /**
+   * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
+   * false.
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initJnetFile(AlignFrame alf)
+  {
+
+    String param = getParameter("jnetfile");
+    if (param == null)
+    {
+      // jnet became jpred around 2016
+      param = getParameter("jpredfile");
+    }
+    if (param != null)
+    {
+      try
+      {
+        ret[0] = param;
+        DataSourceType protocol = resolveFileProtocol(ret);
+        JPredFile predictions = new JPredFile(ret[0], protocol);
+        JnetAnnotationMaker.add_annotation(predictions,
+                alf.getViewport().getAlignment(), 0, false);
+        // false == do not add sequence profile from concise output
+        alf.getViewport().getAlignment().setupJPredAlignment();
+        updateForAnnotations();
+      } catch (Exception ex)
+      {
+        ex.printStackTrace();
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
+   * else false.
+   * 
+   * @param loaderFrame
+   * @return
+   */
+  private boolean initPdbFiles(AlignFrame alf)
+  {
+
+    /*
+     * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
+     * related to JAL-434
+     */
+
+    // not supported (as for JalviewLite)
+    // boolean doAlign = false;//"true".equalsIgnoreCase("" +
+    // getAppletParameter("alignpdbfiles", false));
+    // setAlignPdbStructures(doAlign);
+    /*
+     * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
+     * PDB|1GAQ|1GAQ|C">
+     * 
+     * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
+     * 
+     * <param name="PDBfile3" value="1q0o Q45135_9MICO">
+     */
+
+    // Accumulate pdbs here if they are heading for the same view (if
+    // alignPdbStructures is true)
+    // ArrayList<Object[]> pdbs = new ArrayList<>();
+    // init a lazy matcher if we're asked to
+    boolean relaxed = "true"
+            .equalsIgnoreCase(getParameter("relaxedidmatch"));
+    jalview.analysis.SequenceIdMatcher matcher = relaxed
+            ? new jalview.analysis.SequenceIdMatcher(
+                    alf.getViewport().getAlignment().getSequencesArray())
+            : null;
+
+    String param = getParameter("PDBFILE");
+    int plast = (param == null ? 9 : 1);
+    if (param == null && (param = getParameter("PDBFILE1")) == null)
+    {
+      return false;
+    }
+    for (int p = 1; p <= plast; p++)
+    {
+      if (p > 1)
+      {
+        param = getParameter("PDBFILE" + p);
+        if (param == null)
+          break;
+      }
+      PDBEntry pdb = new PDBEntry();
+
+      String seqstring;
+      SequenceI[] seqs = null;
+      String[] chains = null;
+
+      StringTokenizer st = new StringTokenizer(param, " ");
+
+      if (st.countTokens() < 2)
+      {
+        String sequence = getParameter("PDBSEQ");
+        if (sequence != null)
+        {
+          seqs = new SequenceI[] { matcher == null
+                  ? (Sequence) alf.getViewport().getAlignment()
+                          .findName(sequence)
+                  : matcher.findIdMatch(sequence) };
+        }
+
+      }
+      else
+      {
+        param = st.nextToken();
+        List<SequenceI> tmp = new ArrayList<>();
+        List<String> tmp2 = new ArrayList<>();
+
+        while (st.hasMoreTokens())
+        {
+          seqstring = st.nextToken();
+          StringTokenizer st2 = new StringTokenizer(seqstring, "=");
+          if (st2.countTokens() > 1)
+          {
+            // This is the chain
+            tmp2.add(st2.nextToken());
+            seqstring = st2.nextToken();
+          }
+          tmp.add(matcher == null
+                  ? (Sequence) alf.getViewport().getAlignment()
+                          .findName(seqstring)
+                  : matcher.findIdMatch(seqstring));
+        }
+
+        seqs = tmp.toArray(new SequenceI[tmp.size()]);
+        if (tmp2.size() == tmp.size())
+        {
+          chains = tmp2.toArray(new String[tmp2.size()]);
+        }
+      }
+      pdb.setId(param);
+      ret[0] = param;
+      DataSourceType protocol = resolveFileProtocol(ret);
+      // TODO check JAL-357 for files in a jar (CLASSLOADER)
+      pdb.setFile(ret[0]);
+
+      if (seqs != null)
+      {
+        for (int i = 0; i < seqs.length; i++)
+        {
+          if (seqs[i] != null)
+          {
+            ((Sequence) seqs[i]).addPDBId(pdb);
+            StructureSelectionManager
+                    .getStructureSelectionManager(
+                            (StructureSelectionManagerProvider) this)
+                    .registerPDBEntry(pdb);
+          }
+        }
+
+        // if (doAlign)
+        // {
+        // pdbs.add(new Object[] { pdb, seqs, chains, protocol });
+        // }
+        // else
+        {
+          StructureViewer.launchStructureViewer(
+                  (alf == null ? getCurrentAlignFrame() : alf).alignPanel,
+                  pdb, seqs);
+        }
+      }
+    }
+    //
+    // if (doAlign && pdbs.size() > 0)
+    // {
+    // SequenceI[][] seqs = new SequenceI[pdbs.size()][];
+    // PDBEntry[] pdb = new PDBEntry[pdbs.size()];
+    // String[][] chains = new String[pdbs.size()][];
+    // String[] protocols = new String[pdbs.size()];
+    // for (int pdbsi = 0, pdbsiSize = pdbs
+    // .size(); pdbsi < pdbsiSize; pdbsi++)
+    // {
+    // Object[] o = pdbs.get(pdbsi);
+    // pdb[pdbsi] = (PDBEntry) o[0];
+    // seqs[pdbsi] = (SequenceI[]) o[1];
+    // chains[pdbsi] = (String[]) o[2];
+    // protocols[pdbsi] = (String) o[3];
+    // }
+    //// alignedStructureView(pdb, seqs, chains, protocols);
+    // result = true;
+    // }
+    return true;
+  }
+
+  /**
+   * Load a score file if specified by parameter. Returns true if file was
+   * loaded, else false.
+   * 
+   * @param loaderFrame
+   */
+  private boolean initScoreFile(AlignFrame alf)
+  {
+
+    String sScoreFile = getParameter("scoreFile");
+    if (sScoreFile != null && !"".equals(sScoreFile))
+    {
+      try
+      {
+        if (loadScoreFile(sScoreFile, alf))
+        {
+          return true;
+        }
+        System.err.println(
+                "Failed to parse T-COFFEE parameter as a valid score file ('"
+                        + sScoreFile + "')");
+      } catch (Exception e)
+      {
+        System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
+                sScoreFile, e.getMessage());
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Load a tree for the alignment if specified by parameter. Returns true if a
+   * tree was loaded, else false.
+   * 
+   * @return
+   */
+  private boolean initTree(AlignFrame alf)
+  {
+    String treeFile;
+    if ((treeFile = getParameter("tree")) == null
+            && (treeFile = getParameter("treefile")) == null)
+      return false;
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    try
+    {
+      ret[0] = treeFile;
+      NewickFile nf = new NewickFile(treeFile, resolveFileProtocol(ret));
+      nf.parse();
+      if (nf.getTree() != null)
+      {
+        treeFile = ret[0];
+        alf.getViewport()
+                .setCurrentTree(alf.showNewickTree(nf, treeFile).getTree());
+        return true;
+      }
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+    }
+    return false;
+  }
+
+  private void updateForAnnotations()
+  {
+    getCurrentAlignFrame().updateForAnnotations();
+  }
+}
index 7d66f6d..b6b5859 100644 (file)
@@ -47,10 +47,12 @@ import jalview.io.IdentifyFile;
 import jalview.io.JPredFile;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
-import jalview.javascript.JSFunctionExec;
-import jalview.javascript.JalviewLiteJsApi;
-import jalview.javascript.JsCallBack;
-import jalview.javascript.MouseOverStructureListener;
+import jalview.appletgui.js.JSFunctionExec;
+import jalview.appletgui.js.JalviewLiteJsApi;
+import jalview.appletgui.js.JsCallBack;
+import jalview.appletgui.js.JsSelectionSender;
+import jalview.appletgui.js.MouseOverListener;
+import jalview.appletgui.js.MouseOverStructureListener;
 import jalview.structure.SelectionListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.ColorUtils;
@@ -923,7 +925,7 @@ public class JalviewLite extends Applet
     setMouseoverListener(currentAlignFrame, listener);
   }
 
-  private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<>();
+  private Vector<JSFunctionExec> javascriptListeners = new Vector<>();
 
   /*
    * (non-Javadoc)
@@ -945,7 +947,7 @@ public class JalviewLite extends Applet
         return;
       }
     }
-    jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
+    MouseOverListener mol = new MouseOverListener(
             this, af, listener);
     javascriptListeners.addElement(mol);
     StructureSelectionManager.getStructureSelectionManager(this)
@@ -992,7 +994,7 @@ public class JalviewLite extends Applet
         return;
       }
     }
-    jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
+    JsSelectionSender mol = new JsSelectionSender(
             this, af, listener);
     javascriptListeners.addElement(mol);
     StructureSelectionManager.getStructureSelectionManager(this)
@@ -1131,7 +1133,7 @@ public class JalviewLite extends Applet
     {
       while (javascriptListeners.size() > 0)
       {
-        jalview.javascript.JSFunctionExec mol = javascriptListeners
+        JSFunctionExec mol = javascriptListeners
                 .elementAt(0);
         javascriptListeners.removeElement(mol);
         if (mol instanceof SelectionListener)
@@ -1158,7 +1160,7 @@ public class JalviewLite extends Applet
     StructureSelectionManager.release(this);
   }
 
-  private jalview.javascript.JSFunctionExec jsFunctionExec;
+  private JSFunctionExec jsFunctionExec;
 
   /*
    * (non-Javadoc)
@@ -1233,7 +1235,7 @@ public class JalviewLite extends Applet
    * (non-Javadoc)
    * 
    * @see
-   * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
+   * JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
   @Override
@@ -1264,7 +1266,7 @@ public class JalviewLite extends Applet
    * (non-Javadoc)
    * 
    * @see
-   * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
+   * JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
   @Override
index a2a152a..5232f8e 100755 (executable)
@@ -292,6 +292,32 @@ public class Alignment implements AlignmentI, AutoCloseable
   }
 
   /**
+   * Inserts a sequence at a point in the alignment.
+   * 
+   * @param i
+   *          the index of the position the sequence is to be inserted in.
+   */
+  @Override
+  public void insertSequenceAt(int i, SequenceI snew)
+  {
+    synchronized (sequences)
+    {
+      if (sequences.size() > i)
+      {
+        sequences.add(i, snew);
+        return;
+
+      }
+      else
+      {
+        sequences.add(snew);
+        hiddenSequences.adjustHeightSequenceAdded();
+      }
+      return;
+    }
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
@@ -591,7 +617,8 @@ public class Alignment implements AlignmentI, AutoCloseable
   @Override
   public SequenceI findName(SequenceI startAfter, String token, boolean b)
   {
-
+    if (token == null)
+      return null;
     int i = 0;
     SequenceI sq = null;
     String sqname = null;
@@ -1605,34 +1632,50 @@ public class Alignment implements AlignmentI, AutoCloseable
           String calcId, boolean autoCalc, SequenceI seqRef,
           SequenceGroup groupRef)
   {
-    if (annotations != null)
+    AlignmentAnnotation annot = annotations == null ? null
+            : AlignmentAnnotation.findFirstAnnotation(
+              Arrays.asList(getAlignmentAnnotation()), name, calcId,
+              autoCalc, seqRef, groupRef);
+
+    if (annot == null)
     {
-      for (AlignmentAnnotation annot : getAlignmentAnnotation())
+
+      annot = new AlignmentAnnotation(name, name, new Annotation[1], 0f, 0f,
+              AlignmentAnnotation.BAR_GRAPH);
+      annot.hasText = false;
+      if (calcId != null)
       {
-        if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
-                && (calcId == null || annot.getCalcId().equals(calcId))
-                && annot.sequenceRef == seqRef
-                && annot.groupRef == groupRef)
-        {
-          return annot;
-        }
+        annot.setCalcId(calcId);
+      }
+      annot.autoCalculated = autoCalc;
+      if (seqRef != null)
+      {
+        annot.setSequenceRef(seqRef);
       }
+      annot.groupRef = groupRef;
+      addAnnotation(annot);
     }
-    AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
-            new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
-    annot.hasText = false;
-    if (calcId != null)
+    return annot;
+  }
+
+
+  @Override
+  public AlignmentAnnotation updateFromOrCopyAnnotation(
+          AlignmentAnnotation ala)
+  {
+    AlignmentAnnotation annot = AlignmentAnnotation.findFirstAnnotation(
+            Arrays.asList(getAlignmentAnnotation()), ala.label, ala.calcId,
+            ala.autoCalculated, ala.sequenceRef, ala.groupRef);
+    if (annot == null)
     {
-      annot.setCalcId(new String(calcId));
+      annot = new AlignmentAnnotation(ala);
+      addAnnotation(annot);
     }
-    annot.autoCalculated = autoCalc;
-    if (seqRef != null)
+    else
     {
-      annot.setSequenceRef(seqRef);
+      annot.updateAlignmentAnnotationFrom(ala);
     }
-    annot.groupRef = groupRef;
-    addAnnotation(annot);
-
+    validateAnnotation(annot);
     return annot;
   }
 
@@ -2032,4 +2075,18 @@ public class Alignment implements AlignmentI, AutoCloseable
     }
   }
 
+  @Override
+  public List<SequenceI> getHmmSequences()
+  {
+    List<SequenceI> result = new ArrayList<>();
+    for (int i = 0; i < sequences.size(); i++)
+    {
+      SequenceI seq = sequences.get(i);
+      if (seq.hasHMMProfile())
+      {
+        result.add(seq);
+      }
+    }
+    return result;
+  }
 }
index 0f41850..4ebb27f 100755 (executable)
@@ -95,6 +95,10 @@ public class AlignmentAnnotation
    */
   private long invalidrnastruc = -2;
 
+  private double bitScore;
+
+  private double eValue;
+
   /**
    * Updates the _rnasecstr field Determines the positions that base pair and
    * the positions of helices based on secondary structure from a Stockholm file
@@ -596,7 +600,7 @@ public class AlignmentAnnotation
     }
     return null;
   }
-
+  
   /**
    * Creates a new AlignmentAnnotation object.
    * 
@@ -708,7 +712,7 @@ public class AlignmentAnnotation
       }
     }
   }
-
+  
   /**
    * Copy constructor creates a new independent annotation row with the same
    * associated sequenceRef
@@ -718,6 +722,17 @@ public class AlignmentAnnotation
   public AlignmentAnnotation(AlignmentAnnotation annotation)
   {
     setAnnotationId();
+    updateAlignmentAnnotationFrom(annotation);
+  }
+
+  /**
+   * copy attributes and annotation from an existing annotation (used by copy
+   * constructor). This method does not update the unique annotationId
+   * 
+   * @param annotation
+   */
+  public void updateAlignmentAnnotationFrom(AlignmentAnnotation annotation)
+  {
     this.label = new String(annotation.label);
     if (annotation.description != null)
     {
@@ -741,6 +756,9 @@ public class AlignmentAnnotation
     this.scaleColLabel = annotation.scaleColLabel;
     this.showAllColLabels = annotation.showAllColLabels;
     this.calcId = annotation.calcId;
+    this.bitScore = annotation.bitScore;
+    this.eValue = annotation.eValue;
+
     if (annotation.properties != null)
     {
       properties = new HashMap<>();
@@ -972,6 +990,7 @@ public class AlignmentAnnotation
    * @param seqRef
    * @param startRes
    * @param alreadyMapped
+   *          - annotation are at aligned columns
    */
   public void createSequenceMapping(SequenceI seqRef, int startRes,
           boolean alreadyMapped)
@@ -1173,7 +1192,7 @@ public class AlignmentAnnotation
   {
     return hasScore || !Double.isNaN(score);
   }
-
+  
   /**
    * Score only annotation
    * 
@@ -1204,6 +1223,7 @@ public class AlignmentAnnotation
     makeVisibleAnnotation(hidden);
   }
 
+
   public void setPadGaps(boolean padgaps, char gapchar)
   {
     this.padGaps = padgaps;
@@ -1663,7 +1683,6 @@ public class AlignmentAnnotation
           Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
           String label)
   {
-
     ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
     for (AlignmentAnnotation ann : list)
     {
@@ -1708,7 +1727,6 @@ public class AlignmentAnnotation
   public static Iterable<AlignmentAnnotation> findAnnotation(
           List<AlignmentAnnotation> list, String calcId)
   {
-
     List<AlignmentAnnotation> aa = new ArrayList<>();
     if (calcId == null)
     {
@@ -1726,4 +1744,42 @@ public class AlignmentAnnotation
     return aa;
   }
 
+  public double getBitScore()
+  {
+    return bitScore;
+  }
+
+  public void setBitScore(double bitScore)
+  {
+    this.bitScore = bitScore;
+  }
+
+  public double getEValue()
+  {
+    return eValue;
+  }
+
+  public void setEValue(double eValue)
+  {
+    this.eValue = eValue;
+  }
+
+  public static AlignmentAnnotation findFirstAnnotation(
+          Iterable<AlignmentAnnotation> alignmentAnnotation, String name,
+          String calcId, boolean autoCalc, SequenceI seqRef,
+          SequenceGroup groupRef)
+  {
+
+    for (AlignmentAnnotation annot : alignmentAnnotation)
+    {
+      if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
+              && (calcId == null || annot.getCalcId().equals(calcId))
+              && annot.sequenceRef == seqRef && annot.groupRef == groupRef)
+      {
+        return annot;
+      }
+    }
+    return null;
+  }
+
 }
index 93a2456..cea1c5b 100755 (executable)
@@ -518,8 +518,6 @@ public interface AlignmentI extends AnnotatedCollectionI
    *          - null or specific sequence reference
    * @param groupRef
    *          - null or specific group reference
-   * @param method
-   *          - CalcId for the annotation (must match)
    * 
    * @return existing annotation matching the given attributes
    */
@@ -527,6 +525,17 @@ public interface AlignmentI extends AnnotatedCollectionI
           boolean autoCalc, SequenceI seqRef, SequenceGroup groupRef);
 
   /**
+   * like findOrCreateAnnotation - looks for an existing alignment annotation
+   * row with matching name, calcId, sequenceRef, groupRef and autoCalculated
+   * flag and updates it from the annotation. If none is found the annotation is
+   * added directly.
+   * 
+   * @param ala
+   * @return ala or the annotation row that was updated.
+   */
+  AlignmentAnnotation updateFromOrCopyAnnotation(AlignmentAnnotation ala);
+
+  /**
    * move the given group up or down in the alignment by the given number of
    * rows. Implementor assumes given group is already present on alignment - no
    * recalculations are triggered.
@@ -604,6 +613,16 @@ public interface AlignmentI extends AnnotatedCollectionI
   public boolean setHiddenColumns(HiddenColumns cols);
 
   /**
+   * Insert a sequence at a position in an alignment
+   * 
+   * @param i
+   *          The index of the position.
+   * @param snew
+   *          The new sequence.
+   */
+  void insertSequenceAt(int i, SequenceI snew);
+
+  /**
    * Set the first sequence as representative and hide its insertions. Typically
    * used when loading JPred files.
    */
@@ -623,5 +642,4 @@ public interface AlignmentI extends AnnotatedCollectionI
    */
   public HiddenColumns propagateInsertions(SequenceI profileseq,
           AlignmentView input);
-
 }
index 6d09145..ef46d79 100755 (executable)
@@ -80,7 +80,7 @@ public class AlignmentOrder
    */
   public AlignmentOrder(AlignmentI orderFrom)
   {
-    Order = new ArrayList<SequenceI>();
+    Order = new ArrayList<>();
 
     for (SequenceI seq : orderFrom.getSequences())
     {
@@ -96,7 +96,7 @@ public class AlignmentOrder
    */
   public AlignmentOrder(SequenceI[] orderFrom)
   {
-    Order = new ArrayList<SequenceI>(Arrays.asList(orderFrom));
+    Order = new ArrayList<>(Arrays.asList(orderFrom));
   }
 
   /**
index e6604d1..c00f0b1 100644 (file)
@@ -25,6 +25,7 @@ import jalview.util.ShiftList;
 
 import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -114,6 +115,16 @@ public class AlignmentView
     {
       return seqs.size();
     }
+
+    public SequenceGroup getNewSequenceGroup(char c)
+    {
+      SequenceGroup newsg = new SequenceGroup(sg);
+      for (SeqCigar seq : seqs)
+      {
+        newsg.addSequence(seq.getSeq(c), false);
+      }
+      return newsg;
+    }
   }
 
   /**
@@ -1237,4 +1248,21 @@ public class AlignmentView
     }
 
   }
+
+  /**
+   * return pruned visible sequences in each group in alignment view
+   * 
+   * @param c
+   * @return
+   */
+  public Collection<? extends AnnotatedCollectionI> getVisibleGroups(char c)
+  {
+    ArrayList<SequenceGroup> groups = new ArrayList<>();
+    for (ScGroup sc : scGroups)
+    {
+      SequenceGroup sg = sc.getNewSequenceGroup(c);
+      groups.add(sg);
+    }
+    return groups;
+  }
 }
index 2963fd5..878f22a 100644 (file)
@@ -61,4 +61,6 @@ public interface AnnotatedCollectionI extends SequenceCollectionI
    *         alignment, parent group).
    */
   AnnotatedCollectionI getContext();
+
+  
 }
diff --git a/src/jalview/datamodel/HMMNode.java b/src/jalview/datamodel/HMMNode.java
new file mode 100644 (file)
index 0000000..b646eee
--- /dev/null
@@ -0,0 +1,148 @@
+package jalview.datamodel;
+
+/**
+ * stores data for each node in the hmm model
+ * @author TZVanaalten
+ *
+ */
+public class HMMNode
+{
+  //contains the match emissions for each symbol 
+  double[] matchEmissions;
+
+  //contains the insert emissions for each symbol 
+  double[] insertEmissions;
+
+  // contains the state transitions for each possible transition. These are mm,
+  // mi, md, im, ii, dm and dd in order
+  double[] stateTransitions;
+  
+  //annotations
+  int residueNumber;
+  char consensusResidue;
+  char referenceAnnotation;
+  char maskValue;
+  char consensusStructure;
+
+  /**
+   * Constructor
+   */
+  public HMMNode()
+  {
+  }
+
+  public double[] getMatchEmissions()
+  {
+    return matchEmissions;
+  }
+
+  double getMatchEmission(int symbolIndex)
+  {
+    return matchEmissions[symbolIndex];
+  }
+
+  public void setMatchEmissions(double[] matches)
+  {
+    this.matchEmissions = matches;
+  }
+
+  public double[] getInsertEmissions()
+  {
+    return insertEmissions;
+  }
+
+  double getInsertEmission(int symbolIndex)
+  {
+    return insertEmissions[symbolIndex];
+  }
+
+  public void setInsertEmissions(double[] insertEmissionsL)
+  {
+    this.insertEmissions = insertEmissionsL;
+  }
+
+  public double[] getStateTransitions()
+  {
+    return stateTransitions;
+  }
+
+  double getStateTransition(int transition)
+  {
+    return stateTransitions[transition];
+  }
+
+  public void setStateTransitions(double[] stateTransitionsM)
+  {
+    this.stateTransitions = stateTransitionsM;
+  }
+
+  int getResidueNumber()
+  {
+    return residueNumber;
+  }
+  public void setResidueNumber(int resNo)
+  {
+    this.residueNumber = resNo;
+  }
+
+  char getConsensusResidue()
+  {
+    return consensusResidue;
+  }
+  public void setConsensusResidue(char consensusResidue)
+  {
+    this.consensusResidue = consensusResidue;
+  }
+
+  char getReferenceAnnotation()
+  {
+    return referenceAnnotation;
+  }
+  public void setReferenceAnnotation(char referenceAnnotation)
+  {
+    this.referenceAnnotation = referenceAnnotation;
+  }
+
+  char getMaskValue()
+  {
+    return maskValue;
+  }
+  public void setMaskValue(char maskValue)
+  {
+    this.maskValue = maskValue;
+  }
+
+  char getConsensusStructure()
+  {
+    return consensusStructure;
+  }
+  public void setConsensusStructure(char consensusStructure)
+  {
+    this.consensusStructure = consensusStructure;
+  }
+
+  /**
+   * Answers the symbol index of the symbol with the highest match emission
+   * probability (first symbol in case of a tie). Note this object stores
+   * probabilities, not the negative logarithms as in the HMM file.
+   * 
+   * @return
+   */
+  int getMaxMatchEmissionIndex()
+  {
+    int maxIndex = 0;
+    double max = 0D;
+
+    for (int i = 0; i < matchEmissions.length; i++)
+    {
+      if (matchEmissions[i] > max)
+      {
+        max = matchEmissions[i];
+        maxIndex = i;
+      }
+    }
+    return maxIndex;
+  }
+}
+   
+  
diff --git a/src/jalview/datamodel/HiddenMarkovModel.java b/src/jalview/datamodel/HiddenMarkovModel.java
new file mode 100644 (file)
index 0000000..6b8b095
--- /dev/null
@@ -0,0 +1,672 @@
+package jalview.datamodel;
+
+import jalview.io.HMMFile;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.MapList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data structure which stores a hidden Markov model
+ * 
+ * @author TZVanaalten
+ * 
+ */
+public class HiddenMarkovModel
+{
+  private static final char GAP_DASH = '-';
+
+  public final static String YES = "yes";
+
+  public final static String NO = "no";
+
+  public static final int MATCHTOMATCH = 0;
+
+  public static final int MATCHTOINSERT = 1;
+
+  public static final int MATCHTODELETE = 2;
+
+  public static final int INSERTTOMATCH = 3;
+
+  public static final int INSERTTOINSERT = 4;
+
+  public static final int DELETETOMATCH = 5;
+
+  public static final int DELETETODELETE = 6;
+
+  private static final double LOG2 = Math.log(2);
+
+  /*
+   * properties read from HMM file header lines
+   */
+  private Map<String, String> fileProperties = new HashMap<>();
+
+  private String fileHeader;
+  
+  /*
+   * the symbols used in this model e.g. "ACGT"
+   */
+  private String alphabet;
+
+  /*
+   * symbol lookup index into the alphabet for 'A' to 'Z'
+   */
+  private int[] symbolIndexLookup = new int['Z' - 'A' + 1];
+
+  /*
+   * Nodes in the model. The begin node is at index 0, and contains 
+   * average emission probabilities for each symbol.
+   */
+  private List<HMMNode> nodes = new ArrayList<>();
+
+  /*
+   * the aligned HMM consensus sequence extracted from the HMM profile
+   */
+  private SequenceI hmmSeq;
+
+  /*
+   * mapping from HMM nodes to residues of the hmm consensus sequence
+   */
+  private Mapping mapToHmmConsensus;
+
+  // stores background frequencies of alignment from which this model came
+  private Map<Character, Float> backgroundFrequencies;
+
+  /**
+   * Constructor
+   */
+  public HiddenMarkovModel()
+  {
+  }
+
+  /**
+   * Copy constructor given a new aligned sequence with which to associate the
+   * HMM profile
+   * 
+   * @param hmm
+   * @param sq
+   */
+  public HiddenMarkovModel(HiddenMarkovModel hmm, SequenceI sq)
+  {
+    super();
+    this.fileProperties = new HashMap<>(hmm.fileProperties);
+    this.alphabet = hmm.alphabet;
+    this.nodes = new ArrayList<>(hmm.nodes);
+    this.symbolIndexLookup = hmm.symbolIndexLookup;
+    this.fileHeader = new String(hmm.fileHeader);
+    this.hmmSeq = sq;
+    this.backgroundFrequencies = hmm.getBackgroundFrequencies();
+    if (sq.getDatasetSequence() == hmm.mapToHmmConsensus.getTo())
+    {
+      // same dataset sequence e.g. after realigning search results
+      this.mapToHmmConsensus = hmm.mapToHmmConsensus;
+    }
+    else
+    {
+      // different dataset sequence e.g. after loading HMM from project
+      this.mapToHmmConsensus = new Mapping(sq.getDatasetSequence(),
+              hmm.mapToHmmConsensus.getMap());
+    }
+  }
+
+  /**
+   * Returns the information content at a specified column, calculated as the
+   * sum (over possible symbols) of the log ratio
+   * 
+   * <pre>
+   *  log(emission probability / background probability) / log(2)
+   * </pre>
+   * 
+   * @param column
+   *          column position (base 0)
+   * @return
+   */
+  public float getInformationContent(int column)
+  {
+    float informationContent = 0f;
+
+    for (char symbol : getSymbols().toCharArray())
+    {
+      float freq = ResidueProperties.backgroundFrequencies
+              .get(getAlphabetType()).get(symbol);
+      float prob = (float) getMatchEmissionProbability(column, symbol);
+      informationContent += prob * Math.log(prob / freq);
+    }
+
+    informationContent = informationContent / (float) LOG2;
+
+    return informationContent;
+  }
+
+  /**
+   * Gets the file header of the .hmm file this model came from
+   * 
+   * @return
+   */
+  public String getFileHeader()
+  {
+    return fileHeader;
+  }
+
+  /**
+   * Sets the file header of this model.
+   * 
+   * @param header
+   */
+  public void setFileHeader(String header)
+  {
+    fileHeader = header;
+  }
+
+  /**
+   * Returns the symbols used in this hidden Markov model
+   * 
+   * @return
+   */
+  public String getSymbols()
+  {
+    return alphabet;
+  }
+  
+  /**
+   * Gets the node in the hidden Markov model at the specified position.
+   * 
+   * @param nodeIndex
+   *          The index of the node requested. Node 0 optionally contains the
+   *          average match emission probabilities across the entire model, and
+   *          always contains the insert emission probabilities and state
+   *          transition probabilities for the begin node. Node 1 contains the
+   *          first node in the HMM that can correspond to a column in the
+   *          alignment.
+   * @return
+   */
+  public HMMNode getNode(int nodeIndex)
+  {
+    return nodes.get(nodeIndex);
+  }
+
+  /**
+   * Returns the name of the sequence alignment on which the HMM is based.
+   * 
+   * @return
+   */
+  public String getName()
+  {
+    return fileProperties.get(HMMFile.NAME);
+  }
+  
+  /**
+   * Answers the string value of the property (parsed from an HMM file) for the
+   * given key, or null if the property is not present
+   * 
+   * @param key
+   * @return
+   */
+  public String getProperty(String key)
+  {
+    return fileProperties.get(key);
+  }
+
+  /**
+   * Answers true if the property with the given key is present with a value of
+   * "yes" (not case-sensitive), else false
+   * 
+   * @param key
+   * @return
+   */
+  public boolean getBooleanProperty(String key)
+  {
+    return YES.equalsIgnoreCase(fileProperties.get(key));
+  }
+
+  /**
+   * Returns the length of the hidden Markov model. The value returned is the
+   * LENG property if specified, else the number of nodes, excluding the begin
+   * node (which should be the same thing).
+   * 
+   * @return
+   */
+  public int getLength()
+  {
+    if (fileProperties.get(HMMFile.LENGTH) == null)
+    {
+      return nodes.size() - 1; // not counting BEGIN node
+    }
+    return Integer.parseInt(fileProperties.get(HMMFile.LENGTH));
+  }
+
+  /**
+   * Returns the value of mandatory property "ALPH" - "amino", "DNA", "RNA" are
+   * the options. Other alphabets may be added.
+   * 
+   * @return
+   */
+  public String getAlphabetType()
+  {
+    return fileProperties.get(HMMFile.ALPHABET);
+  }
+
+  /**
+   * Sets the model alphabet to the symbols in the given string (ignoring any
+   * whitespace), and returns the number of symbols
+   * 
+   * @param symbols
+   */
+  public int setAlphabet(String symbols)
+  {
+    String trimmed = symbols.toUpperCase().replaceAll("\\s", "");
+    int count = trimmed.length();
+    alphabet = trimmed;
+    symbolIndexLookup = new int['Z' - 'A' + 1];
+    Arrays.fill(symbolIndexLookup, -1);
+    int ignored = 0;
+
+    /*
+     * save the symbols in order, and a quick lookup of symbol position
+     */
+    for (short i = 0; i < count; i++)
+    {
+      char symbol = trimmed.charAt(i);
+      if (symbol >= 'A' && symbol <= 'Z'
+              && symbolIndexLookup[symbol - 'A'] == -1)
+      {
+        symbolIndexLookup[symbol - 'A'] = i;
+      }
+      else
+      {
+        System.err
+                .println(
+                        "Unexpected or duplicated character in HMM ALPHabet: "
+                                + symbol);
+        ignored++;
+      }
+    }
+    return count - ignored;
+  }
+
+  /**
+   * Answers the node of the model corresponding to an aligned column position
+   * (0...), or null if there is no such node
+   * 
+   * @param column
+   * @return
+   */
+  HMMNode getNodeForColumn(int column)
+  {
+    /*
+     * if the hmm consensus is gapped at the column,
+     * there is no corresponding node
+     */
+    if (Comparison.isGap(hmmSeq.getCharAt(column)))
+    {
+      return null;
+    }
+
+    /*
+     * find the node (if any) that is mapped to the
+     * consensus sequence residue position at the column
+     */
+    int seqPos = hmmSeq.findPosition(column);
+    int[] nodeNo = mapToHmmConsensus.getMap().locateInFrom(seqPos, seqPos);
+    if (nodeNo != null)
+    {
+      return getNode(nodeNo[0]);
+    }
+    return null;
+  }
+
+  /**
+   * Gets the match emission probability for a given symbol at a column in the
+   * alignment.
+   * 
+   * @param alignColumn
+   *          The index of the alignment column, starting at index 0. Index 0
+   *          usually corresponds to index 1 in the HMM.
+   * @param symbol
+   *          The symbol for which the desired probability is being requested.
+   * @return
+   * 
+   */
+  public double getMatchEmissionProbability(int alignColumn, char symbol)
+  {
+    HMMNode node = getNodeForColumn(alignColumn);
+    int symbolIndex = getSymbolIndex(symbol);
+    if (node != null && symbolIndex != -1)
+    {
+      return node.getMatchEmission(symbolIndex);
+    }
+    return 0D;
+  }
+
+  /**
+   * Gets the insert emission probability for a given symbol at a column in the
+   * alignment.
+   * 
+   * @param alignColumn
+   *          The index of the alignment column, starting at index 0. Index 0
+   *          usually corresponds to index 1 in the HMM.
+   * @param symbol
+   *          The symbol for which the desired probability is being requested.
+   * @return
+   * 
+   */
+  public double getInsertEmissionProbability(int alignColumn, char symbol)
+  {
+    HMMNode node = getNodeForColumn(alignColumn);
+    int symbolIndex = getSymbolIndex(symbol);
+    if (node != null && symbolIndex != -1)
+    {
+      return node.getInsertEmission(symbolIndex);
+    }
+    return 0D;
+  }
+  
+  /**
+   * Gets the state transition probability for a given symbol at a column in the
+   * alignment.
+   * 
+   * @param alignColumn
+   *          The index of the alignment column, starting at index 0. Index 0
+   *          usually corresponds to index 1 in the HMM.
+   * @param symbol
+   *          The symbol for which the desired probability is being requested.
+   * @return
+   * 
+   */
+  public double getStateTransitionProbability(int alignColumn,
+          int transition)
+  {
+    HMMNode node = getNodeForColumn(alignColumn);
+    if (node != null)
+    {
+      return node.getStateTransition(transition);
+    }
+    return 0D;
+  }
+  
+  /**
+   * Returns the sequence position linked to the node at the given index. This
+   * corresponds to an aligned column position (counting from 1).
+   * 
+   * @param nodeIndex
+   *          The index of the node, starting from index 1. Index 0 is the begin
+   *          node, which does not correspond to a column in the alignment.
+   * @return
+   */
+  public int getNodeMapPosition(int nodeIndex)
+  {
+    return nodes.get(nodeIndex).getResidueNumber();
+  }
+  
+  /**
+   * Returns the consensus residue at the specified node.
+   * 
+   * @param nodeIndex
+   *          The index of the specified node.
+   * @return
+   */
+  public char getConsensusResidue(int nodeIndex)
+  {
+   char value = nodes.get(nodeIndex).getConsensusResidue();
+   return value;
+  }
+  
+  /**
+   * Returns the reference annotation at the specified node.
+   * 
+   * @param nodeIndex
+   *          The index of the specified node.
+   * @return
+   */
+  public char getReferenceAnnotation(int nodeIndex)
+  {
+   char value = nodes.get(nodeIndex).getReferenceAnnotation();
+   return value;
+  }
+  
+  /**
+   * Returns the mask value at the specified node.
+   * 
+   * @param nodeIndex
+   *          The index of the specified node.
+   * @return
+   */
+  public char getMaskedValue(int nodeIndex)
+  {
+   char value = nodes.get(nodeIndex).getMaskValue();
+   return value;
+  }
+  
+  /**
+   * Returns the consensus structure at the specified node.
+   * 
+   * @param nodeIndex
+   *          The index of the specified node.
+   * @return
+   */
+  public char getConsensusStructure(int nodeIndex)
+  {
+   char value = nodes.get(nodeIndex).getConsensusStructure();
+   return value;
+  }
+  
+  /**
+   * Sets a property read from an HMM file
+   * 
+   * @param key
+   * @param value
+   */
+  public void setProperty(String key, String value)
+  {
+    fileProperties.put(key, value);
+  }
+
+  /**
+   * Temporary implementation, should not be used.
+   * 
+   * @return
+   */
+  public String getViterbi()
+  {
+    String value;
+    value = fileProperties.get(HMMFile.VITERBI);
+    return value;
+  }
+
+  /**
+   * Temporary implementation, should not be used.
+   * 
+   * @return
+   */
+  public String getMSV()
+  {
+    String value;
+    value = fileProperties.get(HMMFile.MSV);
+    return value;
+  }
+
+  /**
+   * Temporary implementation, should not be used.
+   * 
+   * @return
+   */
+  public String getForward()
+  {
+    String value;
+    value = fileProperties.get(HMMFile.FORWARD);
+    return value;
+  }
+
+  /**
+   * Constructs the consensus sequence based on the most probable symbol at each
+   * position. Gap characters are inserted for discontinuities in the node map
+   * numbering (if provided), else an ungapped sequence is generated.
+   * <p>
+   * A mapping between the HMM nodes and residue positions of the sequence is
+   * also built and saved.
+   * 
+   * @return
+   */
+  void buildConsensusSequence()
+  {
+    List<int[]> toResidues = new ArrayList<>();
+
+    /*
+     * if the HMM provided a map to sequence, use those start/end values,
+     * else just treat it as for a contiguous sequence numbered from 1
+     */
+    boolean hasMap = getBooleanProperty(HMMFile.MAP);
+    int start = hasMap ? getNode(1).getResidueNumber() : 1;
+    int endResNo = hasMap ? getNode(nodes.size() - 1).getResidueNumber()
+            : (start + getLength() - 1);
+    char[] sequence = new char[endResNo];
+
+    int lastResNo = start - 1;
+    int seqOffset = -1;
+    int gapCount = 0;
+
+
+    for (int seqN = 0; seqN < start; seqN++)
+    {
+      sequence[seqN] = GAP_DASH;
+      seqOffset++;
+    }
+    
+    for (int nodeNo = 1; nodeNo < nodes.size(); nodeNo++)
+    {
+      HMMNode node = nodes.get(nodeNo);
+      final int resNo = hasMap ? node.getResidueNumber() : lastResNo + 1;
+
+      /*
+       * insert gaps if map numbering is not continuous
+       */
+      while (resNo > lastResNo + 1)
+      {
+        sequence[seqOffset++] = GAP_DASH;
+        lastResNo++;
+        gapCount++;
+      }
+      char consensusResidue = node.getConsensusResidue();
+      if (GAP_DASH == consensusResidue)
+      {
+        /*
+         * no residue annotation in HMM - scan for the symbol
+         * with the highest match emission probability
+         */
+        int symbolIndex = node.getMaxMatchEmissionIndex();
+        consensusResidue = alphabet.charAt(symbolIndex);
+        if (node.getMatchEmission(symbolIndex) < 0.5D)
+        {
+          // follow convention of lower case if match emission prob < 0.5
+          consensusResidue = Character.toLowerCase(consensusResidue);
+        }
+      }
+      sequence[seqOffset++] = consensusResidue;
+      lastResNo = resNo;
+    }
+
+    Sequence seq = new Sequence(getName(), sequence, start,
+            lastResNo - gapCount);
+    seq.createDatasetSequence();
+    seq.setHMM(this);
+    this.hmmSeq = seq;
+
+    /*
+     * construct and store Mapping of nodes to residues
+     * note as constructed this is just an identity mapping, 
+     * but it allows for greater flexibility in future
+     */
+    List<int[]> fromNodes = new ArrayList<>();
+    fromNodes.add(new int[] { 1, getLength() });
+    toResidues.add(new int[] { seq.getStart(), seq.getEnd() });
+    MapList mapList = new MapList(fromNodes, toResidues, 1, 1);
+    mapToHmmConsensus = new Mapping(seq.getDatasetSequence(), mapList);
+  }
+
+
+  /**
+   * Answers the aligned consensus sequence for the profile. Note this will
+   * return null if called before <code>setNodes</code> has been called.
+   * 
+   * @return
+   */
+  public SequenceI getConsensusSequence()
+  {
+    return hmmSeq;
+  }
+
+  /**
+   * Answers the index position (0...) of the given symbol, or -1 if not a valid
+   * symbol for this HMM
+   * 
+   * @param symbol
+   * @return
+   */
+  private int getSymbolIndex(char symbol)
+  {
+    /*
+     * symbolIndexLookup holds the index for 'A' to 'Z'
+     */
+    char c = Character.toUpperCase(symbol);
+    if ('A' <= c && c <= 'Z')
+    {
+      return symbolIndexLookup[c - 'A'];
+    }
+    return -1;
+  }
+
+  /**
+   * Sets the nodes of this HMM, and also extracts the HMM consensus sequence
+   * and a mapping between node numbers and sequence positions
+   * 
+   * @param nodeList
+   */
+  public void setNodes(List<HMMNode> nodeList)
+  {
+    nodes = nodeList;
+    if (nodes.size() > 1)
+    {
+      buildConsensusSequence();
+    }
+  }
+
+  /**
+   * Sets the aligned consensus sequence this HMM is the model for
+   * 
+   * @param hmmSeq
+   */
+  public void setHmmSeq(SequenceI hmmSeq)
+  {
+    this.hmmSeq = hmmSeq;
+  }
+
+  public void setBackgroundFrequencies(Map<Character, Float> bkgdFreqs)
+  {
+    backgroundFrequencies = bkgdFreqs;
+  }
+
+  public void setBackgroundFrequencies(ResidueCount bkgdFreqs)
+  {
+    backgroundFrequencies = new HashMap<>();
+
+    int total = bkgdFreqs.getTotalResidueCount();
+
+    for (char c : bkgdFreqs.getSymbolCounts().symbols)
+    {
+      backgroundFrequencies.put(c, bkgdFreqs.getCount(c) * 1f / total);
+    }
+
+  }
+
+  public Map<Character, Float> getBackgroundFrequencies()
+  {
+    return backgroundFrequencies;
+  }
+
+}
+
index 65ba18b..a966ca1 100755 (executable)
  */
 package jalview.datamodel;
 
-import jalview.util.CaseInsensitiveString;
-
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
+import jalview.util.CaseInsensitiveString;
+
 public class PDBEntry
 {
 
@@ -165,6 +165,32 @@ public class PDBEntry
   {
   }
 
+  /**
+   * Entry point when file is not known and fileType may be string
+   * @param pdbId
+   * @param chain may be null
+   * @param fileType "pdb", "mmcif", or "bcif"; null defaults to mmcif
+   */
+  public PDBEntry(String pdbId, String chain, String fileType) {
+    this.id = pdbId.toLowerCase();
+    setChainCode(chain); // I note that PDB Chains ARE case-sensitive now
+    if (fileType == null)
+      fileType = "mmcif";
+    switch (fileType.toLowerCase()) {
+    case "pdb":
+      this.type = Type.PDB.toString();
+      break;
+    case "mmcif":
+      this.type = Type.MMCIF.toString();
+      break;
+    default:
+    case "bcif":
+      System.out.println("format " + fileType + " has not been implemented; using mmCIF");
+      this.type = Type.MMCIF.toString();
+      break;
+    }
+  }
+  
   public PDBEntry(String pdbId, String chain, PDBEntry.Type type,
           String filePath)
   {
@@ -266,6 +292,12 @@ public class PDBEntry
     return id;
   }
 
+  /**
+   * TODO 
+   * 
+   * @param key  "protocol" 
+   * @param value
+   */
   public void setProperty(String key, Object value)
   {
     if (this.properties == null)
index dc98f99..4ca4c80 100644 (file)
@@ -25,6 +25,8 @@ import jalview.util.Format;
 import jalview.util.QuickSort;
 import jalview.util.SparseCount;
 
+import java.util.List;
+
 /**
  * A class to count occurrences of residues in a profile, optimised for speed
  * and memory footprint.
@@ -148,6 +150,24 @@ public class ResidueCount
   }
 
   /**
+   * A constructor that counts frequency of all symbols (including gaps) in the
+   * sequences (not case-sensitive)
+   * 
+   * @param sequences
+   */
+  public ResidueCount(List<SequenceI> sequences)
+  {
+    this();
+    for (SequenceI seq : sequences)
+    {
+      for (int i = 0; i < seq.getLength(); i++)
+      {
+        add(seq.getCharAt(i));
+      }
+    }
+  }
+
+  /**
    * Increments the count for the given character. The supplied character may be
    * upper or lower case but counts are for the upper case only. Gap characters
    * (space, ., -) are all counted together.
@@ -637,4 +657,19 @@ public class ResidueCount
     sb.append("]");
     return sb.toString();
   }
+
+  /**
+   * Answers the total count for all symbols (excluding gaps)
+   * 
+   * @return
+   */
+  public int getTotalResidueCount()
+  {
+    int total = 0;
+    for (char symbol : this.getSymbolCounts().symbols)
+    {
+      total += getCount(symbol);
+    }
+    return total;
+  }
 }
index c2a6a9c..07a62c9 100644 (file)
@@ -22,6 +22,7 @@ package jalview.datamodel;
 
 import jalview.analysis.AlignSeq;
 import jalview.analysis.SeqsetUtils;
+import jalview.analysis.SeqsetUtils.SequenceInfo;
 import jalview.util.MessageManager;
 import jalview.util.ShiftList;
 
@@ -37,7 +38,7 @@ public class SeqCigar extends CigarSimple
 
   private SequenceI refseq = null;
 
-  private Hashtable seqProps;
+  private SequenceInfo seqProps;
 
   /**
    * Reference dataset sequence for the cigar string
index f8e70b1..509a287 100755 (executable)
@@ -27,6 +27,7 @@ import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.StringUtils;
+import jalview.workers.InformationThread;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -82,6 +83,9 @@ public class Sequence extends ASequence implements SequenceI
 
   private String vamsasId;
 
+  HiddenMarkovModel hmm;
+
+  boolean isHMMConsensusSequence = false;
   private DBModList<DBRefEntry> dbrefs; // controlled access
 
   /**
@@ -361,6 +365,10 @@ public class Sequence extends ASequence implements SequenceI
         this.addPDBId(new PDBEntry(pdb));
       }
     }
+    if (seq.getHMM() != null)
+    {
+      this.hmm = new HiddenMarkovModel(seq.getHMM(), this);
+    }
   }
 
   @Override
@@ -1069,7 +1077,8 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public ContiguousI findPositions(int fromColumn, int toColumn)
   {
-    if (toColumn < fromColumn || fromColumn < 1)
+    fromColumn = Math.max(fromColumn, 1);
+    if (toColumn < fromColumn)
     {
       return null;
     }
@@ -1819,12 +1828,12 @@ public class Sequence extends ASequence implements SequenceI
     {
       for (AlignmentAnnotation ann : annotation)
       {
-        if ((ann.calcId != null && ann.calcId.equals(calcId))
+        String id = ann.getCalcId();
+        if ((id != null && id.equals(calcId))
                 && (ann.label != null && ann.label.equals(label))
                 && ((ignoreDescription && description == null)
                         || (ann.description != null
                                 && ann.description.equals(description))))
-
         {
           result.add(ann);
         }
@@ -1943,6 +1952,33 @@ public class Sequence extends ASequence implements SequenceI
     }
   }
 
+  @Override
+  public HiddenMarkovModel getHMM()
+  {
+    return hmm;
+  }
+
+  @Override
+  public void setHMM(HiddenMarkovModel hmm)
+  {
+    this.hmm = hmm;
+  }
+
+  @Override
+  public boolean hasHMMAnnotation()
+  {
+    if (this.annotation == null) {
+      return false;
+    }
+    for (AlignmentAnnotation ann : annotation)
+    {
+      if (InformationThread.HMM_CALC_ID.equals(ann.getCalcId()))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
   /**
    * {@inheritDoc}
    */
@@ -2107,4 +2143,10 @@ public class Sequence extends ASequence implements SequenceI
     // otherwise, sequence was completely hidden
     return 0;
   }
+
+  @Override
+  public boolean hasHMMProfile()
+  {
+    return hmm != null;
+  }
 }
index e2bb5a6..aa15d1f 100644 (file)
@@ -83,4 +83,12 @@ public interface SequenceCollectionI
    * @return
    */
   boolean isNucleotide();
+
+  /**
+   * Returns the (possibly empty) list of HMM consensus sequences in the
+   * collection
+   * 
+   * @return
+   */
+  List<SequenceI> getHmmSequences();
 }
index 5e33229..0a68792 100755 (executable)
@@ -25,6 +25,8 @@ import jalview.analysis.Conservation;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
+import jalview.util.MessageManager;
+import jalview.workers.InformationThread;
 
 import java.awt.Color;
 import java.beans.PropertyChangeListener;
@@ -43,11 +45,10 @@ import java.util.Map;
 public class SequenceGroup implements AnnotatedCollectionI
 {
   // TODO ideally this event notification functionality should be separated into
-  // a
-  // subclass of ViewportProperties similarly to ViewportRanges. Done here as
-  // quick fix for JAL-2665
+  // a subclass of ViewportProperties similarly to ViewportRanges.
+  // Done here as a quick fix for JAL-2665
   public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
-
+  
   protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
@@ -65,40 +66,46 @@ public class SequenceGroup implements AnnotatedCollectionI
   String groupName;
 
   String description;
-
+  
   Conservation conserve;
 
+  Conservation conservationData;
+
+  ProfilesI consensusProfiles;
+
+  ProfilesI hmmProfiles;
+
   boolean displayBoxes = true;
 
   boolean displayText = true;
 
   boolean colourText = false;
 
-  /**
-   * True if the group is defined as a group on the alignment, false if it is
-   * just a selection.
+  /*
+   * true if the group is defined as a group on the alignment, false if it is
+   * just a selection
    */
   boolean isDefined = false;
 
-  /**
+  /*
    * after Olivier's non-conserved only character display
    */
   boolean showNonconserved = false;
 
-  /**
-   * group members
+  /*
+   * sequences in the group
    */
   private List<SequenceI> sequences;
 
-  /**
+  /*
    * representative sequence for this group (if any)
    */
   private SequenceI seqrep = null;
 
   int width = -1;
 
-  /**
-   * Colourscheme applied to group if any
+  /*
+   * colour scheme applied to group if any
    */
   public ResidueShaderI cs;
 
@@ -122,22 +129,29 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   public Color textColour2 = Color.white;
 
-  /**
-   * consensus calculation property
+  /*
+   * properties for consensus annotation
    */
   private boolean ignoreGapsInConsensus = true;
 
-  /**
-   * consensus calculation property
-   */
   private boolean showSequenceLogo = false;
 
-  /**
-   * flag indicating if logo should be rendered normalised
-   */
   private boolean normaliseSequenceLogo;
 
   /*
+   * properties for HMM information annotation
+   */
+  private boolean hmmIgnoreBelowBackground = true;
+
+  private boolean hmmUseInfoLetterHeight;
+
+  private boolean hmmShowSequenceLogo;
+
+  private boolean hmmNormaliseSequenceLogo;
+
+  private boolean hmmShowHistogram;
+
+  /*
    * visibility of rows or represented rows covered by group
    */
   private boolean hidereps = false;
@@ -145,18 +159,22 @@ public class SequenceGroup implements AnnotatedCollectionI
   /*
    * visibility of columns intersecting this group
    */
-  private boolean hidecols = false;
+  private boolean hidecols;
 
   AlignmentAnnotation consensus = null;
 
   AlignmentAnnotation conservation = null;
 
+  private AlignmentAnnotation hmmInformation;
+  
   private boolean showConsensusHistogram;
-
+  
   private AnnotatedCollectionI context;
 
+
   /**
-   * Creates a new SequenceGroup object.
+   * Constructor, assigning a generated default name of "JGroup:" with object
+   * hashcode appended
    */
   public SequenceGroup()
   {
@@ -199,10 +217,13 @@ public class SequenceGroup implements AnnotatedCollectionI
    * copy constructor
    * 
    * @param seqsel
+   * @param keepsequences
+   *          if false do not add sequences from seqsel to new instance
    */
   public SequenceGroup(SequenceGroup seqsel)
   {
     this();
+
     if (seqsel != null)
     {
       sequences = new ArrayList<>();
@@ -228,6 +249,9 @@ public class SequenceGroup implements AnnotatedCollectionI
       showSequenceLogo = seqsel.showSequenceLogo;
       normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
       showConsensusHistogram = seqsel.showConsensusHistogram;
+      hmmShowSequenceLogo = seqsel.hmmShowSequenceLogo;
+      hmmNormaliseSequenceLogo = seqsel.hmmNormaliseSequenceLogo;
+      hmmShowHistogram = seqsel.hmmShowHistogram;
       idColour = seqsel.idColour;
       outlineColour = seqsel.outlineColour;
       seqrep = seqsel.seqrep;
@@ -236,8 +260,12 @@ public class SequenceGroup implements AnnotatedCollectionI
       thresholdTextColour = seqsel.thresholdTextColour;
       width = seqsel.width;
       ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
+      hmmIgnoreBelowBackground = seqsel.hmmIgnoreBelowBackground;
+      hmmUseInfoLetterHeight = seqsel.hmmUseInfoLetterHeight;
       if (seqsel.conserve != null)
       {
+        // todo avoid doing this if we don't actually want derived calculations
+        // !
         recalcConservation(); // safer than
         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
       }
@@ -581,8 +609,9 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * calculate residue conservation for group - but only if necessary. returns
-   * true if the calculation resulted in a visible change to group
+   * Recalculates column consensus, conservation, and HMM annotation for the
+   * group (as applicable). Returns true if the calculation resulted in a
+   * visible change to group.
    * 
    * @param defer
    *          when set, colourschemes for this group are not refreshed after
@@ -590,7 +619,8 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public boolean recalcConservation(boolean defer)
   {
-    if (cs == null && consensus == null && conservation == null)
+    if (cs == null && consensus == null && conservation == null
+            && hmmInformation == null)
     {
       return false;
     }
@@ -601,6 +631,16 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
+      if (hmmInformation != null)
+      {
+        HiddenMarkovModel hmm = hmmInformation.sequenceRef.getHMM();
+
+        ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
+                (endRes + 1) - startRes, startRes, endRes + 1,
+                hmmIgnoreBelowBackground, hmmUseInfoLetterHeight);
+        _updateInformationRow(info);
+        upd = true;
+      }
       if (consensus != null)
       {
         _updateConsensusRow(cnsns, sequences.size());
@@ -701,6 +741,33 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * Recalculates the information content on the HMM annotation
+   * 
+   * @param cnsns
+   */
+  private void _updateInformationRow(ProfilesI cnsns)
+  {
+    if (hmmInformation == null)
+    {
+      createInformationAnnotation();
+    }
+    hmmInformation.description = MessageManager
+            .getString("label.information_description");
+    setHmmProfiles(cnsns);
+    // preserve width if already set
+    int aWidth = (hmmInformation.annotations != null)
+            ? (endRes < hmmInformation.annotations.length
+                    ? hmmInformation.annotations.length : endRes + 1)
+            : endRes + 1;
+    hmmInformation.annotations = null;
+    hmmInformation.annotations = new Annotation[aWidth]; // should be alignment
+                                                      // width
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+    AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
+            endRes + 1);
+  }
+
+  /**
    * @param s
    *          sequence to either add or remove from group
    * @param recalc
@@ -1156,6 +1223,22 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * Creates the Hidden Markov Model annotation for this group
+   */
+  void createInformationAnnotation()
+  {
+    hmmInformation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
+            6.25f, AlignmentAnnotation.BAR_GRAPH);
+    hmmInformation.hasText = true;
+    hmmInformation.autoCalculated = false;
+    hmmInformation.groupRef = this;
+    hmmInformation.label = getName();
+    hmmInformation.description = MessageManager
+            .getString("label.information_description");
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+  }
+
+  /**
    * set this alignmentAnnotation object as the one used to render consensus
    * annotation
    * 
@@ -1242,6 +1325,26 @@ public class SequenceGroup implements AnnotatedCollectionI
     return ignoreGapsInConsensus;
   }
 
+  public void setIgnoreBelowBackground(boolean state)
+  {
+    hmmIgnoreBelowBackground = state;
+  }
+
+  public boolean isIgnoreBelowBackground()
+  {
+    return hmmIgnoreBelowBackground;
+  }
+
+  public void setInfoLetterHeight(boolean state)
+  {
+    hmmUseInfoLetterHeight = state;
+  }
+
+  public boolean isUseInfoLetterHeight()
+  {
+    return hmmUseInfoLetterHeight;
+  }
+
   /**
    * @param showSequenceLogo
    *          indicates if a sequence logo is shown for consensus annotation
@@ -1485,4 +1588,70 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
   }
+
+  public boolean isShowInformationHistogram()
+  {
+    return hmmShowHistogram;
+  }
+
+  public void setShowInformationHistogram(boolean state)
+  {
+    if (hmmShowHistogram != state && hmmInformation != null)
+    {
+      this.hmmShowHistogram = state;
+      // recalcConservation(); TODO don't know what to do here next
+    }
+    this.hmmShowHistogram = state;
+  }
+
+  public boolean isShowHMMSequenceLogo()
+  {
+    return hmmShowSequenceLogo;
+  }
+
+  public void setShowHMMSequenceLogo(boolean state)
+  {
+    hmmShowSequenceLogo = state;
+  }
+
+  public boolean isNormaliseHMMSequenceLogo()
+  {
+    return hmmNormaliseSequenceLogo;
+  }
+
+  public void setNormaliseHMMSequenceLogo(boolean state)
+  {
+    hmmNormaliseSequenceLogo = state;
+  }
+
+  public ProfilesI getConsensusData()
+  {
+    return consensusProfiles;
+  }
+
+  public ProfilesI getHmmProfiles()
+  {
+    return hmmProfiles;
+  }
+
+  public void setHmmProfiles(ProfilesI hmmProfiles)
+  {
+    this.hmmProfiles = hmmProfiles;
+  }
+
+  @Override
+  public List<SequenceI> getHmmSequences()
+  {
+    List<SequenceI> result = new ArrayList<>();
+    for (int i = 0; i < sequences.size(); i++)
+    {
+      SequenceI seq = sequences.get(i);
+      if (seq.hasHMMProfile())
+      {
+        result.add(seq);
+      }
+    }
+    return result;
+  }
+
 }
index 2f365e6..8f1d160 100755 (executable)
@@ -48,6 +48,9 @@ public interface SequenceI extends ASequenceI
    */
   public void setName(String name);
 
+  public HiddenMarkovModel getHMM();
+
+  public void setHMM(HiddenMarkovModel hmm);
   /**
    * Get the display name
    */
@@ -215,10 +218,10 @@ public interface SequenceI extends ASequenceI
    * from 1), or null if no residues are included in the range
    * 
    * @param fromColum
-   *          - first column base 1
+   *          - first column base 1. (0 and negative positions are rounded up)
    * @param toColumn
    *          - last column, base 1
-   * @return
+   * @return null if fromColum>toColumn
    */
   public ContiguousI findPositions(int fromColum, int toColumn);
 
@@ -357,13 +360,12 @@ public interface SequenceI extends ASequenceI
   /**
    * set the array of Database references for the sequence.
    * 
-   * BH 2019.02.04 changes param to DBModlist
+   * BH 2019.02.04 changes param to DBModlist 
    * 
    * @param dbs
    * @deprecated - use is discouraged since side-effects may occur if DBRefEntry
    *             set are not normalised.
-   * @throws InvalidArgumentException
-   *           if the is not one created by Sequence itself
+   * @throws InvalidArgumentException if the is not one created by Sequence itself
    */
   @Deprecated
   public void setDBRefs(DBModList<DBRefEntry> dbs);
@@ -448,8 +450,8 @@ public interface SequenceI extends ASequenceI
 
   /**
    * 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.
+   * calcId (source), label (type) and description (observation instance).
+   * Null values do not match.
    * 
    * @param calcId
    * @param label
@@ -457,7 +459,6 @@ public interface SequenceI extends ASequenceI
    */
   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
@@ -517,6 +518,11 @@ public interface SequenceI extends ASequenceI
    *         list
    */
   public List<DBRefEntry> getPrimaryDBRefs();
+  /**
+   * Answers true if the sequence has annotation for Hidden Markov Model
+   * information content, else false
+   */
+  boolean hasHMMAnnotation();
 
   /**
    * Returns a (possibly empty) list of sequence features that overlap the given
@@ -532,8 +538,7 @@ public interface SequenceI extends ASequenceI
    *          optional feature types to restrict results to
    * @return
    */
-  List<SequenceFeature> findFeatures(int fromCol, int toCol,
-          String... types);
+  List<SequenceFeature> findFeatures(int fromCol, int toCol, String... types);
 
   /**
    * Method to call to indicate that the sequence (characters or alignment/gaps)
@@ -541,7 +546,7 @@ public interface SequenceI extends ASequenceI
    * positions to be invalidated.
    */
   void sequenceChanged();
-
+  
   /**
    * 
    * @return BitSet corresponding to index [0,length) where Comparison.isGap()
@@ -573,8 +578,9 @@ public interface SequenceI extends ASequenceI
    * @param chromosomeId
    * @param map
    */
-  void setGeneLoci(String speciesId, String assemblyId, String chromosomeId,
-          MapList map);
+  void setGeneLoci(String speciesId, String assemblyId,
+          String chromosomeId, MapList map);
+
 
   /**
    * Returns the sequence string constructed from the substrings of a sequence
@@ -597,4 +603,12 @@ public interface SequenceI extends ASequenceI
    */
   public int firstResidueOutsideIterator(Iterator<int[]> it);
 
+
+  /**
+   * Answers true if this sequence has an associated Hidden Markov Model
+   * 
+   * @return
+   */
+  boolean hasHMMProfile();
 }
+
index d274511..a57fd55 100644 (file)
@@ -29,17 +29,29 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton class to hold the set of attributes known for each feature type
  */
-public class FeatureAttributes
+public class FeatureAttributes implements ApplicationSingletonI
 {
   public enum Datatype
   {
     Character, Number, Mixed
   }
 
-  private static FeatureAttributes instance = new FeatureAttributes();
+  public static FeatureAttributes getInstance()
+  {
+    return (FeatureAttributes) ApplicationSingletonProvider
+            .getInstance(FeatureAttributes.class);
+  }
+
+  private FeatureAttributes()
+  {
+    attributes = new HashMap<>();
+  }
 
   /*
    * map, by feature type, of a map, by attribute name, of
@@ -120,12 +132,17 @@ public class FeatureAttributes
       if (value != null)
       {
         value = value.trim();
+        if (value.isEmpty())
+        {
+          return;
+        }
 
         /*
          * Parse numeric value unless we have previously
          * seen text data for this attribute type
          */
-        if (type == null || type == Datatype.Number)
+        if ((type == null && couldBeNumber(value))
+                || type == Datatype.Number)
         {
           try
           {
@@ -149,6 +166,16 @@ public class FeatureAttributes
             hasValue = false;
           }
         }
+        else
+        {
+          /*
+           * if not a number, and not seen before...
+           */
+          type = Datatype.Character;
+          min = 0f;
+          max = 0f;
+          hasValue = false;
+        }
       }
     }
 
@@ -195,21 +222,6 @@ public class FeatureAttributes
   }
 
   /**
-   * Answers the singleton instance of this class
-   * 
-   * @return
-   */
-  public static FeatureAttributes getInstance()
-  {
-    return instance;
-  }
-
-  private FeatureAttributes()
-  {
-    attributes = new HashMap<>();
-  }
-
-  /**
    * Answers the attribute names known for the given feature type, in
    * alphabetical order (not case sensitive), or an empty set if no attributes
    * are known. An attribute name is typically 'simple' e.g. "AC", but may be
@@ -229,6 +241,34 @@ public class FeatureAttributes
   }
 
   /**
+   * A partial check that the string is numeric - only checks the first
+   * character. Returns true if the first character is a digit, or if it is '.',
+   * '+' or '-' and not the only character. Otherwise returns false (including
+   * for an empty string). Note this is not a complete check as it returns true
+   * for (e.g.) "1A".
+   * 
+   * @param f
+   * @return
+   */
+  public static boolean couldBeNumber(String f)
+  {
+    int len = f.length();
+    if (len == 0)
+    {
+      return false;
+    }
+    char ch = f.charAt(0);
+    switch (ch)
+    {
+    case '.':
+    case '+':
+    case '-':
+      return len > 1;
+    }
+    return (ch <= '9' && ch >= '0');
+  }
+
+  /**
    * Answers true if at least one attribute is known for the given feature type,
    * else false
    * 
index b316821..4a87349 100644 (file)
@@ -23,6 +23,9 @@ package jalview.datamodel.features;
 import java.util.HashMap;
 import java.util.Map;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton to hold metadata about feature attributes, keyed by a unique
  * feature source identifier
@@ -30,22 +33,17 @@ import java.util.Map;
  * @author gmcarstairs
  *
  */
-public class FeatureSources
+public class FeatureSources implements ApplicationSingletonI
 {
-  private static FeatureSources instance = new FeatureSources();
-
-  private Map<String, FeatureSourceI> sources;
 
-  /**
-   * Answers the singleton instance of this class
-   * 
-   * @return
-   */
   public static FeatureSources getInstance()
   {
-    return instance;
+    return (FeatureSources) ApplicationSingletonProvider
+            .getInstance(FeatureSources.class);
   }
 
+  private Map<String, FeatureSourceI> sources;
+
   private FeatureSources()
   {
     sources = new HashMap<>();
index 905fd8b..15c2c07 100644 (file)
@@ -372,7 +372,7 @@ public class SequenceFeatures implements SequenceFeaturesI
     {
       return true;
     }
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     for (String term : soTerm)
     {
       if (type.equals(term) || so.isA(type, term))
index e01ad17..ec7318f 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.io.gff.SequenceOntologyI;
+import jalview.util.Platform;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,7 +45,7 @@ public class EnsemblCdna extends EnsemblSeqProxy
    * or ENSMUST or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)[TG][0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
 
   /*
index f397412..07c3431 100644 (file)
@@ -93,7 +93,7 @@ public class EnsemblCds extends EnsemblSeqProxy
   @Override
   protected boolean retainFeature(SequenceFeature sf, String accessionId)
   {
-    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+    if (SequenceOntologyFactory.getSequenceOntology().isA(sf.getType(),
             SequenceOntologyI.CDS))
     {
       return false;
index 5649859..bc94619 100644 (file)
  */
 package jalview.ext.ensembl;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.io.gff.SequenceOntologyI;
-import jalview.util.JSONUtils;
-import jalview.util.Platform;
-
-import java.io.BufferedReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -40,6 +30,14 @@ import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.JSONUtils;
+
 /**
  * A client for fetching and processing Ensembl feature data in GFF format by
  * calling the overlap REST service
index 75a7154..ae85b45 100644 (file)
@@ -55,7 +55,7 @@ public class EnsemblGene extends EnsemblSeqProxy
    * accepts anything as we will attempt lookup of gene or 
    * transcript id or gene name
    */
-  private static final Regex ACCESSION_REGEX = new Regex(".*");
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(".*");
 
   private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
       EnsemblFeatureType.gene, EnsemblFeatureType.transcript,
@@ -579,7 +579,7 @@ public class EnsemblGene extends EnsemblSeqProxy
   @Override
   protected boolean retainFeature(SequenceFeature sf, String accessionId)
   {
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = sf.getType();
     if (so.isA(type, SequenceOntologyI.GENE))
     {
@@ -626,7 +626,7 @@ public class EnsemblGene extends EnsemblSeqProxy
   {
     return new FeatureSettingsAdapter()
     {
-      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
 
       @Override
       public boolean isFeatureHidden(String type)
index 0280f16..468f05d 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,7 +43,7 @@ public class EnsemblProtein extends EnsemblSeqProxy
    * or ENSMUSP or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)P[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
 
   /**
index 56eda5e..7f2fa59 100644 (file)
@@ -735,7 +735,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
        * for sequence_variant on reverse strand, have to convert the allele
        * values to their complements
        */
-      if (!forwardStrand && SequenceOntologyFactory.getInstance()
+      if (!forwardStrand && SequenceOntologyFactory.getSequenceOntology()
               .isA(sf.getType(), SequenceOntologyI.SEQUENCE_VARIANT))
       {
         reverseComplementAlleles(copy);
@@ -969,7 +969,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
   public static boolean isTranscript(String featureType)
   {
     return SequenceOntologyI.NMD_TRANSCRIPT_VARIANT.equals(featureType)
-            || SequenceOntologyFactory.getInstance().isA(featureType,
+            || SequenceOntologyFactory.getSequenceOntology().isA(featureType,
                     SequenceOntologyI.TRANSCRIPT);
   }
 }
index 66b0e3b..dc7346b 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.analysis.AlignmentUtils;
 import jalview.bin.Cache;
 import jalview.datamodel.DBRefSource;
+import jalview.util.Platform;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
 import com.stevesoft.pat.Regex;
@@ -50,7 +51,7 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
    * or ENSMUSP or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|"
                   + "(CCDS[0-9.]{3,}$)");
 
index 4eeb977..714a5c0 100644 (file)
  */
 package jalview.ext.ensembl;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
-import jalview.util.DBRefUtils;
-import jalview.util.JSONUtils;
-
-import java.io.BufferedReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -36,6 +30,10 @@ import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.util.DBRefUtils;
+
 /**
  * A class to fetch cross-references from Ensembl by calling the /xrefs REST
  * service
index 895db9a..8ebc5d5 100644 (file)
@@ -32,7 +32,6 @@ 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;
@@ -60,13 +59,13 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.ws.dbsources.Pdb;
 import javajs.util.BS;
-
 public abstract class JalviewJmolBinding extends AAStructureBindingModel
         implements JmolStatusListener, JmolSelectionListener,
         ComponentListener
 {
   private String lastMessage;
 
+
   /*
    * when true, try to search the associated datamodel for sequences that are
    * associated with any unknown structures in the Jmol view.
@@ -137,6 +136,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     {
       return null;
     }
+
     String cmd = command.getCommand();
     jmolHistory(false);
     if (lastCommand == null || !lastCommand.equals(cmd))
@@ -186,6 +186,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     return null;
   }
 
+
   /**
    * map between index of model filename returned from getPdbFile and the first
    * index of models from this file in the viewer. Note - this is not trimmed -
@@ -297,6 +298,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
     jmolScript(cmd.toString());
     jmolHistory(true);
+
   }
 
   private boolean debug = true;
@@ -782,6 +784,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     setLoadingFromArchive(false);
   }
 
+
   protected IProgressIndicator getIProgressIndicator()
   {
     return null;
@@ -825,6 +828,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   }
 
+
   public void showHelp()
   {
     showUrl("http://wiki.jmol.org"
index 796bc0e..6e352d4 100644 (file)
@@ -41,6 +41,8 @@ import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.SequenceI;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
@@ -62,13 +64,18 @@ import jalview.util.Platform;
  * @author tcnofoegbu
  */
 public class PDBFTSRestClient extends FTSRestClient
-        implements StructureFTSRestClientI
+        implements StructureFTSRestClientI,ApplicationSingletonI
 {
 
   private static FTSRestClientI instance = null;
 
   public static final String PDB_SEARCH_ENDPOINT = "https://www.ebi.ac.uk/pdbe/search/pdb/select?";
 
+  public static FTSRestClientI getInstance()
+  {
+    return (FTSRestClientI) ApplicationSingletonProvider
+            .getInstance(PDBFTSRestClient.class);
+  }
   protected PDBFTSRestClient()
   {
   }
@@ -178,6 +185,7 @@ public class PDBFTSRestClient extends FTSRestClient
       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;
@@ -210,7 +218,6 @@ public class PDBFTSRestClient extends FTSRestClient
       switch (responseStatus)
       {
       case 200:
-
         if (isMocked())
         {
           responseString = mockQueries.get(uri.toString());
@@ -368,7 +375,6 @@ public class PDBFTSRestClient extends FTSRestClient
       result = new ArrayList<FTSData>();
       if (numFound > 0)
       {
-
         for (Iterator<Object> docIter = docs.iterator(); docIter.hasNext();)
         {
           Map<String, Object> doc = (Map<String, Object>) docIter.next();
@@ -380,7 +386,6 @@ public class PDBFTSRestClient extends FTSRestClient
       searchResult.setNumberOfItemsFound(numFound);
       searchResult.setResponseTime(queryTime);
       searchResult.setSearchSummary(result);
-
     } catch (ParseException e)
     {
       e.printStackTrace();
@@ -509,14 +514,6 @@ public class PDBFTSRestClient extends FTSRestClient
     return "/fts/pdb_data_columns.txt";
   }
 
-  public static FTSRestClientI getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new PDBFTSRestClient();
-    }
-    return instance;
-  }
 
   private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
 
@@ -532,7 +529,6 @@ public class PDBFTSRestClient extends FTSRestClient
     }
     return allDefaultDisplayedStructureDataColumns;
   }
-
   @Override
   public String[] getPreferencesColumnsFor(PreferenceSource source)
   {
index 1827293..f2a9265 100644 (file)
 
 package jalview.fts.service.uniprot;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import java.lang.invoke.MethodHandles;
 import java.net.MalformedURLException;
 import java.net.URL;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -40,6 +43,7 @@ import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
 import jalview.fts.core.FTSRestClient;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
@@ -72,9 +76,14 @@ import jalview.util.Platform;
  * Where such a link is passed it is put into the cursors ArrayList.
  * There are @Overridden methods in UniprotFTSPanel.
  */
-
 public class UniProtFTSRestClient extends FTSRestClient
+    implements ApplicationSingletonI,FTSRestClientI
 {
+public static UniProtFTSRestClient getInstance()
+{
+return (UniProtFTSRestClient) ApplicationSingletonProvider
+    .getInstance(UniProtFTSRestClient.class);
+}
   private static final String DEFAULT_UNIPROT_DOMAIN = "https://rest.uniprot.org";
 
   private static final String USER_AGENT = ChannelProperties
@@ -87,11 +96,10 @@ public class UniProtFTSRestClient extends FTSRestClient
     Platform.addJ2SDirectDatabaseCall(DEFAULT_UNIPROT_DOMAIN);
   }
 
-  private static UniProtFTSRestClient instance = null;
 
   public final String uniprotSearchEndpoint;
 
-  public UniProtFTSRestClient()
+  private UniProtFTSRestClient()
   {
     super();
     this.clearCursors();
@@ -214,7 +222,6 @@ public class UniProtFTSRestClient extends FTSRestClient
           }
         }
       }
-
       String uniProtTabDelimittedResponseString = clientResponse
               .getEntity(String.class);
       // Make redundant objects eligible for garbage collection to conserve
@@ -448,14 +455,6 @@ public class UniProtFTSRestClient extends FTSRestClient
     };
   }
 
-  public static UniProtFTSRestClient getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new UniProtFTSRestClient();
-    }
-    return instance;
-  }
 
   @Override
   public String getColumnDataConfigFileName()
@@ -550,4 +549,4 @@ public class UniProtFTSRestClient extends FTSRestClient
     }
     return null;
   }
-}
\ No newline at end of file
+}
index 08ff021..f776494 100644 (file)
@@ -96,7 +96,7 @@ public class AlignExportOptions extends JPanel
     this.settings = defaults;
     this.isComplexAlignFile = format.isComplexAlignFile();
     init(viewport.hasHiddenRows(), viewport.hasHiddenColumns());
-    dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+    dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
   }
 
   /**
index e24cbea..c38f336 100644 (file)
@@ -22,9 +22,16 @@ package jalview.gui;
 
 import java.util.Locale;
 
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
+import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
 import java.awt.datatransfer.Clipboard;
@@ -48,13 +55,14 @@ import java.awt.event.MouseEvent;
 import java.awt.print.PageFormat;
 import java.awt.print.PrinterJob;
 import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.File;
 import java.io.FileWriter;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Deque;
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -73,6 +81,8 @@ import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.SwingUtilities;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 
 import ext.vamsas.ServiceHandle;
 import jalview.analysis.AlignmentSorter;
@@ -120,6 +130,13 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
 import jalview.gui.ViewSelectionMenu.ViewSetProvider;
+import jalview.hmmer.HMMAlign;
+import jalview.hmmer.HMMBuild;
+import jalview.hmmer.HMMERParamStore;
+import jalview.hmmer.HMMERPreset;
+import jalview.hmmer.HMMSearch;
+import jalview.hmmer.HmmerCommand;
+import jalview.hmmer.JackHMMER;
 import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
 import jalview.io.BackupFiles;
@@ -155,10 +172,81 @@ import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
+import jalview.ws.ServiceChangeListener;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.jws2.PreferredServiceRegistry;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.WsParamSetI;
 import jalview.ws.seqfetcher.DbSourceProxy;
+import jalview.ws.slivkaws.SlivkaWSDiscoverer;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterJob;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+import ext.vamsas.ServiceHandle;
 
 /**
  * DOCUMENT ME!
@@ -167,10 +255,12 @@ import jalview.ws.seqfetcher.DbSourceProxy;
  * @version $Revision$
  */
 @SuppressWarnings("serial")
-public class AlignFrame extends GAlignFrame implements DropTargetListener,
-        IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
+public class AlignFrame extends GAlignFrame
+        implements DropTargetListener, IProgressIndicator,
+        AlignViewControllerGuiI, ColourChangeListener, ServiceChangeListener
 {
 
+  public static int frameCount;
   public static final int DEFAULT_WIDTH = 700;
 
   public static final int DEFAULT_HEIGHT = 500;
@@ -196,8 +286,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   String fileName = null;
 
+  /**
+   * TODO: remove reference to 'FileObject' in AlignFrame - not correct mapping
+   */
   File fileObject;
 
+  private int id;
+
+  private DataSourceType protocol ;
   /**
    * Creates a new AlignFrame object with specific width and height.
    * 
@@ -292,6 +388,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
           int height, String sequenceSetId, String viewId)
   {
+    id = (++frameCount);
     setSize(width, height);
 
     if (al.getDataset() == null)
@@ -301,9 +398,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
 
-    alignPanel = new AlignmentPanel(this, viewport);
-
-    addAlignmentPanel(alignPanel, true);
+    // JalviewJS needs to distinguish a new panel from an old one in init()
+    // alignPanel = new AlignmentPanel(this, viewport);
+    // addAlignmentPanel(alignPanel, true);
     init();
   }
 
@@ -323,8 +420,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       viewport.hideSequence(hiddenSeqs);
     }
-    alignPanel = new AlignmentPanel(this, viewport);
-    addAlignmentPanel(alignPanel, true);
+    // alignPanel = new AlignmentPanel(this, viewport);
+    // addAlignmentPanel(alignPanel, true);
     init();
   }
 
@@ -340,7 +437,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport = ap.av;
     alignPanel = ap;
-    addAlignmentPanel(ap, false);
+    // addAlignmentPanel(ap, false);
     init();
   }
 
@@ -350,11 +447,34 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
+    boolean newPanel = (alignPanel == null);
+    viewport.setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    if (newPanel)
+    {
+      if (Platform.isJS())
+      {
+        // need to set this up front if NOANNOTATION is
+        // used in conjunction with SHOWOVERVIEW.
+
+        // I have not determined if this is appropriate for
+        // Jalview/Java, as it means we are setting this flag
+        // for all subsequent AlignFrames. For now, at least,
+        // I am setting it to be JalviewJS-only.
+
+        boolean showAnnotation = Jalview.getInstance().getShowAnnotation();
+        viewport.setShowAnnotation(showAnnotation);
+      }
+      alignPanel = new AlignmentPanel(this, viewport);
+    }
+    addAlignmentPanel(alignPanel, newPanel);
     // setBackground(Color.white); // BH 2019
 
     if (!Jalview.isHeadlessMode())
     {
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+      // JalviewJS options
+      statusPanel.setVisible(Jalview.getInstance().getShowStatus());
+      alignFrameMenuBar.setVisible(Jalview.getInstance().getAllowMenuBar());
     }
 
     avc = new jalview.controller.AlignViewController(this, viewport,
@@ -369,7 +489,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // modifyPID.setEnabled(false);
     }
 
-    String sortby = Cache.getDefault("SORT_ALIGNMENT", "No sort");
+    String sortby = Cache.getDefault(Preferences.SORT_ALIGNMENT,
+            "No sort");
 
     if (sortby.equals("Id"))
     {
@@ -380,8 +501,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       sortPairwiseMenuItem_actionPerformed(null);
     }
 
-    this.alignPanel.av
-            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    // BH see above
+    //
+    // this.alignPanel.av
+    // .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
 
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
@@ -396,12 +519,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     });
     buildColourMenu();
 
-    if (Desktop.desktop != null)
+    if (Desktop.getDesktopPane() != null)
     {
       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
+      addServiceListeners();
       if (!Platform.isJS())
       {
-        addServiceListeners();
       }
       setGUINucleotide();
     }
@@ -411,7 +534,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       wrapMenuItem_actionPerformed(null);
     }
 
-    if (Cache.getDefault("SHOW_OVERVIEW", false))
+    if (Cache.getDefault(Preferences.SHOW_OVERVIEW, false))
     {
       this.overviewMenuItem_actionPerformed(null);
     }
@@ -513,6 +636,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param format
    *          format of file
    */
+  @Deprecated
   public void setFileName(String file, FileFormatI format)
   {
     fileName = file;
@@ -521,6 +645,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
+   * 
+   * @param fileName
+   * @param file  from SwingJS; may contain bytes -- for reload
+   * @param protocol from SwingJS; may be RELATIVE_URL
+   * @param format
+   */
+  public void setFile(String fileName, File file, DataSourceType protocol, FileFormatI format)
+  {
+    this.fileName = fileName;
+    this.fileObject = file;
+    this.protocol = protocol;
+    setFileFormat(format);
+    reload.setEnabled(true);
+  }
+
+  /**
    * JavaScript will have this, maybe others. More dependable than a file name
    * and maintains a reference to the actual bytes loaded.
    * 
@@ -555,8 +695,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         switch (evt.getKeyCode())
         {
 
-        case 27: // escape key
-          deselectAllSequenceMenuItem_actionPerformed(null);
+        case KeyEvent.VK_ESCAPE: // escape key
+                                 // alignPanel.deselectAllSequences();
+          alignPanel.deselectAllSequences();
 
           break;
 
@@ -739,16 +880,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
           {
-            viewport.firePropertyChange("alignment", null,
-                    viewport.getAlignment().getSequences());
+            viewport.notifyAlignment();
           }
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
           {
-            viewport.firePropertyChange("alignment", null,
-                    viewport.getAlignment().getSequences());
+            viewport.notifyAlignment();
           }
           break;
         }
@@ -794,9 +933,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         ap.av.getAlignment().padGaps();
       }
-      ap.av.updateConservation(ap);
-      ap.av.updateConsensus(ap);
-      ap.av.updateStrucConsensus(ap);
+      if (Jalview.getInstance().getStartCalculations())
+      {
+        ap.av.updateConservation(ap);
+        ap.av.updateConsensus(ap);
+        ap.av.updateStrucConsensus(ap);
+        ap.av.initInformationWorker(ap);
+      }
     }
   }
 
@@ -815,55 +958,42 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return viewport;
   }
 
+  @Override
+  public void servicesChanged(WSDiscovererI discoverer,
+          Collection<? extends ServiceWithParameters> services)
+  {
+    buildWebServicesMenu();
+  }
   /* Set up intrinsic listeners for dynamically generated GUI bits. */
   private void addServiceListeners()
   {
-    final java.beans.PropertyChangeListener thisListener;
-    Desktop.instance.addJalviewPropertyChangeListener("services",
-            thisListener = new java.beans.PropertyChangeListener()
-            {
-              @Override
-              public void propertyChange(PropertyChangeEvent evt)
-              {
-                // // System.out.println("Discoverer property change.");
-                // if (evt.getPropertyName().equals("services"))
-                {
-                  SwingUtilities.invokeLater(new Runnable()
-                  {
-
-                    @Override
-                    public void run()
-                    {
-                      System.err.println(
-                              "Rebuild WS Menu for service change");
-                      BuildWebServiceMenu();
-                    }
-
-                  });
-                }
-              }
-            });
-    addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+    if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
     {
+      WSDiscovererI discoverer = SlivkaWSDiscoverer.getInstance();
+      discoverer.addServiceChangeListener(this);
+    }
+    if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
+    {
+      WSDiscovererI discoverer = Jws2Discoverer.getInstance();
+      discoverer.addServiceChangeListener(this);
+    }
+    // legacy event listener for compatibility with jws1
+    PropertyChangeListener legacyListener = (changeEvent) -> {
+      buildWebServicesMenu();
+    };
+    Desktop.getInstance().addJalviewPropertyChangeListener("services",legacyListener);
+    
+    addInternalFrameListener(new InternalFrameAdapter() {
       @Override
-      public void internalFrameClosed(
-              javax.swing.event.InternalFrameEvent evt)
-      {
-        // System.out.println("deregistering discoverer listener");
-        Desktop.instance.removeJalviewPropertyChangeListener("services",
-                thisListener);
+      public void internalFrameClosed(InternalFrameEvent e) {
+        System.out.println("deregistering discoverer listener");
+        SlivkaWSDiscoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
+        Jws2Discoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
+        Desktop.getInstance().removeJalviewPropertyChangeListener("services", legacyListener);
         closeMenuItem_actionPerformed(true);
       }
     });
-    // Finally, build the menu once to get current service state
-    new Thread(new Runnable()
-    {
-      @Override
-      public void run()
-      {
-        BuildWebServiceMenu();
-      }
-    }).start();
+    buildWebServicesMenu();
   }
 
   /**
@@ -924,13 +1054,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     scaleLeft.setVisible(av.getWrapAlignment());
     scaleRight.setVisible(av.getWrapAlignment());
     annotationPanelMenuItem.setState(av.isShowAnnotation());
-    /*
-     * Show/hide annotations only enabled if annotation panel is shown
-     */
-    showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    // Show/hide annotations only enabled if annotation panel is shown
+    syncAnnotationMenuItems(av.isShowAnnotation());
     viewBoxesMenuItem.setSelected(av.getShowBoxes());
     viewTextMenuItem.setSelected(av.getShowText());
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
@@ -939,6 +1064,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     showConsensusHistogram.setSelected(av.isShowConsensusHistogram());
     showSequenceLogo.setSelected(av.isShowSequenceLogo());
     normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
+    showInformationHistogram.setSelected(av.isShowInformationHistogram());
+    showHMMSequenceLogo.setSelected(av.isShowHMMSequenceLogo());
+    normaliseHMMSequenceLogo.setSelected(av.isNormaliseHMMSequenceLogo());
 
     ColourMenuHelper.setColourSelected(colourMenu,
             av.getGlobalColourScheme());
@@ -948,7 +1076,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
     showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
     showDbRefsMenuitem.setSelected(av.isShowDBRefs());
-    autoCalculate.setSelected(av.autoCalculateConsensus);
+    autoCalculate
+            .setSelected(av.getAutoCalculateConsensusAndConservation());
     sortByTree.setSelected(av.sortByTree);
     listenToViewSelections.setSelected(av.followSelection);
 
@@ -980,6 +1109,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     progressBar.setProgressBar(message, id);
   }
+  
+  @Override
+  public void removeProgressBar(long id)
+  {
+    progressBar.removeProgressBar(id);
+  }
 
   @Override
   public void registerHandler(final long id,
@@ -1031,102 +1166,357 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void addFromFile_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
+    Desktop.getInstance().inputLocalFileMenuItem_actionPerformed(viewport);
   }
 
   @Override
-  public void reload_actionPerformed(ActionEvent e)
+  public void hmmBuild_actionPerformed(boolean withDefaults)
   {
-    if (fileName != null)
+    if (!alignmentIsSufficient(1))
     {
-      // TODO: JAL-1108 - ensure all associated frames are closed regardless of
-      // originating file's format
-      // TODO: work out how to recover feature settings for correct view(s) when
-      // file is reloaded.
-      if (FileFormat.Jalview.equals(currentFileFormat))
-      {
-        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-        for (int i = 0; i < frames.length; i++)
+      return;
+    }
+
+    /*
+     * get default parameters, and optionally show a dialog
+     * to allow them to be modified
+     */
+    ParamDatastoreI store = HMMERParamStore.forBuild(viewport);
+    List<ArgumentI> args = store.getServiceParameters();
+
+    if (!withDefaults)
+    {
+      WsParamSetI set = new HMMERPreset();
+      WsJobParameters params = new WsJobParameters(store, set, args);
+      params.showRunDialog().thenAccept((startJob) -> {
+        if (startJob)
         {
-          if (frames[i] instanceof AlignFrame && frames[i] != this
-                  && ((AlignFrame) frames[i]).fileName != null
-                  && ((AlignFrame) frames[i]).fileName.equals(fileName))
-          {
-            try
-            {
-              frames[i].setSelected(true);
-              Desktop.instance.closeAssociatedWindows();
-            } catch (java.beans.PropertyVetoException ex)
-            {
-            }
-          }
+          var args2 = params.getJobParams();
+          new Thread(new HMMBuild(this, args2)).start();
+        }
+      });
+    }
+    else
+    {
+      new Thread(new HMMBuild(this, args)).start();
+    }
+  }
+
+  @Override
+  public void hmmAlign_actionPerformed(boolean withDefaults)
+  {
+    if (!(checkForHMM() && alignmentIsSufficient(2)))
+    {
+      return;
+    }
+
+    /*
+     * get default parameters, and optionally show a dialog
+     * to allow them to be modified
+     */
+    ParamDatastoreI store = HMMERParamStore.forAlign(viewport);
+    List<ArgumentI> args = store.getServiceParameters();
+
+    if (!withDefaults)
+    {
+      WsParamSetI set = new HMMERPreset();
+      WsJobParameters params = new WsJobParameters(store, set, args);
+      params.showRunDialog().thenAccept((startJob) -> {
+        if (startJob)
+        {
+          var args2 = params.getJobParams();
+          new Thread(new HMMAlign(this, args2)).start();
+        }
+      });
+    }
+    else
+    {
+      new Thread(new HMMAlign(this, args)).start();
+    }
+  }
+
+  @Override
+  public void hmmSearch_actionPerformed(boolean withDefaults)
+  {
+    if (!checkForHMM())
+    {
+      return;
+    }
+
+    /*
+     * get default parameters, and (if requested) show 
+     * dialog to allow modification
+     */
+    ParamDatastoreI store = HMMERParamStore.forSearch(viewport);
+    List<ArgumentI> args = store.getServiceParameters();
+
+    if (!withDefaults)
+    {
+      WsParamSetI set = new HMMERPreset();
+      WsJobParameters params = new WsJobParameters(store, set, args);
+      params.showRunDialog().thenAccept((startJob) -> {
+        if (startJob)
+        {
+          var args2 = params.getJobParams();
+          new Thread(new HMMSearch(this, args2)).start();
+          alignPanel.repaint();
+        }
+      });
+    }
+    else
+    {
+      new Thread(new HMMSearch(this, args)).start();
+      alignPanel.repaint();
+    }
+  }
 
+  @Override
+  public void jackhmmer_actionPerformed(boolean withDefaults)
+  {
+
+    /*
+     * get default parameters, and (if requested) show 
+     * dialog to allow modification
+     */
+
+    ParamDatastoreI store = HMMERParamStore.forJackhmmer(viewport);
+    List<ArgumentI> args = store.getServiceParameters();
+
+    if (!withDefaults)
+    {
+      WsParamSetI set = new HMMERPreset();
+      WsJobParameters params = new WsJobParameters(store, set, args);
+      params.showRunDialog().thenAccept((startJob) -> {
+        if (startJob)
+        {
+          var args2 = params.getJobParams();
+          new Thread(new JackHMMER(this, args2)).start();
+          alignPanel.repaint();
         }
-        Desktop.instance.closeAssociatedWindows();
+      });
+    }
+    else
+    {
+      new Thread(new JackHMMER(this, args)).start();
+      alignPanel.repaint();
+    }
+  }
+
+  /**
+   * Checks if the alignment has at least one hidden Markov model, if not shows
+   * a dialog advising to run hmmbuild or load an HMM profile
+   * 
+   * @return
+   */
+  private boolean checkForHMM()
+  {
+    if (viewport.getAlignment().getHmmSequences().isEmpty())
+    {
+      JOptionPane.showMessageDialog(this,
+              MessageManager.getString("warn.no_hmm"));
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected void filterByEValue_actionPerformed()
+  {
+    viewport.filterByEvalue(inputDouble("Enter E-Value Cutoff"));
+  }
+
+  @Override
+  protected void filterByScore_actionPerformed()
+  {
+    viewport.filterByScore(inputDouble("Enter Bit Score Threshold"));
+  }
+
+  private double inputDouble(String message)
+  {
+    String str = null;
+    Double d = null;
+    while (d == null || d <= 0)
+    {
+      str = JOptionPane.showInputDialog(this.alignPanel, message);
+      try
+      {
+        d = Double.valueOf(str);
+      } catch (NumberFormatException e)
+      {
+      }
+    }
+    return d;
+  }
+
+  /**
+   * Checks if the alignment contains the required number of sequences.
+   * 
+   * @param required
+   * @return
+   */
+  public boolean alignmentIsSufficient(int required)
+  {
+    if (getViewport().getSequenceSelection().length < required)
+    {
+      JOptionPane.showMessageDialog(this,
+              MessageManager.getString("label.not_enough_sequences"));
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Opens a file browser and adds the selected file, if in Fasta, Stockholm or
+   * Pfam format, to the list held under preference key "HMMSEARCH_DBS" (as a
+   * comma-separated list)
+   */
+  @Override
+  public void addDatabase_actionPerformed() throws IOException
+  {
+    if (Cache.getProperty(Preferences.HMMSEARCH_DBS) == null)
+    {
+      Cache.setProperty(Preferences.HMMSEARCH_DBS, "");
+    }
 
-        FileLoader loader = new FileLoader();
-        DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
-                ? DataSourceType.URL
-                : DataSourceType.FILE;
-        loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
+    String path = openFileChooser(false);
+    if (path != null && new File(path).exists())
+    {
+      IdentifyFile identifier = new IdentifyFile();
+      FileFormatI format = identifier.identify(path, DataSourceType.FILE);
+      if (format == FileFormat.Fasta || format == FileFormat.Stockholm
+              || format == FileFormat.Pfam)
+      {
+        String currentDbPaths = Cache
+                .getProperty(Preferences.HMMSEARCH_DBS);
+        currentDbPaths += Preferences.COMMA + path;
+        Cache.setProperty(Preferences.HMMSEARCH_DBS, currentDbPaths);
       }
       else
       {
-        Rectangle bounds = this.getBounds();
+        JOptionPane.showMessageDialog(this,
+                MessageManager.getString("warn.invalid_format"));
+      }
+    }
+  }
 
-        FileLoader loader = new FileLoader();
+  /**
+   * Opens a file chooser, optionally restricted to selecting folders
+   * (directories) only. Answers the path to the selected file or folder, or
+   * null if none is chosen.
+   * 
+   * @param
+   * @return
+   */
+  protected String openFileChooser(boolean forFolder)
+  {
+    // TODO duplicates GPreferences method - relocate to JalviewFileChooser?
+    String choice = null;
+    JFileChooser chooser = new JFileChooser();
+    if (forFolder)
+    {
+      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+    }
+    chooser.setDialogTitle(
+            MessageManager.getString("label.open_local_file"));
+    chooser.setToolTipText(MessageManager.getString("action.open"));
 
-        AlignFrame newframe = null;
+    int value = chooser.showOpenDialog(this);
 
-        if (fileObject == null)
-        {
+    if (value == JFileChooser.APPROVE_OPTION)
+    {
+      choice = chooser.getSelectedFile().getPath();
+    }
+    return choice;
+  }
 
-          DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(
-                  fileName) ? DataSourceType.URL : DataSourceType.FILE;
-          newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
-                  currentFileFormat);
-        }
-        else
+  @Override
+  public void reload_actionPerformed(ActionEvent e)
+  {
+    if (fileName == null && fileObject == null)
+    {
+      return;
+    }
+    // TODO: JAL-1108 - ensure all associated frames are closed regardless of
+    // originating file's format
+    // TODO: work out how to recover feature settings for correct view(s) when
+    // file is reloaded.
+    if (FileFormat.Jalview.equals(currentFileFormat))
+    {
+      JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
+      for (int i = 0; i < frames.length; i++)
+      {
+        if (frames[i] instanceof AlignFrame && frames[i] != this
+                && ((AlignFrame) frames[i]).fileName != null
+                && ((AlignFrame) frames[i]).fileName.equals(fileName))
         {
-          newframe = loader.LoadFileWaitTillLoaded(fileObject,
-                  DataSourceType.FILE, currentFileFormat);
+          try
+          {
+            frames[i].setSelected(true);
+            Desktop.getInstance().closeAssociatedWindows();
+          } catch (java.beans.PropertyVetoException ex)
+          {
+          }
         }
 
-        newframe.setBounds(bounds);
-        if (featureSettings != null && featureSettings.isShowing())
+      }
+      Desktop.getInstance().closeAssociatedWindows();
+
+      FileLoader loader = new FileLoader();
+      loader.LoadFile(viewport, (fileObject == null ? fileName : fileObject), protocol, currentFileFormat);
+    }
+    else
+    {
+      Rectangle bounds = this.getBounds();
+
+      FileLoader loader = new FileLoader();
+
+      AlignFrame newframe = null;
+
+      if (fileObject == null)
+      {
+        newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
+                currentFileFormat);
+      }
+      else
+      {
+        newframe = loader.LoadFileWaitTillLoaded(fileObject,
+                DataSourceType.FILE, currentFileFormat);
+      }
+
+      newframe.setBounds(bounds);
+      if (featureSettings != null && featureSettings.isShowing())
+      {
+        final Rectangle fspos = featureSettings.frame.getBounds();
+        // TODO: need a 'show feature settings' function that takes bounds -
+        // need to refactor Desktop.addFrame
+        newframe.featureSettings_actionPerformed(null);
+        final FeatureSettings nfs = newframe.featureSettings;
+        SwingUtilities.invokeLater(new Runnable()
         {
-          final Rectangle fspos = featureSettings.frame.getBounds();
-          // TODO: need a 'show feature settings' function that takes bounds -
-          // need to refactor Desktop.addFrame
-          newframe.featureSettings_actionPerformed(null);
-          final FeatureSettings nfs = newframe.featureSettings;
-          SwingUtilities.invokeLater(new Runnable()
+          @Override
+          public void run()
           {
-            @Override
-            public void run()
-            {
-              nfs.frame.setBounds(fspos);
-            }
-          });
-          this.featureSettings.close();
-          this.featureSettings = null;
-        }
-        this.closeMenuItem_actionPerformed(true);
+            nfs.frame.setBounds(fspos);
+          }
+        });
+        this.featureSettings.close();
+        this.featureSettings = null;
       }
+      this.closeMenuItem_actionPerformed(true);
     }
   }
 
   @Override
   public void addFromText_actionPerformed(ActionEvent e)
   {
-    Desktop.instance
+    Desktop.getInstance()
             .inputTextboxMenuItem_actionPerformed(viewport.getAlignPanel());
   }
 
   @Override
   public void addFromURL_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
+    Desktop.getInstance().inputURLMenuItem_actionPerformed(viewport);
   }
 
   @Override
@@ -1170,7 +1560,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // todo is this (2005) test now obsolete - value is never null?
     while (currentFileFormat == null)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.select_file_format_before_saving"),
               MessageManager.getString("label.file_format_not_specified"),
@@ -1261,7 +1651,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       statusBar.setText(MessageManager.formatMessage(
               "label.successfully_saved_to_file_in_format", new Object[]
-              { file, format }));
+              { fileName, format }));
 
       return;
     }
@@ -1456,6 +1846,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     bjs.exportHTML(null);
   }
 
+  // ??
   public void createImageMap(File file, String image)
   {
     alignPanel.makePNGImageMap(file, image);
@@ -1532,6 +1923,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   @Override
   public void associatedData_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
     final JalviewFileChooser chooser = new JalviewFileChooser(
             Cache.getProperty("LAST_DIRECTORY"));
@@ -1742,8 +2134,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null,
-              originalSource.getAlignment().getSequences());
+      originalSource.notifyAlignment();
     }
   }
 
@@ -1782,8 +2173,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null,
-              originalSource.getAlignment().getSequences());
+      originalSource.notifyAlignment();
     }
   }
 
@@ -1999,16 +2389,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     StringSelection ss = new StringSelection(output);
 
+    Desktop d = Desktop.getInstance();
     try
     {
-      jalview.gui.Desktop.internalCopy = true;
+      d.internalCopy = true;
       // Its really worth setting the clipboard contents
       // to empty before setting the large StringSelection!!
       Toolkit.getDefaultToolkit().getSystemClipboard()
               .setContents(new StringSelection(""), null);
 
       Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,
-              Desktop.instance);
+              d);
     } catch (OutOfMemoryError er)
     {
       new OOMWarning("copying region", er);
@@ -2028,7 +2419,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               hiddenCutoff, hiddenOffset);
     }
 
-    Desktop.jalviewClipboard = new Object[] { seqs,
+    d.jalviewClipboard = new Object[] { seqs,
         viewport.getAlignment().getDataset(), hiddenColumns };
     setStatus(MessageManager.formatMessage(
             "label.copied_sequences_to_clipboard", new Object[]
@@ -2040,9 +2431,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param e
    *          DOCUMENT ME!
+   * @throws InterruptedException
+   * @throws IOException
    */
   @Override
   protected void pasteNew_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
     paste(true);
   }
@@ -2052,9 +2446,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param e
    *          DOCUMENT ME!
+   * @throws InterruptedException
+   * @throws IOException
    */
   @Override
   protected void pasteThis_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
     paste(false);
   }
@@ -2064,8 +2461,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param newAlignment
    *          true to paste to a new alignment, otherwise add to this.
+   * @throws InterruptedException
+   * @throws IOException
    */
-  void paste(boolean newAlignment)
+  void paste(boolean newAlignment) throws IOException, InterruptedException
   {
     boolean externalPaste = true;
     try
@@ -2100,12 +2499,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       boolean annotationAdded = false;
       AlignmentI alignment = null;
 
-      if (Desktop.jalviewClipboard != null)
+      Desktop d = Desktop.getInstance();
+
+      if (d.jalviewClipboard != null)
       {
         // The clipboard was filled from within Jalview, we must use the
         // sequences
         // And dataset from the copied alignment
-        SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
+        SequenceI[] newseq = (SequenceI[]) d.jalviewClipboard[0];
         // be doubly sure that we create *new* sequence objects.
         sequences = new SequenceI[newseq.length];
         for (int i = 0; i < newseq.length; i++)
@@ -2130,10 +2531,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (newAlignment)
       {
 
-        if (Desktop.jalviewClipboard != null)
+        if (d.jalviewClipboard != null)
         {
           // dataset is inherited
-          alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
+          alignment.setDataset((Alignment) d.jalviewClipboard[1]);
         }
         else
         {
@@ -2149,8 +2550,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         alignment = viewport.getAlignment();
         alwidth = alignment.getWidth() + 1;
         // decide if we need to import sequences from an existing dataset
-        boolean importDs = Desktop.jalviewClipboard != null
-                && Desktop.jalviewClipboard[1] != alignment.getDataset();
+        boolean importDs = d.jalviewClipboard != null
+                && d.jalviewClipboard[1] != alignment.getDataset();
         // importDs==true instructs us to copy over new dataset sequences from
         // an existing alignment
         Vector<SequenceI> newDs = (importDs) ? new Vector<>() : null; // used to
@@ -2331,8 +2732,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           buildSortByAnnotationScoresMenu();
         }
-        viewport.firePropertyChange("alignment", null,
-                alignment.getSequences());
+        viewport.notifyAlignment();
         if (alignPanels != null)
         {
           for (AlignmentPanel ap : alignPanels)
@@ -2352,10 +2752,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 DEFAULT_HEIGHT);
         String newtitle = new String("Copied sequences");
 
-        if (Desktop.jalviewClipboard != null
-                && Desktop.jalviewClipboard[2] != null)
+        if (d.jalviewClipboard != null && d.jalviewClipboard[2] != null)
         {
-          HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+          HiddenColumns hc = (HiddenColumns) d.jalviewClipboard[2];
           af.viewport.setHiddenColumns(hc);
         }
 
@@ -2408,10 +2807,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               DEFAULT_HEIGHT);
       String newtitle = new String("Flanking alignment");
 
-      if (Desktop.jalviewClipboard != null
-              && Desktop.jalviewClipboard[2] != null)
+      Desktop d = Desktop.getInstance();
+      if (d.jalviewClipboard != null && d.jalviewClipboard[2] != null)
       {
-        HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+        HiddenColumns hc = (HiddenColumns) d.jalviewClipboard[2];
         af.viewport.setHiddenColumns(hc);
       }
 
@@ -2488,8 +2887,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         viewport.sendSelection();
         viewport.getAlignment().deleteGroup(sg);
 
-        viewport.firePropertyChange("alignment", null,
-                viewport.getAlignment().getSequences());
+        viewport.notifyAlignment();
         if (viewport.getAlignment().getHeight() < 1)
         {
           try
@@ -2498,6 +2896,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           } catch (Exception ex)
           {
           }
+        } else {
+          updateAll(null);
         }
       }
     };
@@ -2511,7 +2911,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             + 1) == viewport.getAlignment().getWidth()) ? true : false;
     if (wholeHeight && wholeWidth)
     {
-      JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+      JvOptionPane dialog = JvOptionPane
+              .newOptionDialog(Desktop.getDesktopPane());
       dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
       Object[] options = new Object[] {
           MessageManager.getString("action.ok"),
@@ -2538,10 +2939,26 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     if (avc.deleteGroups())
     {
-      PaintRefresher.Refresh(this, viewport.getSequenceSetId());
-      alignPanel.updateAnnotation();
+      updateAll(viewport.getSequenceSetId());
+    }
+  }
+
+  private void updateAll(String id)
+  {
+    if (id == null)
+    {
+      // this will force a non-fast repaint of both the IdPanel and SeqPanel
+      alignPanel.getIdPanel().getIdCanvas().setNoFastPaint();
+      alignPanel.getSeqPanel().seqCanvas.setNoFastPaint();
+      alignPanel.repaint();
+    }
+    else
+    {
+      // original version
+      PaintRefresher.Refresh(this, id);
       alignPanel.paintAlignment(true, true);
     }
+    alignPanel.updateAnnotation();
   }
 
   /**
@@ -2553,18 +2970,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceGroup sg = new SequenceGroup(
-            viewport.getAlignment().getSequences());
-
-    sg.setEndRes(viewport.getAlignment().getWidth() - 1);
-    viewport.setSelectionGroup(sg);
-    viewport.isSelectionGroupChanged(true);
-    viewport.sendSelection();
-    // JAL-2034 - should delegate to
-    // alignPanel to decide if overview needs
-    // updating.
-    alignPanel.paintAlignment(false, false);
-    PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    alignPanel.selectAllSequences();
   }
 
   /**
@@ -2576,21 +2982,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.cursorMode)
-    {
-      alignPanel.getSeqPanel().keyboardNo1 = null;
-      alignPanel.getSeqPanel().keyboardNo2 = null;
-    }
-    viewport.setSelectionGroup(null);
-    viewport.getColumnSelection().clear();
-    viewport.setSearchResults(null);
-    alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-    // JAL-2034 - should delegate to
-    // alignPanel to decide if overview needs
-    // updating.
-    alignPanel.paintAlignment(false, false);
-    PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
-    viewport.sendSelection();
+    alignPanel.deselectAllSequences();
   }
 
   /**
@@ -2606,7 +2998,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (sg == null)
     {
-      selectAllSequenceMenuItem_actionPerformed(null);
+      alignPanel.selectAllSequences();
 
       return;
     }
@@ -2711,8 +3103,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         }
       }
 
-      viewport.firePropertyChange("alignment", null,
-              viewport.getAlignment().getSequences());
+      viewport.notifyAlignment();
     }
   }
 
@@ -2761,8 +3152,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
     ranges.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2800,8 +3190,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2815,8 +3204,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void padGapsMenuitem_actionPerformed(ActionEvent e)
   {
     viewport.setPadGaps(padGapsMenuitem.isSelected());
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
   }
 
   /**
@@ -2986,7 +3374,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.gatherViews(this);
+    Desktop.getInstance().gatherViews(this);
   }
 
   /**
@@ -3371,11 +3759,26 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     final boolean setVisible = annotationPanelMenuItem.isSelected();
     viewport.setShowAnnotation(setVisible);
-    this.showAllSeqAnnotations.setEnabled(setVisible);
-    this.hideAllSeqAnnotations.setEnabled(setVisible);
-    this.showAllAlAnnotations.setEnabled(setVisible);
-    this.hideAllAlAnnotations.setEnabled(setVisible);
+    syncAnnotationMenuItems(setVisible);
     alignPanel.updateLayout();
+    repaint();
+    SwingUtilities.invokeLater(new Runnable() {
+
+      @Override
+      public void run()
+      {
+        alignPanel.updateScrollBarsFromRanges();
+      }
+      
+    });
+  }
+
+  private void syncAnnotationMenuItems(boolean setVisible)
+  {
+    showAllSeqAnnotations.setEnabled(setVisible);
+    hideAllSeqAnnotations.setEnabled(setVisible);
+    showAllAlAnnotations.setEnabled(setVisible);
+    hideAllAlAnnotations.setEnabled(setVisible);
   }
 
   @Override
@@ -3438,12 +3841,34 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     JInternalFrame frame = new JInternalFrame();
-    final OverviewPanel overview = new OverviewPanel(alignPanel);
+    // BH 2019.07.26 we allow for an embedded
+    // undecorated overview with defined size
+    frame.setName(Platform.getAppID("overview"));
+    //
+    Dimension dim = Platform.getDimIfEmbedded(frame, -1, -1);
+    if (dim != null && dim.width == 0)
+    {
+      dim = null; // hidden, not embedded
+    }
+    OverviewPanel overview = new OverviewPanel(alignPanel, dim);
     frame.setContentPane(overview);
+    if (dim == null)
+    {
+      dim = new Dimension();
+      // was frame.getSize(), but that is 0,0 at this point;
+    }
+    else
+    {
+      // we are imbedding, and so we have an undecorated frame
+      // and we can set the the frame dimensions accordingly.
+    }
+    // allowing for unresizable option using, style="resize:none"
+    boolean resizable = (Platform.getEmbeddedAttribute(frame,
+            "resize") != "none");
     Desktop.addInternalFrame(frame, MessageManager
             .formatMessage("label.overview_params", new Object[]
-            { this.getTitle() }), true, frame.getWidth(), frame.getHeight(),
-            true, true);
+            { this.getTitle() }), Desktop.FRAME_MAKE_VISIBLE, dim.width,
+            dim.height, resizable, Desktop.FRAME_ALLOW_ANY_SIZE);
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     frame.addInternalFrameListener(
@@ -3680,6 +4105,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     alignPanel.paintAlignment(true, false);
   }
 
+  @Override
+  public void sortEValueMenuItem_actionPerformed(ActionEvent e)
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByEValue(viewport.getAlignment());
+    addHistoryItem(new OrderCommand("Group Sort", oldOrder,
+            viewport.getAlignment()));
+    alignPanel.paintAlignment(true, false);
+
+  }
+
+  @Override
+  public void sortBitScoreMenuItem_actionPerformed(ActionEvent e)
+  {
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByBitScore(viewport.getAlignment());
+    addHistoryItem(new OrderCommand("Group Sort", oldOrder,
+            viewport.getAlignment()));
+    alignPanel.paintAlignment(true, false);
+
+  }
   /**
    * DOCUMENT ME!
    * 
@@ -3723,11 +4169,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void autoCalculate_actionPerformed(ActionEvent e)
   {
-    viewport.autoCalculateConsensus = autoCalculate.isSelected();
-    if (viewport.autoCalculateConsensus)
+    viewport.setAutoCalculateConsensusAndConservation(
+            autoCalculate.isSelected());
+    if (viewport.getAutoCalculateConsensusAndConservation())
     {
-      viewport.firePropertyChange("alignment", null,
-              viewport.getAlignment().getSequences());
+      viewport.notifyAlignment();
     }
   }
 
@@ -3770,7 +4216,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         if (_s.getLength() < sg.getEndRes())
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop,
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                   MessageManager.getString(
                           "label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
                   MessageManager.getString(
@@ -3802,7 +4248,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     frameTitle += this.title;
 
-    Desktop.addInternalFrame(tp, frameTitle, 600, 500);
+    Dimension dim = Platform.getDimIfEmbedded(tp, 600, 500);
+    Desktop.addInternalFrame(tp, frameTitle, dim.width, dim.height);
   }
 
   /**
@@ -3889,37 +4336,48 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     if (viewport.getAlignment().getAlignmentAnnotation()
-            .hashCode() != _annotationScoreVectorHash)
+            .hashCode() == _annotationScoreVectorHash)
     {
-      sortByAnnotScore.removeAll();
-      // almost certainly a quicker way to do this - but we keep it simple
-      Hashtable<String, String> scoreSorts = new Hashtable<>();
-      AlignmentAnnotation aann[];
-      for (SequenceI sqa : viewport.getAlignment().getSequences())
+      return;
+    }
+
+    sortByAnnotScore.removeAll();
+    Set<String> scoreSorts = new HashSet<>();
+    for (SequenceI sqa : viewport.getAlignment().getSequences())
+    {
+      AlignmentAnnotation[] anns = sqa.getAnnotation();
+      for (int i = 0; anns != null && i < anns.length; i++)
       {
-        aann = sqa.getAnnotation();
-        for (int i = 0; aann != null && i < aann.length; i++)
+        AlignmentAnnotation aa = anns[i];
+        if (aa != null && aa.hasScore() && aa.sequenceRef != null)
         {
-          if (aann[i].hasScore() && aann[i].sequenceRef != null)
-          {
-            scoreSorts.put(aann[i].label, aann[i].label);
-          }
+          scoreSorts.add(aa.label);
         }
       }
-      Enumeration<String> labels = scoreSorts.keys();
-      while (labels.hasMoreElements())
-      {
-        addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement());
-      }
-      sortByAnnotScore.setVisible(scoreSorts.size() > 0);
-      scoreSorts.clear();
-
-      _annotationScoreVectorHash = viewport.getAlignment()
-              .getAlignmentAnnotation().hashCode();
     }
+    for (String label : scoreSorts)
+    {
+      addSortByAnnotScoreMenuItem(sortByAnnotScore, label);
+    }
+    sortByAnnotScore.setVisible(!scoreSorts.isEmpty());
+
+    _annotationScoreVectorHash = viewport.getAlignment()
+            .getAlignmentAnnotation().hashCode();
   }
 
   /**
+   * Enable (or, if desired, make visible) the By Tree 
+   * submenu only if it has at least one element (or will have).
+   * 
+   */
+  @Override
+  protected void enableSortMenuOptions()
+  {
+    List<TreePanel> treePanels = getTreePanels();
+    sortByTreeMenu.setEnabled(!treePanels.isEmpty());
+  }
+  
+  /**
    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
    * call. Listeners are added to remove the menu item when the treePanel is
@@ -3931,24 +4389,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     sortByTreeMenu.removeAll();
 
-    List<Component> comps = PaintRefresher.components
-            .get(viewport.getSequenceSetId());
-    List<TreePanel> treePanels = new ArrayList<>();
-    for (Component comp : comps)
-    {
-      if (comp instanceof TreePanel)
-      {
-        treePanels.add((TreePanel) comp);
-      }
-    }
-
-    if (treePanels.size() < 1)
-    {
-      sortByTreeMenu.setVisible(false);
-      return;
-    }
-
-    sortByTreeMenu.setVisible(true);
+    List<TreePanel> treePanels = getTreePanels();
 
     for (final TreePanel tp : treePanels)
     {
@@ -3968,6 +4409,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  private List<TreePanel> getTreePanels()
+  {
+    List<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
+    List<TreePanel> treePanels = new ArrayList<>();
+    for (Component comp : comps)
+    {
+      if (comp instanceof TreePanel)
+      {
+        treePanels.add((TreePanel) comp);
+      }
+    }
+    return treePanels;
+  }
   public boolean sortBy(AlignmentOrder alorder, String undoname)
   {
     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
@@ -4091,7 +4546,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
         } catch (Exception ex)
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
+                  ex.getMessage(),
                   MessageManager
                           .getString("label.problem_reading_tree_file"),
                   JvOptionPane.WARNING_MESSAGE);
@@ -4099,7 +4555,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         }
         if (fin != null && fin.hasWarningMessage())
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop,
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                   fin.getWarningMessage(),
                   MessageManager.getString(
                           "label.possible_problem_with_tree_file"),
@@ -4154,14 +4610,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         tp = new TreePanel(alignPanel, nf, treeTitle, input);
 
-        tp.setSize(w, h);
+        Dimension dim = Platform.getDimIfEmbedded(tp, -1, -1);
+        if (dim == null)
+        {
+          dim = new Dimension(w, h);
+        }
+        else
+        {
+          // no offset, either
+          x = 0;
+        }
+        tp.setSize(dim.width, dim.height);
 
         if (x > 0 && y > 0)
         {
           tp.setLocation(x, y);
         }
 
-        Desktop.addInternalFrame(tp, treeTitle, w, h);
+        Desktop.addInternalFrame(tp, treeTitle, dim.width, dim.height);
       }
     } catch (Exception ex)
     {
@@ -4171,208 +4637,82 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return tp;
   }
 
-  private boolean buildingMenu = false;
 
   /**
-   * Generates menu items and listener event actions for web service clients
-   * 
+   * Schedule the web services menu rebuild to the event dispatch thread.
    */
-  public void BuildWebServiceMenu()
+  public void buildWebServicesMenu()
   {
-    while (buildingMenu)
-    {
-      try
+    SwingUtilities.invokeLater(() -> {
+      Console.info("Rebuiling WS menu");
+      webService.removeAll();
+      if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
       {
-        System.err.println("Waiting for building menu to finish.");
-        Thread.sleep(10);
-      } catch (Exception e)
+        Console.info("Building web service menu for slivka");
+        SlivkaWSDiscoverer discoverer = SlivkaWSDiscoverer.getInstance();
+        JMenu submenu = new JMenu("Slivka");
+        buildWebServicesMenu(discoverer, submenu);
+        webService.add(submenu);
+      }
+      if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
       {
+        WSDiscovererI jws2servs = Jws2Discoverer.getInstance();
+        JMenu submenu = new JMenu("JABAWS");
+        buildLegacyWebServicesMenu(submenu);
+        buildWebServicesMenu(jws2servs, submenu);
+        webService.add(submenu);
       }
-    }
-    final AlignFrame me = this;
-    buildingMenu = true;
-    new Thread(new Runnable()
+      build_fetchdbmenu(webService);
+    });
+  }
+
+  private void buildLegacyWebServicesMenu(JMenu menu)
+  {
+    JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
+    if (Discoverer.getServices() != null && Discoverer.getServices().size() > 0) 
     {
-      @Override
-      public void run()
+      var secstrpred = Discoverer.getServices().get("SecStrPred");
+      if (secstrpred != null) 
       {
-        final List<JMenuItem> legacyItems = new ArrayList<>();
-        try
-        {
-          // System.err.println("Building ws menu again "
-          // + Thread.currentThread());
-          // TODO: add support for context dependent disabling of services based
-          // on
-          // alignment and current selection
-          // TODO: add additional serviceHandle parameter to specify abstract
-          // handler
-          // class independently of AbstractName
-          // TODO: add in rediscovery GUI function to restart discoverer
-          // TODO: group services by location as well as function and/or
-          // introduce
-          // object broker mechanism.
-          final Vector<JMenu> wsmenu = new Vector<>();
-          final IProgressIndicator af = me;
-
-          /*
-           * do not i18n these strings - they are hard-coded in class
-           * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
-           * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
-           */
-          final JMenu msawsmenu = new JMenu("Alignment");
-          final JMenu secstrmenu = new JMenu(
-                  "Secondary Structure Prediction");
-          final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
-          final JMenu analymenu = new JMenu("Analysis");
-          final JMenu dismenu = new JMenu("Protein Disorder");
-          // JAL-940 - only show secondary structure prediction services from
-          // the legacy server
-          if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
-              // &&
-          Discoverer.services != null && (Discoverer.services.size() > 0))
-          {
-            // TODO: refactor to allow list of AbstractName/Handler bindings to
-            // be
-            // stored or retrieved from elsewhere
-            // No MSAWS used any more:
-            // Vector msaws = null; // (Vector)
-            // Discoverer.services.get("MsaWS");
-            Vector<ServiceHandle> secstrpr = Discoverer.services
-                    .get("SecStrPred");
-            if (secstrpr != null)
-            {
-              // Add any secondary structure prediction services
-              for (int i = 0, j = secstrpr.size(); i < j; i++)
-              {
-                final ext.vamsas.ServiceHandle sh = secstrpr.get(i);
-                jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
-                        .getServiceClient(sh);
-                int p = secstrmenu.getItemCount();
-                impl.attachWSMenuEntry(secstrmenu, me);
-                int q = secstrmenu.getItemCount();
-                for (int litm = p; litm < q; litm++)
-                {
-                  legacyItems.add(secstrmenu.getItem(litm));
-                }
-              }
-            }
-          }
-
-          // Add all submenus in the order they should appear on the web
-          // services menu
-          wsmenu.add(msawsmenu);
-          wsmenu.add(secstrmenu);
-          wsmenu.add(dismenu);
-          wsmenu.add(analymenu);
-          // No search services yet
-          // wsmenu.add(seqsrchmenu);
-
-          javax.swing.SwingUtilities.invokeLater(new Runnable()
-          {
-            @Override
-            public void run()
-            {
-              try
-              {
-                webService.removeAll();
-                // first, add discovered services onto the webservices menu
-                if (wsmenu.size() > 0)
-                {
-                  for (int i = 0, j = wsmenu.size(); i < j; i++)
-                  {
-                    webService.add(wsmenu.get(i));
-                  }
-                }
-                else
-                {
-                  webService.add(me.webServiceNoServices);
-                }
-                // TODO: move into separate menu builder class.
-                {
-                  // logic for 2.11.1.4 is
-                  // always look to see if there is a discover. if there isn't
-                  // we can't show any Jws2 services
-                  // if there are services available, show them - regardless of
-                  // the 'show JWS2 preference'
-                  // if the discoverer is running then say so
-                  // otherwise offer to trigger discovery if 'show JWS2' is not
-                  // enabled
-                  Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
-                  if (jws2servs != null)
-                  {
-                    if (jws2servs.hasServices())
-                    {
-                      jws2servs.attachWSMenuEntry(webService, me);
-                      for (Jws2Instance sv : jws2servs.getServices())
-                      {
-                        if (sv.description.toLowerCase(Locale.ROOT)
-                                .contains("jpred"))
-                        {
-                          for (JMenuItem jmi : legacyItems)
-                          {
-                            jmi.setVisible(false);
-                          }
-                        }
-                      }
-                    }
-
-                    if (jws2servs.isRunning())
-                    {
-                      JMenuItem tm = new JMenuItem(
-                              "Still discovering JABA Services");
-                      tm.setEnabled(false);
-                      webService.add(tm);
-                    }
-                    else if (!Cache.getDefault("SHOW_JWS2_SERVICES", true))
-                    {
-                      JMenuItem enableJws2 = new JMenuItem(
-                              "Discover Web Services");
-                      enableJws2.setToolTipText(
-                              "Select to start JABA Web Service discovery (or enable option in Web Service preferences)");
-                      enableJws2.setEnabled(true);
-                      enableJws2.addActionListener(new ActionListener()
-                      {
-
-                        @Override
-                        public void actionPerformed(ActionEvent e)
-                        {
-                          // start service discoverer, but ignore preference
-                          Desktop.instance.startServiceDiscovery(false,
-                                  true);
-                        }
-                      });
-                      webService.add(enableJws2);
-                    }
-                  }
-                }
-                build_urlServiceMenu(me.webService);
-                build_fetchdbmenu(webService);
-                for (JMenu item : wsmenu)
-                {
-                  if (item.getItemCount() == 0)
-                  {
-                    item.setEnabled(false);
-                  }
-                  else
-                  {
-                    item.setEnabled(true);
-                  }
-                }
-              } catch (Exception e)
-              {
-                Console.debug(
-                        "Exception during web service menu building process.",
-                        e);
-              }
-            }
-          });
-        } catch (Exception e)
+        for (ext.vamsas.ServiceHandle sh : secstrpred) 
         {
+          var menuProvider = Discoverer.getServiceClient(sh);
+          menuProvider.attachWSMenuEntry(secstrmenu, this);
         }
-        buildingMenu = false;
       }
-    }).start();
+    }
+    menu.add(secstrmenu);
+  }
 
+  /**
+   * Constructs the web services menu for the given discoverer under the
+   * specified menu. This method must be called on the EDT
+   * 
+   * @param discoverer
+   *          the discoverer used to build the menu
+   * @param menu
+   *          parent component which the elements will be attached to
+   */
+  private void buildWebServicesMenu(WSDiscovererI discoverer, JMenu menu)
+  {
+    if (discoverer.hasServices())
+    {
+      PreferredServiceRegistry.getRegistry().populateWSMenuEntry(
+              discoverer.getServices(), sv -> buildWebServicesMenu(), menu,
+              this, null);
+    }
+    if (discoverer.isRunning())
+    {
+      JMenuItem item = new JMenuItem("Service discovery in progress.");
+      item.setEnabled(false);
+      menu.add(item);
+    }
+    else if (!discoverer.hasServices())
+    {
+      JMenuItem item = new JMenuItem("No services available.");
+      item.setEnabled(false);
+      menu.add(item);
+    }
   }
 
   /**
@@ -4388,7 +4728,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
      * 
-     * @Override public void actionPerformed(ActionEvent e) {
+     *  public void actionPerformed(ActionEvent e) {
      * jalview.datamodel.AlignmentView
      * .testSelectionViews(af.viewport.getAlignment(),
      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
@@ -4505,8 +4845,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       final String errorTitle = MessageManager
               .getString("label.implementation_error")
               + MessageManager.getString("label.translation_failed");
-      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
-              JvOptionPane.ERROR_MESSAGE);
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+              errorTitle, JvOptionPane.ERROR_MESSAGE);
       return;
     }
     if (al == null || al.getHeight() == 0)
@@ -4515,8 +4855,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               "label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
       final String errorTitle = MessageManager
               .getString("label.translation_failed");
-      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
-              JvOptionPane.WARNING_MESSAGE);
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+              errorTitle, JvOptionPane.WARNING_MESSAGE);
     }
     else
     {
@@ -4529,7 +4869,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
       {
         final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
-        viewport.openSplitFrame(af, new Alignment(seqs));
+        AlignViewport.openSplitFrame(this, af, new Alignment(seqs));
       }
       else
       {
@@ -4562,7 +4902,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     // BH 2018
     return avc.parseFeaturesFile(file, sourceType,
-            Cache.getDefault("RELAXEDSEQIDMATCHING", false));
+            Cache.getDefault(Preferences.RELAXEDSEQIDMATCHING, false));
 
   }
 
@@ -4608,187 +4948,191 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
 
-    final AlignFrame thisaf = this;
     final List<Object> files = new ArrayList<>();
     List<DataSourceType> protocols = new ArrayList<>();
 
     try
     {
       Desktop.transferFromDropTarget(files, protocols, evt, t);
+      if (files.size() > 0)
+      {
+        new Thread(new Runnable()
+        {
+
+          @Override
+          public void run()
+          {
+            loadDroppedFiles(files, protocols, evt, t);
+          }
+        }).start();
+      }
     } catch (Exception e)
     {
       e.printStackTrace();
     }
-    if (files != null)
+  }
+
+  protected void loadDroppedFiles(List<Object> files,
+          List<DataSourceType> protocols, DropTargetDropEvent evt,
+          Transferable t)
+  {
+    try
     {
-      new Thread(new Runnable()
+      // check to see if any of these files have names matching sequences
+      // in
+      // the alignment
+      SequenceIdMatcher idm = new SequenceIdMatcher(
+              viewport.getAlignment().getSequencesArray());
+      /**
+       * Object[] { String,SequenceI}
+       */
+      ArrayList<Object[]> filesmatched = new ArrayList<>();
+      ArrayList<Object> filesnotmatched = new ArrayList<>();
+      for (int i = 0; i < files.size(); i++)
       {
-        @Override
-        public void run()
+        // BH 2018
+        Object fileObj = files.get(i);
+        String fileName = fileObj.toString();
+        String pdbfn = "";
+        DataSourceType protocol = (fileObj instanceof File
+                ? DataSourceType.FILE
+                : FormatAdapter.checkProtocol(fileName));
+        if (protocol == DataSourceType.FILE)
         {
-          try
+          File file;
+          if (fileObj instanceof File)
+          {
+            file = (File) fileObj;
+            Platform.cacheFileData(file);
+          }
+          else
+          {
+            file = new File(fileName);
+          }
+          pdbfn = file.getName();
+        }
+        else if (protocol == DataSourceType.URL)
+        {
+          URL url = new URL(fileName);
+          pdbfn = url.getFile();
+        }
+        if (pdbfn.length() > 0)
+        {
+          // attempt to find a match in the alignment
+          SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
+          int l = 0, c = pdbfn.indexOf(".");
+          while (mtch == null && c != -1)
           {
-            // check to see if any of these files have names matching sequences
-            // in
-            // the alignment
-            SequenceIdMatcher idm = new SequenceIdMatcher(
-                    viewport.getAlignment().getSequencesArray());
-            /**
-             * Object[] { String,SequenceI}
-             */
-            ArrayList<Object[]> filesmatched = new ArrayList<>();
-            ArrayList<Object> filesnotmatched = new ArrayList<>();
-            for (int i = 0; i < files.size(); i++)
+            do
             {
-              // BH 2018
-              Object file = files.get(i);
-              String fileName = file.toString();
-              String pdbfn = "";
-              DataSourceType protocol = (file instanceof File
-                      ? DataSourceType.FILE
-                      : FormatAdapter.checkProtocol(fileName));
-              if (protocol == DataSourceType.FILE)
-              {
-                File fl;
-                if (file instanceof File)
-                {
-                  fl = (File) file;
-                  Platform.cacheFileData(fl);
-                }
-                else
-                {
-                  fl = new File(fileName);
-                }
-                pdbfn = fl.getName();
-              }
-              else if (protocol == DataSourceType.URL)
-              {
-                URL url = new URL(fileName);
-                pdbfn = url.getFile();
-              }
-              if (pdbfn.length() > 0)
-              {
-                // attempt to find a match in the alignment
-                SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
-                int l = 0, c = pdbfn.indexOf(".");
-                while (mtch == null && c != -1)
-                {
-                  do
-                  {
-                    l = c;
-                  } while ((c = pdbfn.indexOf(".", l)) > l);
-                  if (l > -1)
-                  {
-                    pdbfn = pdbfn.substring(0, l);
-                  }
-                  mtch = idm.findAllIdMatches(pdbfn);
-                }
-                if (mtch != null)
-                {
-                  FileFormatI type;
-                  try
-                  {
-                    type = new IdentifyFile().identify(file, protocol);
-                  } catch (Exception ex)
-                  {
-                    type = null;
-                  }
-                  if (type != null && type.isStructureFile())
-                  {
-                    filesmatched.add(new Object[] { file, protocol, mtch });
-                    continue;
-                  }
-                }
-                // File wasn't named like one of the sequences or wasn't a PDB
-                // file.
-                filesnotmatched.add(file);
-              }
+              l = c;
+            } while ((c = pdbfn.indexOf(".", l)) > l);
+            if (l > -1)
+            {
+              pdbfn = pdbfn.substring(0, l);
             }
-            int assocfiles = 0;
-            if (filesmatched.size() > 0)
+            mtch = idm.findAllIdMatches(pdbfn);
+          }
+          if (mtch != null)
+          {
+            FileFormatI type;
+            try
             {
-              boolean autoAssociate = Cache
-                      .getDefault("AUTOASSOCIATE_PDBANDSEQS", false);
-              if (!autoAssociate)
-              {
-                String msg = MessageManager.formatMessage(
-                        "label.automatically_associate_structure_files_with_sequences_same_name",
-                        new Object[]
-                        { Integer.valueOf(filesmatched.size())
-                                .toString() });
-                String ttl = MessageManager.getString(
-                        "label.automatically_associate_structure_files_by_name");
-                int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
-                        ttl, JvOptionPane.YES_NO_OPTION);
-                autoAssociate = choice == JvOptionPane.YES_OPTION;
-              }
-              if (autoAssociate)
-              {
-                for (Object[] fm : filesmatched)
-                {
-                  // try and associate
-                  // TODO: may want to set a standard ID naming formalism for
-                  // associating PDB files which have no IDs.
-                  for (SequenceI toassoc : (SequenceI[]) fm[2])
-                  {
-                    PDBEntry pe = new AssociatePdbFileWithSeq()
-                            .associatePdbWithSeq(fm[0].toString(),
-                                    (DataSourceType) fm[1], toassoc, false,
-                                    Desktop.instance);
-                    if (pe != null)
-                    {
-                      System.err.println("Associated file : "
-                              + (fm[0].toString()) + " with "
-                              + toassoc.getDisplayId(true));
-                      assocfiles++;
-                    }
-                  }
-                  // TODO: do we need to update overview ? only if features are
-                  // shown I guess
-                  alignPanel.paintAlignment(true, false);
-                }
-              }
-              else
-              {
-                /*
-                 * add declined structures as sequences
-                 */
-                for (Object[] o : filesmatched)
-                {
-                  filesnotmatched.add(o[0]);
-                }
-              }
+              type = new IdentifyFile().identify(fileObj, protocol);
+            } catch (Exception ex)
+            {
+              type = null;
             }
-            if (filesnotmatched.size() > 0)
+            if (type != null && type.isStructureFile())
             {
-              if (assocfiles > 0 && (Cache.getDefault(
-                      "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
-                      || JvOptionPane.showConfirmDialog(thisaf,
-                              "<html>" + MessageManager.formatMessage(
-                                      "label.ignore_unmatched_dropped_files_info",
-                                      new Object[]
-                                      { Integer.valueOf(
-                                              filesnotmatched.size())
-                                              .toString() })
-                                      + "</html>",
-                              MessageManager.getString(
-                                      "label.ignore_unmatched_dropped_files"),
-                              JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
-              {
-                return;
-              }
-              for (Object fn : filesnotmatched)
+              filesmatched.add(new Object[] { fileObj, protocol, mtch });
+              continue;
+            }
+          }
+          // File wasn't named like one of the sequences or wasn't a PDB
+          // file.
+          filesnotmatched.add(fileObj);
+        }
+      }
+      int assocfiles = 0;
+      if (filesmatched.size() > 0)
+      {
+        boolean autoAssociate = Cache
+                .getDefault(Preferences.AUTOASSOCIATE_PDBANDSEQS, false);
+        if (!autoAssociate)
+        {
+          String msg = MessageManager.formatMessage(
+                  "label.automatically_associate_structure_files_with_sequences_same_name",
+                  new Object[]
+                  { Integer.valueOf(filesmatched.size()).toString() });
+          String ttl = MessageManager.getString(
+                  "label.automatically_associate_structure_files_by_name");
+          int choice = JvOptionPane.showConfirmDialog(this, msg, ttl,
+                  JvOptionPane.YES_NO_OPTION);
+          autoAssociate = choice == JvOptionPane.YES_OPTION;
+        }
+        if (autoAssociate)
+        {
+          for (Object[] fm : filesmatched)
+          {
+            // try and associate
+            // TODO: may want to set a standard ID naming formalism for
+            // associating PDB files which have no IDs.
+            for (SequenceI toassoc : (SequenceI[]) fm[2])
+            {
+              PDBEntry pe = AssociatePdbFileWithSeq.associatePdbWithSeq(
+                      fm[0].toString(), (DataSourceType) fm[1], toassoc,
+                      false);
+              if (pe != null)
               {
-                loadJalviewDataFile(fn, null, null, null);
+                System.err.println("Associated file : " + (fm[0].toString())
+                        + " with " + toassoc.getDisplayId(true));
+                assocfiles++;
               }
 
             }
-          } catch (Exception ex)
+            // TODO: do we need to update overview ? only if features are
+            // shown I guess
+            alignPanel.paintAlignment(true, false);
+          }
+        }
+        else
+        {
+          /*
+           * add declined structures as sequences
+           */
+          for (Object[] o : filesmatched)
           {
-            ex.printStackTrace();
+            filesnotmatched.add(o[0]);
           }
         }
-      }).start();
+      }
+      if (filesnotmatched.size() > 0)
+      {
+        if (assocfiles > 0 && (Cache
+                .getDefault("AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
+                || JvOptionPane.showConfirmDialog(this,
+                        "<html>" + MessageManager.formatMessage(
+                                "label.ignore_unmatched_dropped_files_info",
+                                new Object[]
+                                { Integer.valueOf(filesnotmatched.size())
+                                        .toString() })
+                                + "</html>",
+                        MessageManager.getString(
+                                "label.ignore_unmatched_dropped_files"),
+                        JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
+        {
+          return;
+        }
+        for (Object fn : filesnotmatched)
+        {
+          loadJalviewDataFile(fn, null, null, null);
+        }
+
+      }
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
     }
   }
 
@@ -4803,6 +5147,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param file
    *          either a filename or a URL string.
+   * @throws InterruptedException
+   * @throws IOException
    */
   public void loadJalviewDataFile(Object file, DataSourceType sourceType,
           FileFormatI format, SequenceI assocSeq)
@@ -4845,7 +5191,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             {
               // some problem - if no warning its probable that the ID matching
               // process didn't work
-              JvOptionPane.showMessageDialog(Desktop.desktop,
+              JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                       tcf.getWarningMessage() == null
                               ? MessageManager.getString(
                                       "label.check_file_matches_sequence_ids_alignment")
@@ -4919,10 +5265,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       if (isAnnotation)
       {
 
-        alignPanel.adjustAnnotationHeight();
-        viewport.updateSequenceIdColours();
-        buildSortByAnnotationScoresMenu();
-        alignPanel.paintAlignment(true, true);
+        updateForAnnotations();
       }
     } catch (Exception ex)
     {
@@ -4946,7 +5289,47 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       + (format != null
                               ? "(parsing as '" + format + "' file)"
                               : ""),
-              oom, Desktop.desktop);
+              oom, Desktop.getDesktopPane());
+    }
+  }
+
+  /**
+   * Do all updates necessary after an annotation file such as jnet. Also called
+   * from Jalview.loadAppletParams for "annotations", "jnetFile"
+   */
+
+  public void updateForAnnotations()
+  {
+    alignPanel.adjustAnnotationHeight();
+    viewport.updateSequenceIdColours();
+    buildSortByAnnotationScoresMenu();
+    alignPanel.paintAlignment(true, true);
+  }
+
+  /**
+   * Change the display state for the given feature groups -- Added by BH from
+   * JalviewLite
+   * 
+   * @param groups
+   *          list of group strings
+   * @param state
+   *          visible or invisible
+   */
+
+  public void setFeatureGroupState(String[] groups, boolean state)
+  {
+    jalview.api.FeatureRenderer fr = null;
+    viewport.setShowSequenceFeatures(true);
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
+    {
+
+      fr.setGroupVisibility(Arrays.asList(groups), state);
+      alignPanel.getSeqPanel().seqCanvas.repaint();
+      if (alignPanel.overviewPanel != null)
+      {
+        alignPanel.overviewPanel.updateOverviewImage();
+      }
     }
   }
 
@@ -5198,13 +5581,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       @Override
       public void run()
       {
-        final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
-                .getSequenceFetcherSingleton();
+        // ??
+        // final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
+        // .getSequenceFetcherSingleton();
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
           @Override
           public void run()
           {
+            jalview.ws.SequenceFetcher sf = jalview.ws.SequenceFetcher
+                    .getInstance();
             String[] dbclasses = sf.getNonAlignmentSources();
             List<DbSourceProxy> otherdb;
             JMenu dfetch = new JMenu();
@@ -5228,9 +5614,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               }
               if (otherdb.size() == 1)
               {
-                final DbSourceProxy[] dassource = otherdb
-                        .toArray(new DbSourceProxy[0]);
                 DbSourceProxy src = otherdb.get(0);
+                DbSourceProxy[] dassource = new DbSourceProxy[] { src };
                 fetchr = new JMenuItem(src.getDbSource());
                 fetchr.addActionListener(new ActionListener()
                 {
@@ -5431,9 +5816,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
   {
-    AlignmentI al = viewport.getAlignment();
-    al.justify(false);
-    viewport.firePropertyChange("alignment", null, al);
+    viewport.getAlignment().justify(false);
+    viewport.notifyAlignment();
   }
 
   /**
@@ -5442,9 +5826,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
   {
-    AlignmentI al = viewport.getAlignment();
-    al.justify(true);
-    viewport.firePropertyChange("alignment", null, al);
+    viewport.getAlignment().justify(true);
+    viewport.notifyAlignment();
   }
 
   @Override
@@ -5781,7 +6164,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception ex)
       {
         System.err.println((ex.toString()));
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.couldnt_run_groovy_script"),
                 MessageManager.getString("label.groovy_support_failed"),
                 JvOptionPane.ERROR_MESSAGE);
@@ -5868,6 +6251,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  /**
+   * Sets the status of the HMMER menu
+   */
+  public void updateHMMERStatus()
+  {
+    hmmerMenu.setEnabled(HmmerCommand.isHmmerAvailable());
+  }
   @Override
   protected void loadVcf_actionPerformed()
   {
@@ -5905,41 +6295,108 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     return lastFeatureSettingsBounds;
   }
-}
 
-class PrintThread extends Thread
-{
-  AlignmentPanel ap;
+  public void scrollTo(int row, int column)
+  {
+    alignPanel.getSeqPanel().scrollTo(row, column);
+  }
 
-  public PrintThread(AlignmentPanel ap)
+  public void scrollToRow(int row)
   {
-    this.ap = ap;
+    alignPanel.getSeqPanel().scrollToRow(row);
   }
 
-  static PageFormat pf;
+  public void scrollToColumn(int column)
+  {
+    alignPanel.getSeqPanel().scrollToColumn(column);
+  }
 
-  @Override
-  public void run()
+  /**
+   * BH 2019 from JalviewLite
+   * 
+   * get sequence feature groups that are hidden or shown
+   * 
+   * @param visible
+   *          true is visible
+   * @return list
+   */
+
+  public String[] getFeatureGroupsOfState(boolean visible)
   {
-    PrinterJob printJob = PrinterJob.getPrinterJob();
+    jalview.api.FeatureRenderer fr = null;
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
+    {
+      List<String> gps = fr.getGroups(visible);
+      String[] _gps = gps.toArray(new String[gps.size()]);
+      return _gps;
+    }
+    return null;
+  }
+
+  /**
+   * 
+   * @return list of feature groups on the view
+   */
 
-    if (pf != null)
+  public String[] getFeatureGroups()
+  {
+    jalview.api.FeatureRenderer fr = null;
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      printJob.setPrintable(ap, pf);
+      List<String> gps = fr.getFeatureGroups();
+      String[] _gps = gps.toArray(new String[gps.size()]);
+      return _gps;
     }
-    else
+    return null;
+  }
+
+  public void select(SequenceGroup sel, ColumnSelection csel,
+          HiddenColumns hidden)
+  {
+    alignPanel.getSeqPanel().selection(sel, csel, hidden, null);
+  }
+
+  public int getID()
+  {
+    return id;
+  }
+
+  static class PrintThread extends Thread
+  {
+    AlignmentPanel ap;
+
+    public PrintThread(AlignmentPanel ap)
     {
-      printJob.setPrintable(ap);
+      this.ap = ap;
     }
 
-    if (printJob.printDialog())
+    static PageFormat pf;
+
+    @Override
+    public void run()
     {
-      try
+      PrinterJob printJob = PrinterJob.getPrinterJob();
+
+      if (pf != null)
+      {
+        printJob.setPrintable(ap, pf);
+      }
+      else
       {
-        printJob.print();
-      } catch (Exception PrintException)
+        printJob.setPrintable(ap);
+      }
+
+      if (printJob.printDialog())
       {
-        PrintException.printStackTrace();
+        try
+        {
+          printJob.print();
+        } catch (Exception PrintException)
+        {
+          PrintException.printStackTrace();
+        }
       }
     }
   }
index 30ccdbe..2d82579 100644 (file)
@@ -40,6 +40,7 @@ import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.renderer.ResidueShader;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
@@ -60,6 +61,7 @@ import java.awt.FontMetrics;
 import java.awt.Rectangle;
 import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.swing.JInternalFrame;
@@ -73,6 +75,11 @@ import javax.swing.JInternalFrame;
 public class AlignViewport extends AlignmentViewport
         implements SelectionSource
 {
+  public final static int NO_SPLIT = 0;
+
+  public final static int SPLIT_FRAME = 1;
+
+  public final static int NEW_WINDOW = 2;
   Font font;
 
   boolean cursorMode = false;
@@ -215,7 +222,7 @@ public class AlignViewport extends AlignmentViewport
 
     setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
     setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
-    autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
+    autoCalculateConsensusAndConservation = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
 
     setPadGaps(Cache.getDefault("PAD_GAPS", true));
     setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
@@ -256,14 +263,13 @@ public class AlignViewport extends AlignmentViewport
 
     setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true);
 
-    alignment
-            .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
+               alignment.setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
 
     // We must set conservation and consensus before setting colour,
     // as Blosum and Clustal require this to be done
-    if (hconsensus == null && !isDataset)
+               if (hconsensus == null && !isDataset)
     {
-      if (!alignment.isNucleotide())
+                       if (!alignment.isNucleotide())
       {
         showConservation = Cache.getDefault("SHOW_CONSERVATION", true);
         showQuality = Cache.getDefault("SHOW_QUALITY", true);
@@ -275,13 +281,19 @@ public class AlignViewport extends AlignmentViewport
       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
       normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
               false);
+      // for now, use consensus options for Information till it gets its own
+      setShowHMMSequenceLogo(showSequenceLogo);
+      setNormaliseHMMSequenceLogo(normaliseSequenceLogo);
+      setShowInformationHistogram(showConsensusHistogram);
       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
 
       showOccupancy = Cache.getDefault(Preferences.SHOW_OCCUPANCY, true);
     }
     initAutoAnnotation();
-    String colourProperty = alignment.isNucleotide()
+    // initInformation();
+
+               String colourProperty = alignment.isNucleotide()
             ? Preferences.DEFAULT_COLOUR_NUC
             : Preferences.DEFAULT_COLOUR_PROT;
     String schemeName = Cache.getProperty(colourProperty);
@@ -304,11 +316,12 @@ public class AlignViewport extends AlignmentViewport
 
     if (residueShading != null)
     {
-      residueShading.setConsensus(hconsensus);
+                       residueShading.setConsensus(hconsensus);
     }
     setColourAppliesToAllGroups(true);
   }
 
+  
   boolean validCharWidth;
 
   /**
@@ -386,14 +399,14 @@ public class AlignViewport extends AlignmentViewport
     if (align != null)
     {
       StructureSelectionManager ssm = StructureSelectionManager
-              .getStructureSelectionManager(Desktop.instance);
+              .getStructureSelectionManager(Desktop.getInstance());
       ssm.registerMappings(align.getCodonFrames());
     }
 
     /*
      * replace mappings on our alignment
      */
-    if (alignment != null && align != null)
+               if (alignment != null && align != null)
     {
       alignment.setCodonFrames(align.getCodonFrames());
     }
@@ -408,7 +421,7 @@ public class AlignViewport extends AlignmentViewport
       if (mappings != null)
       {
         StructureSelectionManager ssm = StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance);
+                .getStructureSelectionManager(Desktop.getInstance());
         for (AlignedCodonFrame acf : mappings)
         {
           if (noReferencesTo(acf))
@@ -509,7 +522,7 @@ public class AlignViewport extends AlignmentViewport
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance)
+            .getStructureSelectionManager(Desktop.getInstance())
             .sendSelection(new SequenceGroup(getSelectionGroup()),
                     new ColumnSelection(getColumnSelection()),
                     new HiddenColumns(getAlignment().getHiddenColumns()),
@@ -555,16 +568,18 @@ public class AlignViewport extends AlignmentViewport
   public StructureSelectionManager getStructureSelectionManager()
   {
     return StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
   }
 
+  
   @Override
   public boolean isNormaliseSequenceLogo()
   {
     return normaliseSequenceLogo;
   }
 
-  public void setNormaliseSequenceLogo(boolean state)
+  @Override
+public void setNormaliseSequenceLogo(boolean state)
   {
     normaliseSequenceLogo = state;
   }
@@ -579,6 +594,7 @@ public class AlignViewport extends AlignmentViewport
     return validCharWidth;
   }
 
+  
   private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<>();
 
   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
@@ -721,7 +737,7 @@ public class AlignViewport extends AlignmentViewport
     }
 
     ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18
-    firePropertyChange("alignment", null, getAlignment().getSequences());
+    notifyAlignment();
   }
 
   /**
@@ -741,53 +757,61 @@ public class AlignViewport extends AlignmentViewport
     final String question = JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.open_split_window?"));
     final AlignViewport us = this;
-
+    
     /*
      * options No, Split Window, New Window correspond to
      * dialog responses 0, 1, 2 (even though JOptionPane shows them
      * in reverse order)
      */
-    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop)
-            .setResponseHandler(0, new Runnable()
+    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane())
+            .setResponseHandler(NO_SPLIT, new Runnable()
             {
               @Override
               public void run()
               {
                 addDataToAlignment(al);
               }
-            }).setResponseHandler(1, new Runnable()
+            }).setResponseHandler(SPLIT_FRAME, new Runnable()
             {
               @Override
               public void run()
               {
-                us.openLinkedAlignmentAs(al, title, true);
+                // Make a copy of this one to open it in a splitframe
+                openLinkedAlignmentAs(getAlignPanel().alignFrame,
+                        new Alignment(getAlignment()), al, title,
+                        SPLIT_FRAME);
               }
-            }).setResponseHandler(2, new Runnable()
+            }).setResponseHandler(NEW_WINDOW, new Runnable()
             {
               @Override
               public void run()
               {
-                us.openLinkedAlignmentAs(al, title, false);
+                openLinkedAlignmentAs(null, getAlignment(), al, title,
+                        NEW_WINDOW);
               }
             });
-    dialog.showDialog(question,
+      dialog.showDialog(question,
             MessageManager.getString("label.open_split_window"),
             JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
             options, options[0]);
   }
 
-  protected void openLinkedAlignmentAs(AlignmentI al, String title,
-          boolean newWindowOrSplitPane)
+  /**
+   * Open a split frame or a new window
+   * 
+   * @param al
+   * @param title
+   * @param mode
+   *          SPLIT_FRAME or NEW_WINDOW
+   */
+  public static void openLinkedAlignmentAs(AlignFrame thisFrame,
+          AlignmentI thisAlignment, AlignmentI al, String title, int mode)
   {
     /*
-     * Identify protein and dna alignments. Make a copy of this one if opening
-     * in a new split pane.
+     * Identify protein and dna alignments. 
      */
-    AlignmentI thisAlignment = newWindowOrSplitPane
-            ? new Alignment(getAlignment())
-            : getAlignment();
     AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
-    final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
+    AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
 
     /*
      * Map sequences. At least one should get mapped as we have already passed
@@ -816,7 +840,7 @@ public class AlignViewport extends AlignmentViewport
     // alignFrame.setFileName(file, format);
     // }
 
-    if (!newWindowOrSplitPane)
+    if (mode == NEW_WINDOW)
     {
       Desktop.addInternalFrame(newAlignFrame, title,
               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
@@ -829,10 +853,10 @@ public class AlignViewport extends AlignmentViewport
     {
     }
 
-    if (newWindowOrSplitPane)
+    if (mode == SPLIT_FRAME)
     {
       al.alignAs(thisAlignment);
-      protein = openSplitFrame(newAlignFrame, thisAlignment);
+      openSplitFrame(thisFrame, newAlignFrame, thisAlignment);
     }
   }
 
@@ -846,8 +870,8 @@ public class AlignViewport extends AlignmentViewport
    *          cdna/protein complement alignment to show in the other split half
    * @return the protein alignment in the split frame
    */
-  protected AlignmentI openSplitFrame(AlignFrame newAlignFrame,
-          AlignmentI complement)
+  static protected AlignmentI openSplitFrame(AlignFrame thisFrame,
+          AlignFrame newAlignFrame, AlignmentI complement)
   {
     /*
      * Make a new frame with a copy of the alignment we are adding to. If this
@@ -856,7 +880,7 @@ public class AlignViewport extends AlignmentViewport
      */
     AlignFrame copyMe = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH,
             AlignFrame.DEFAULT_HEIGHT);
-    copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
+    copyMe.setTitle(thisFrame.getTitle());
 
     AlignmentI al = newAlignFrame.viewport.getAlignment();
     final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
@@ -1019,11 +1043,10 @@ public class AlignViewport extends AlignmentViewport
     {
       return;
     }
-
     FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
             .getFeatureRenderer();
-    List<String> origRenderOrder = new ArrayList<>();
-    List<String> origGroups = new ArrayList<>();
+    List<String> origRenderOrder = new ArrayList(),
+            origGroups = new ArrayList();
     // preserve original render order - allows differentiation between user
     // configured colours and autogenerated ones
     origRenderOrder.addAll(fr.getRenderOrder());
@@ -1035,7 +1058,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?
     //
@@ -1049,6 +1072,8 @@ public class AlignViewport extends AlignmentViewport
     {
       FeatureColourI preferredColour = featureSettings
               .getFeatureColour(type);
+      FeatureMatcherSetI preferredFilters = featureSettings
+              .getFeatureFilters(type);
       FeatureColourI origColour = fr.getFeatureStyle(type);
       if (!mergeOnly || (!origRenderOrder.contains(type)
               || origColour == null
@@ -1064,6 +1089,11 @@ public class AlignViewport extends AlignmentViewport
         {
           fr.setColour(type, preferredColour);
         }
+        if (preferredFilters != null
+                && (!mergeOnly || fr.getFeatureFilter(type) != null))
+        {
+          fr.setFeatureFilter(type, preferredFilters);
+        }
         if (featureSettings.isFeatureDisplayed(type))
         {
           displayed.setVisible(type);
index e84aae0..fd53faf 100644 (file)
@@ -41,6 +41,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
@@ -123,6 +124,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
+    setName("AligmentPanel");
     // setBackground(Color.white); // BH 2019
     alignFrame = af;
     this.av = av;
@@ -174,6 +176,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           ranges.setViewportWidth(widthInRes);
           ranges.setViewportHeight(heightInSeq);
         }
+        repaint();
       }
 
     });
@@ -184,10 +187,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
       @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
-        if (evt.getPropertyName().equals("alignment"))
-        {
+        switch (evt.getPropertyName()) {  
+        case AlignmentViewport.PROPERTY_SEQUENCE:
+          updateScrollBarsFromRanges();
+          if (annotationPanel != null)
+            annotationPanel.paintImmediately(0,  0, getWidth(), getHeight());
+          break;
+        case AlignmentViewport.PROPERTY_ALIGNMENT:
+          updateScrollBarsFromRanges();
           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
           alignmentChanged();
+          break;
         }
       }
     };
@@ -260,32 +270,37 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int oldWidth = av.getIdWidth();
 
     // calculate sensible default width when no preference is available
-    Dimension r = null;
+    Dimension d = null;
     if (av.getIdWidth() < 0)
     {
-      int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
-      int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
-      int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
-      r = calculateIdWidth(maxwidth);
-      av.setIdWidth(r.width);
+      int maxWidth = getMaxWidth();
+      d = calculateIdWidth(maxWidth);
+      av.setIdWidth(d.width);
     }
     else
     {
-      r = new Dimension();
-      r.width = av.getIdWidth();
-      r.height = 0;
+      d = new Dimension();
+      d.width = av.getIdWidth();
+      d.height = 0;
     }
 
     /*
      * fudge: if desired width has changed, update layout
      * (see also paintComponent - updates layout on a repaint)
      */
-    if (r.width != oldWidth)
+    if (d.width != oldWidth)
     {
-      idPanelHolder.setPreferredSize(r);
+      idPanelHolder.setPreferredSize(d);
       validate();
     }
-    return r;
+    return d;
+  }
+
+  public int getMaxWidth()
+  {
+    int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
+    int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
+    return Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
   }
 
   /**
@@ -297,9 +312,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @return Dimension giving the maximum width of the alignment label panel
    *         that should be used.
    */
-  protected Dimension calculateIdWidth(int maxwidth)
+  public Dimension calculateIdWidth(int maxwidth)
   {
-    Container c = new Container();
+    Container c = this;// new Container();
 
     FontMetrics fm = c.getFontMetrics(
             new Font(av.font.getName(), Font.ITALIC, av.font.getSize()));
@@ -308,10 +323,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int i = 0;
     int idWidth = 0;
 
+    boolean withSuffix = av.getShowJVSuffix();
     while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
     {
       SequenceI s = al.getSequenceAt(i);
-      String id = s.getDisplayId(av.getShowJVSuffix());
+      String id = s.getDisplayId(withSuffix);
       int stringWidth = fm.stringWidth(id);
       idWidth = Math.max(idWidth, stringWidth);
       i++;
@@ -554,17 +570,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     // BH 2018.04.18 comment: addNotify() is not appropriate here. We
     // are not changing ancestors, and keyboard action listeners do
-    // not need to be reset. addNotify() is a very expensive operation,
+    // not need to be reset, and most importantly, we can't be sure we are actually
+    // connected to resources. 
+    
+    // addNotify() is a very expensive operation,
     // requiring a full re-layout of all parents and children.
+    
     // Note in JComponent:
+    
     // This method is called by the toolkit internally and should
     // not be called directly by programs.
+    
     // I note that addNotify() is called in several areas of Jalview.
 
     int annotationHeight = getAnnotationPanel().adjustPanelHeight();
     annotationHeight = getAnnotationPanel()
             .adjustForAlignFrame(adjustPanelHeight, annotationHeight);
 
+    // BH no!!
     hscroll.addNotify();
     annotationScroller.setPreferredSize(
             new Dimension(annotationScroller.getWidth(), annotationHeight));
@@ -572,6 +595,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     Dimension e = idPanel.getSize();
     alabels.setSize(new Dimension(e.width, annotationHeight));
 
+
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
             annotationSpaceFillerHolder.getWidth(), annotationHeight));
     annotationScroller.validate();
@@ -580,16 +604,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   /**
    * update alignment layout for viewport settings
-   * 
-   * @param wrap
-   *          DOCUMENT ME!
    */
   public void updateLayout()
   {
+    ViewportRanges ranges = av.getRanges();
     fontChanged();
     setAnnotationVisible(av.isShowAnnotation());
     boolean wrap = av.getWrapAlignment();
-    ViewportRanges ranges = av.getRanges();
     ranges.setStartSeq(0);
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
@@ -604,6 +625,22 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       annotationScroller.setVisible(true);
       annotationSpaceFillerHolder.setVisible(true);
+    }
+
+    idSpaceFillerPanel1.setVisible(!wrap);
+
+    /*
+     * defer dimension calculations if panel not yet added to a Window
+     * BH 2020.06.09
+     */
+    if (getTopLevelAncestor() == null)
+    {
+      repaint();
+      return;
+    }
+
+    if (!wrap && av.isShowAnnotation())
+    {
       validateAnnotationDimensions(false);
     }
 
@@ -627,7 +664,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
+    // System.out.println("ap dim = " + getSize());
+    // these values will go negative if getSize() returns (0,0):
+    // System.out.println("seqpan dim = " + getSeqPanel().getSize());
+    // System.out.println("seqcan dim = " + getSeqPanel().seqCanvas.getSize());
 
     repaint();
   }
@@ -641,10 +681,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          visible row to scroll to
    * 
    */
-  public void setScrollValues(int xpos, int ypos)
+  public void setScrollValues(int x, int y)
   {
-    int x = xpos;
-    int y = ypos;
 
     if (av == null || av.getAlignment() == null)
     {
@@ -660,45 +698,23 @@ public class AlignmentPanel extends GAlignmentPanel implements
       int width = av.getAlignment().getVisibleWidth();
       int height = av.getAlignment().getHeight();
 
-      hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
-      vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
-
-      if (hextent > width)
-      {
-        hextent = width;
-      }
-
-      if (vextent > height)
-      {
-        vextent = height;
-      }
-
-      if ((hextent + x) > width)
-      {
-        x = width - hextent;
-      }
-
-      if ((vextent + y) > height)
-      {
-        y = height - vextent;
-      }
-
-      if (y < 0)
-      {
-        y = 0;
-      }
-
-      if (x < 0)
-      {
-        x = 0;
-      }
-
-      // update the scroll values
-      hscroll.setValues(x, hextent, 0, width);
-      vscroll.setValues(y, vextent, 0, height);
+      
+      hextent = Math.min(getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(),  width);
+      vextent = Math.min(getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(),  height);
+  
+      x = Math.max(0, Math.min(x,  width - hextent));
+      y = Math.max(0, Math.min(y,  height - vextent));
+      
+      updateRanges(x, y);
+      updateScrollBars(x, y, width, height);
     }
   }
 
+  private void updateScrollBars(int x, int y, int width, int height) 
+  {
+    hscroll.setValues(x, hextent, 0, width);
+    vscroll.setValues(y, vextent, 0, height);
+  }
   /**
    * Respond to adjustment event when horizontal or vertical scrollbar is
    * changed
@@ -715,41 +731,55 @@ public class AlignmentPanel extends GAlignmentPanel implements
       return;
     }
 
-    ViewportRanges ranges = av.getRanges();
 
     if (evt.getSource() == hscroll)
     {
+      if (!updateRanges(hscroll.getValue(), Integer.MIN_VALUE))
+        return;
+    }
+    else if (evt.getSource() == vscroll)
+    {
+      if (!updateRanges(Integer.MIN_VALUE, vscroll.getValue()))
+        return;
+    }
+    repaint();
+  }
+
+  private boolean updateRanges(int x, int y)
+  {
+    ViewportRanges ranges = av.getRanges();
+    boolean isChanged = false;
+    if (x != Integer.MIN_VALUE)
+    {
       int oldX = ranges.getStartRes();
       int oldwidth = ranges.getViewportWidth();
-      int x = hscroll.getValue();
       int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
 
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
-      if ((x == oldX) && (width == oldwidth))
+      if (width > 0 && (x != oldX || width != oldwidth))
       {
-        return;
+        ranges.setViewportStartAndWidth(x, width);
+        isChanged = true;
       }
-      ranges.setViewportStartAndWidth(x, width);
     }
-    else if (evt.getSource() == vscroll)
+    if (y != Integer.MIN_VALUE)
     {
       int oldY = ranges.getStartSeq();
       int oldheight = ranges.getViewportHeight();
-      int y = vscroll.getValue();
       int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
-      if ((y == oldY) && (height == oldheight))
+      if (height > 0 && (y != oldY || height != oldheight))
       {
-        return;
+        ranges.setViewportStartAndHeight(y, height);
+        isChanged = true;
       }
-      ranges.setViewportStartAndHeight(y, height);
     }
-    repaint();
+    return isChanged;
   }
 
   /**
@@ -828,7 +858,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
             av.isShowAutocalculatedAbove());
     sorter.sort(getAlignment().getAlignmentAnnotation(),
             av.getSortAnnotationsBy());
-    repaint();
 
     if (updateStructures)
     {
@@ -837,16 +866,21 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (updateOverview)
     {
 
+      alignFrame.repaint();
       if (overviewPanel != null)
       {
         overviewPanel.updateOverviewImage();
       }
+    } else {
+      invalidate(); // needed so that the id width adjuster works correctly
+      repaint();
     }
   }
 
   @Override
   public void paintComponent(Graphics g)
   {
+    // BH OUCH!
     invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
@@ -860,7 +894,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
-    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+    // setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
     super.paintComponent(g);
   }
 
@@ -1067,8 +1101,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public int printWrappedAlignment(int pageWidth, int pageHeight,
           int pageNumber, Graphics g) throws PrinterException
   {
-    getSeqPanel().seqCanvas.calculateWrappedGeometry(getWidth(),
-            getHeight());
+    getSeqPanel().seqCanvas.calculateWrappedGeometry();
     int annotationHeight = 0;
     if (av.isShowAnnotation())
     {
@@ -1542,6 +1575,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     } catch (Exception ex)
     {
     }
+
     if (b)
     {
       setAlignFrameView();
@@ -1707,10 +1741,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public void propertyChange(PropertyChangeEvent evt)
   {
     // update this panel's scroll values based on the new viewport ranges values
-    ViewportRanges ranges = av.getRanges();
-    int x = ranges.getStartRes();
-    int y = ranges.getStartSeq();
-    setScrollValues(x, y);
+    updateScrollBarsFromRanges();
 
     // now update any complementary alignment (its viewport ranges object
     // is different so does not get automatically updated)
@@ -1722,6 +1753,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
   }
 
+  void updateScrollBarsFromRanges()
+  {
+    ViewportRanges ranges = av.getRanges();
+    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+  }
   /**
    * Set the reference to the PCA/Tree chooser dialog for this panel. This
    * reference should be nulled when the dialog is closed.
@@ -1742,4 +1778,201 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return calculationDialog;
   }
 
+  /**
+   * From appletgui, for JalviewJS JavaScript interface
+   * 
+   * preliminary - untested
+   * 
+   * @param ostart
+   * @param end
+   * @param seqIndex
+   * @param scrollToNearest
+   * @param redrawOverview
+   * @return
+   */
+  public boolean scrollTo(int ostart, int end, int seqIndex,
+          boolean scrollToNearest, boolean redrawOverview)
+  {
+    int startv, endv, starts, ends;// , width;
+
+    int start = -1;
+    if (av.hasHiddenColumns())
+    {
+      AlignmentI al = av.getAlignment();
+      start = al.getHiddenColumns().absoluteToVisibleColumn(ostart);
+      end = al.getHiddenColumns().absoluteToVisibleColumn(end);
+      if (start == end)
+      {
+        if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart))
+        {
+          // don't scroll - position isn't visible
+          return false;
+        }
+      }
+    }
+    else
+    {
+      start = ostart;
+    }
+
+    ViewportRanges ranges = av.getRanges();
+    if (!av.getWrapAlignment())
+    {
+      /*
+       * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =
+       * av.getStartRes()) >= start) { spos=start-1; // seqIn //
+       * setScrollValues(start - 1, seqIndex); } else if ((endv =
+       * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end -
+       * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts =
+       * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),
+       * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {
+       * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }
+       */
+
+      // below is scrolling logic up to Jalview 2.8.2
+      // if ((av.getStartRes() > end)
+      // || (av.getEndRes() < start)
+      // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
+      // {
+      // if (start > av.getAlignment().getWidth() - hextent)
+      // {
+      // start = av.getAlignment().getWidth() - hextent;
+      // if (start < 0)
+      // {
+      // start = 0;
+      // }
+      //
+      // }
+      // if (seqIndex > av.getAlignment().getHeight() - vextent)
+      // {
+      // seqIndex = av.getAlignment().getHeight() - vextent;
+      // if (seqIndex < 0)
+      // {
+      // seqIndex = 0;
+      // }
+      // }
+      // setScrollValues(start, seqIndex);
+      // }
+      // logic copied from jalview.gui.AlignmentPanel:
+      if ((startv = ranges.getStartRes()) >= start)
+      {
+        /*
+         * Scroll left to make start of search results visible
+         */
+        setScrollValues(start - 1, seqIndex);
+      }
+      else if ((endv = ranges.getEndRes()) <= end)
+      {
+        /*
+         * Scroll right to make end of search results visible
+         */
+        setScrollValues(startv + 1 + end - endv, seqIndex);
+      }
+      else if ((starts = ranges.getStartSeq()) > seqIndex)
+      {
+        /*
+         * Scroll up to make start of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), seqIndex);
+      }
+      else if ((ends = ranges.getEndSeq()) <= seqIndex)
+      {
+        /*
+         * Scroll down to make end of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
+      }
+      /*
+       * Else results are already visible - no need to scroll
+       */
+    }
+    else
+    {
+      ranges.scrollToWrappedVisible(start);
+    }
+
+    paintAlignment(redrawOverview, false);
+    return true;
+  }
+
+  private boolean holdRepaint = false;
+
+  /**
+   * Called by IdCanvas and SeqPanel to defer painting until after JVP loading.
+   * 
+   * @return true if holding
+   */
+  public boolean getHoldRepaint()
+  {
+    return holdRepaint;
+  }
+
+  /**
+   * Called by Jalview2xml while loading
+   * 
+   * @param tf
+   */
+  public void setHoldRepaint(boolean tf)
+  {
+    if (holdRepaint == tf)
+    {
+      return;
+    }
+    holdRepaint = tf;
+    if (!tf)
+    {
+      repaint();
+    }
+  }
+
+  @Override
+  public void repaint()
+  {
+    if (holdRepaint)
+    {
+      // System.out.println("AP repaint holding");
+      // Platform.stackTrace();
+      return;
+    }
+    super.repaint();
+  }
+
+  public void selectAllSequences()
+  {
+    selectSequences(av.getAlignment().getSequences());
+  }
+
+  public void deselectAllSequences()
+  {
+    if (av.cursorMode)
+    {
+      getSeqPanel().keyboardNo1 = null;
+      getSeqPanel().keyboardNo2 = null;
+    }
+    av.setSelectionGroup(null);
+    av.getColumnSelection().clear();
+    av.setSearchResults(null);
+    getIdPanel().getIdCanvas().searchResults = null;
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
+  public void selectSequences(List<SequenceI> seqs)
+  {
+    SequenceGroup sg = new SequenceGroup(seqs);
+    sg.setEndRes(av.getAlignment().getWidth() - 1);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
 }
index 791421d..233f280 100644 (file)
@@ -605,8 +605,8 @@ public class AnnotationChooser extends JPanel
     frame.setContentPane(this);
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     Desktop.addInternalFrame(frame,
-            MessageManager.getString("label.choose_annotations"),
-            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
+            MessageManager.getString("label.choose_annotations"), Desktop.FRAME_MAKE_VISIBLE,
+            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   protected void setShowSelected(boolean showSelected)
index 4e9a26d..477fd6a 100644 (file)
@@ -66,6 +66,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
   JPanel maxColour = new JPanel();
 
   private JCheckBox thresholdIsMin = new JCheckBox();
+  private JCheckBox transparency = new JCheckBox();
 
   protected static final int MIN_WIDTH = 500;
 
@@ -245,6 +246,19 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       }
     });
 
+    transparency.setBackground(Color.white);
+    transparency.setFont(JvSwingUtils.getLabelFont());
+    transparency
+            .setText(MessageManager.getString("Use Transparency"));
+    transparency.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        transparency_actionPerformed();
+      }
+    });
     this.setLayout(new BorderLayout());
     JPanel jPanel1 = new JPanel();
     JPanel jPanel2 = new JPanel();
@@ -257,6 +271,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     jPanel2.add(annotations, "grow, wrap");
     jPanel2.add(seqAssociated);
     jPanel2.add(useOriginalColours);
+    jPanel2.add(transparency);
     JPanel colpanel = new JPanel(new FlowLayout());
     colpanel.setBackground(Color.white);
     colpanel.add(minColour);
@@ -272,6 +287,10 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     this.validate();
   }
 
+  protected void transparency_actionPerformed()
+  {
+    updateView();
+  }
   protected void resetColours_actionPerformed()
   {
     setDefaultMinMax();
@@ -299,7 +318,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
         updateView();
       }
     };
-    JalviewColourChooser.showColourChooser(Desktop.getDesktop(), ttl,
+    JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(), ttl,
             colourPanel.getBackground(), listener);
   }
 
@@ -425,6 +444,8 @@ public class AnnotationColourChooser extends AnnotationRowFilter
 
     acg.setThresholdIsMinMax(thresholdIsMin.isSelected());
 
+    acg.setPositionToTransparency(transparency.isSelected());
+    
     this.ap.alignFrame.changeColour(acg);
 
     if (av.getAlignment().getGroups() != null)
index 1efd100..2ffe927 100644 (file)
@@ -111,8 +111,8 @@ public class AnnotationExporter extends JPanel
     frame.setContentPane(this);
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     Dimension preferredSize = frame.getPreferredSize();
-    Desktop.addInternalFrame(frame, "", true, preferredSize.width,
-            preferredSize.height, true, true);
+    Desktop.addInternalFrame(frame, "", Desktop.FRAME_MAKE_VISIBLE, preferredSize.width,
+            preferredSize.height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_ALLOW_ANY_SIZE);
   }
 
   /**
index 9976604..09624a8 100755 (executable)
@@ -36,6 +36,7 @@ import jalview.io.FormatAdapter;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.workers.InformationThread;
 
 import java.awt.Color;
 import java.awt.Cursor;
@@ -353,6 +354,8 @@ public class AnnotationLabels extends JPanel
       pop.show(this, evt.getX(), evt.getY());
       return;
     }
+    final AlignmentAnnotation ann = aa[selectedRow];
+    final boolean isSequenceAnnotation = ann.sequenceRef != null;
     item = new JMenuItem(EDITNAME);
     item.addActionListener(this);
     pop.add(item);
@@ -400,7 +403,8 @@ public class AnnotationLabels extends JPanel
     if (selectedRow < aa.length)
     {
       final String label = aa[selectedRow].label;
-      if (!aa[selectedRow].autoCalculated)
+      if (!(aa[selectedRow].autoCalculated)
+              && !(InformationThread.HMM_CALC_ID.equals(ann.getCalcId())))
       {
         if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
         {
@@ -408,7 +412,7 @@ public class AnnotationLabels extends JPanel
           pop.addSeparator();
           // av and sequencegroup need to implement same interface for
           item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
-                  aa[selectedRow].scaleColLabel);
+                         aa[selectedRow].scaleColLabel);
           item.addActionListener(this);
           pop.add(item);
         }
@@ -421,11 +425,169 @@ public class AnnotationLabels extends JPanel
         consclipbrd.addActionListener(this);
         pop.add(consclipbrd);
       }
+      else if (InformationThread.HMM_CALC_ID.equals(ann.getCalcId()))
+      {
+        addHmmerMenu(pop, ann);
+      }
     }
     pop.show(this, evt.getX(), evt.getY());
   }
 
   /**
+   * Adds context menu options for (alignment or group) Hmmer annotation
+   * 
+   * @param pop
+   * @param ann
+   */
+  protected void addHmmerMenu(JPopupMenu pop, final AlignmentAnnotation ann)
+  {
+    final boolean isGroupAnnotation = ann.groupRef != null;
+    pop.addSeparator();
+    final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
+            MessageManager.getString(
+                    "label.ignore_below_background_frequency"),
+            isGroupAnnotation
+                    ? ann.groupRef
+                            .isIgnoreBelowBackground()
+                    : ap.av.isIgnoreBelowBackground());
+    cbmi.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        if (isGroupAnnotation)
+        {
+          if (!ann.groupRef.isUseInfoLetterHeight())
+          {
+            ann.groupRef.setIgnoreBelowBackground(cbmi.getState());
+            // todo and recompute group annotation
+          }
+        }
+        else if (!ap.av.isInfoLetterHeight())
+        {
+          ap.av.setIgnoreBelowBackground(cbmi.getState(), ap);
+          // todo and recompute annotation
+        }
+        ap.alignmentChanged(); // todo not like this
+      }
+    });
+    pop.add(cbmi);
+    final JCheckBoxMenuItem letterHeight = new JCheckBoxMenuItem(
+            MessageManager.getString("label.use_info_for_height"),
+            isGroupAnnotation ? ann.groupRef.isUseInfoLetterHeight()
+                    : ap.av.isInfoLetterHeight());
+    letterHeight.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        if (isGroupAnnotation)
+        {
+          ann.groupRef.setInfoLetterHeight((letterHeight.getState()));
+          ann.groupRef.setIgnoreBelowBackground(true);
+          // todo and recompute group annotation
+        }
+        else
+        {
+          ap.av.setInfoLetterHeight(letterHeight.getState(), ap);
+          ap.av.setIgnoreBelowBackground(true, ap);
+          // todo and recompute annotation
+        }
+        ap.alignmentChanged();
+      }
+    });
+    pop.add(letterHeight);
+    if (isGroupAnnotation)
+    {
+      final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+              MessageManager.getString("label.show_group_histogram"),
+              ann.groupRef.isShowInformationHistogram());
+      chist.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          ann.groupRef.setShowInformationHistogram(chist.getState());
+          ap.repaint();
+        }
+      });
+      pop.add(chist);
+      final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
+              MessageManager.getString("label.show_group_logo"),
+              ann.groupRef.isShowHMMSequenceLogo());
+      cprofl.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          ann.groupRef.setShowHMMSequenceLogo(cprofl.getState());
+          ap.repaint();
+        }
+      });
+      pop.add(cprofl);
+      final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
+              MessageManager.getString("label.normalise_group_logo"),
+              ann.groupRef.isNormaliseHMMSequenceLogo());
+      cproflnorm.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          ann.groupRef
+                  .setNormaliseHMMSequenceLogo(cproflnorm.getState());
+          // automatically enable logo display if we're clicked
+          ann.groupRef.setShowHMMSequenceLogo(true);
+          ap.repaint();
+        }
+      });
+      pop.add(cproflnorm);
+    }
+    else
+    {
+      final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+              MessageManager.getString("label.show_histogram"),
+              av.isShowInformationHistogram());
+      chist.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          av.setShowInformationHistogram(chist.getState());
+          ap.repaint();
+        }
+      });
+      pop.add(chist);
+      final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
+              MessageManager.getString("label.show_logo"),
+              av.isShowHMMSequenceLogo());
+      cprof.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          av.setShowHMMSequenceLogo(cprof.getState());
+          ap.repaint();
+        }
+      });
+      pop.add(cprof);
+      final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
+              MessageManager.getString("label.normalise_logo"),
+              av.isNormaliseHMMSequenceLogo());
+      cprofnorm.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          av.setShowHMMSequenceLogo(true);
+          av.setNormaliseHMMSequenceLogo(cprofnorm.getState());
+          ap.repaint();
+        }
+      });
+      pop.add(cprofnorm);
+    }
+  }
+
+  /**
    * A helper method that adds menu options for calculation and visualisation of
    * group and/or alignment consensus annotation to a popup menu. This is
    * designed to be reusable for either unwrapped mode (popup menu is shown on
@@ -971,7 +1133,7 @@ public class AnnotationLabels extends JPanel
             seqs, omitHidden, alignmentStartEnd);
 
     Toolkit.getDefaultToolkit().getSystemClipboard()
-            .setContents(new StringSelection(output), Desktop.instance);
+            .setContents(new StringSelection(output), Desktop.getInstance());
 
     HiddenColumns hiddenColumns = null;
 
@@ -981,20 +1143,12 @@ public class AnnotationLabels extends JPanel
               av.getAlignment().getHiddenColumns());
     }
 
-    Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset
-                                                        // of a consensus
-                                                        // sequence ? need to
-                                                        // flag
-        // sequence as special.
+    // what is the dataset of a consensus sequence? 
+    // need to flag sequence as special.
+    Desktop.getInstance().jalviewClipboard = new Object[] { seqs, ds, 
         hiddenColumns };
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g1
-   *          DOCUMENT ME!
-   */
   @Override
   public void paintComponent(Graphics g)
   {
index e9ef19c..93af6ec 100755 (executable)
@@ -150,6 +150,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    */
   public AnnotationPanel(AlignmentPanel ap)
   {
+    setName("AnnotationPanel");
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
@@ -726,13 +727,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
 
     if (dragMode == DragMode.Undefined)
-    {
+      {
       /*
        * drag is diagonal - defer deciding whether to
        * treat as up/down or left/right
        */
-      return;
-    }
+        return;
+      }
 
     try
     {
@@ -749,6 +750,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
           int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
           graphAnnotation.graphHeight = newHeight;
           adjustPanelHeight();
+          setNoFastPaint();
           ap.paintAlignment(false, false);
         }
       }
@@ -890,6 +892,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
             && ann.annotations[column] != null)
     {
       tooltip = ann.annotations[column].description;
+      if ("".equals(tooltip))
+      {
+        tooltip = null;
+      }
     }
 
     return tooltip;
@@ -1001,7 +1007,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   private volatile boolean imageFresh = false;
-
   private Rectangle visibleRect = new Rectangle(),
           clipBounds = new Rectangle();
 
@@ -1014,7 +1019,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   @Override
   public void paintComponent(Graphics g)
   {
-
     // BH: note that this method is generally recommended to
     // call super.paintComponent(g). Otherwise, the children of this
     // component will not be rendered. That is not needed here
@@ -1022,11 +1026,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     // just a JPanel contained in a JViewPort.
 
     computeVisibleRect(visibleRect);
-
     g.setColor(Color.white);
     g.fillRect(0, 0, visibleRect.width, visibleRect.height);
 
-    if (image != null)
+    ViewportRanges ranges = av.getRanges();
+
+    if (allowFastPaint && image != null)
     {
       // BH 2018 optimizing generation of new Rectangle().
       if (fastPaint
@@ -1035,15 +1040,17 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
               || (visibleRect.height != clipBounds.height))
       {
 
-        g.drawImage(image, 0, 0, this);
+         
+         g.drawImage(image, 0, 0, this);
         fastPaint = false;
         return;
       }
     }
-    imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
-            + 1) * av.getCharWidth();
+    imgWidth = (ranges.getEndRes() - ranges.getStartRes() + 1)
+            * av.getCharWidth();
     if (imgWidth < 1)
     {
+      fastPaint = false;
       return;
     }
     Graphics2D gg;
@@ -1088,7 +1095,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       gg = (Graphics2D) image.getGraphics();
 
     }
-
+    
     drawComponent(gg, av.getRanges().getStartRes(),
             av.getRanges().getEndRes() + 1);
     gg.dispose();
@@ -1097,11 +1104,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   /**
-   * set true to enable redraw timing debug output on stderr
-   */
-  private final boolean debugRedraw = false;
-
-  /**
    * non-Thread safe repaint
    * 
    * @param horizontal
@@ -1122,6 +1124,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     int er = av.getRanges().getEndRes() + 1;
     int transX = 0;
 
+    if (er == sr + 1)
+    {
+      fastPaint = false;
+      return;
+    }
     Graphics2D gg = (Graphics2D) image.getGraphics();
 
     if (imgWidth>Math.abs(horizontal*av.getCharWidth())) {
@@ -1139,6 +1146,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         er = sr - horizontal;
       }
     }
+
     gg.translate(transX, 0);
 
     drawComponent(gg, sr, er);
@@ -1146,7 +1154,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     gg.translate(-transX, 0);
 
     gg.dispose();
-
     fastPaint = true;
 
     // Call repaint on alignment panel so that repaints from other alignment
@@ -1264,6 +1271,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
   private int[] bounds = new int[2];
 
+  private boolean allowFastPaint;
   @Override
   public int[] getVisibleVRange()
   {
@@ -1370,4 +1378,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
     return annotationHeight;
   }
+  
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
 }
index 44a1668..fbb881c 100644 (file)
@@ -424,6 +424,7 @@ public abstract class AnnotationRowFilter extends JPanel
 
     updateView();
   }
+  
 
   protected void propagateSeqAssociatedThreshold(boolean allAnnotation,
           AlignmentAnnotation annotation)
index 2447a2f..0c36745 100644 (file)
@@ -52,7 +52,6 @@ import jalview.util.BrowserLauncher;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-
 public class AppJmol extends StructureViewerBase
 {
   // ms to wait for Jmol to load files
@@ -106,7 +105,6 @@ public class AppJmol extends StructureViewerBase
               .toArray(new SequenceI[sequencesForPdb.size()]);
       i++;
     }
-
     // TODO: check if protocol is needed to be set, and if chains are
     // autodiscovered.
     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
@@ -134,7 +132,6 @@ public class AppJmol extends StructureViewerBase
       seqColour.setSelected(true);
       viewerColour.setSelected(false);
     }
-
     this.setBounds(viewerModel.getX(), viewerModel.getY(),
             viewerModel.getWidth(), viewerModel.getHeight());
     setViewId(viewid);
@@ -159,6 +156,7 @@ public class AppJmol extends StructureViewerBase
   {
     super.initMenus();
 
+
     viewerColour
             .setText(MessageManager.getString("label.colour_with_jmol"));
     viewerColour.setToolTipText(MessageManager
@@ -457,6 +455,7 @@ public class AppJmol extends StructureViewerBase
   @Override
   public void showConsole(boolean showConsole)
   {
+
     if (showConsole)
     {
       if (splitPane == null)
index 3f8175d..844faa7 100644 (file)
@@ -283,7 +283,7 @@ public class AppJmolBinding extends JalviewJmolBinding
     }
     if (errormsgs.length() > 0)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+       JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
               MessageManager.formatMessage(
                       "label.pdb_entries_couldnt_be_retrieved", new String[]
                       { errormsgs.toString() }),
index fe0aedf..5b5bd18 100644 (file)
  */
 package jalview.gui;
 
-import jalview.api.StructureSelectionManagerProvider;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
-import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 
-import javax.swing.JOptionPane;
-
 /**
- * GUI related routines for associating PDB files with sequences
+ * GUI related routines for associating PDB files with sequences. A single
+ * static method.
  * 
  * @author JimP
  * 
@@ -39,58 +36,56 @@ import javax.swing.JOptionPane;
 public class AssociatePdbFileWithSeq
 {
 
+  private AssociatePdbFileWithSeq()
+  {
+    // inaccessible
+  }
+
   /**
-   * assocate the given PDB file with
+   * Associate the given PDB file name or URL with a sequence. Do not map
+   * mouse-over events.
    * 
-   * @param choice
+   * @param fileName
+   *          or URL
+   * @param type
+   *          will be DataType.FILE or DataType.URL
    * @param sequence
+   *          to associate
+   * @param prompt
+   *          true if the user should be asked what to do if the specified file
+   *          does not seem to contain PDB information (StructureChooser only)
+   * @return null if file is not found
    */
-  public PDBEntry associatePdbWithSeq(String choice, DataSourceType file,
-          SequenceI sequence, boolean prompt,
-          StructureSelectionManagerProvider ssmp)
+  public static PDBEntry associatePdbWithSeq(String fileName,
+          DataSourceType type, SequenceI sequence, boolean prompt)
   {
     PDBEntry entry = new PDBEntry();
     StructureFile pdbfile = null;
-    pdbfile = StructureSelectionManager.getStructureSelectionManager(ssmp)
+    pdbfile = Desktop.getStructureSelectionManager()
             .setMapping(false, new SequenceI[]
-            { sequence }, null, choice, file);
+            { sequence }, null, fileName, type);
     if (pdbfile == null)
     {
       // stacktrace already thrown so just return
       return null;
     }
-    if (pdbfile.getId() == null)
-    {
-      String reply = null;
-
-      if (prompt)
-      {
-        reply = JvOptionPane.showInternalInputDialog(Desktop.desktop,
-                MessageManager
-                        .getString("label.couldnt_find_pdb_id_in_file"),
-                MessageManager.getString("label.no_pdb_id_in_file"),
-                JvOptionPane.QUESTION_MESSAGE);
-      }
-      if (reply == null)
-      {
-        return null;
-      }
-
-      entry.setId(reply);
-    }
-    else
+    String id = pdbfile.getId();
+    if (id == null && (id = (prompt
+            ? JvOptionPane.showInternalInputDialog(Desktop.getDesktopPane(),
+                    MessageManager
+                            .getString("label.couldnt_find_pdb_id_in_file"),
+                    MessageManager.getString("label.no_pdb_id_in_file"),
+                    JvOptionPane.QUESTION_MESSAGE)
+            : null)) == null)
     {
-      entry.setId(pdbfile.getId());
+      return null;
     }
+    entry.setId(id);
     entry.setType(PDBEntry.Type.FILE);
-
-    if (pdbfile != null)
-    {
-      entry.setFile(choice);
-      sequence.getDatasetSequence().addPDBId(entry);
-      StructureSelectionManager.getStructureSelectionManager(ssmp)
-              .registerPDBEntry(entry);
-    }
+    entry.setFile(fileName);
+    sequence.getDatasetSequence().addPDBId(entry);
+    Desktop.getStructureSelectionManager()
+            .registerPDBEntry(entry);
     return entry;
   }
 }
index f7e5413..830e21e 100644 (file)
@@ -23,12 +23,12 @@ package jalview.gui;
 import jalview.analysis.TreeBuilder;
 import jalview.analysis.scoremodels.ScoreModels;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.AlignViewportI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.api.analysis.SimilarityParamsI;
 import jalview.bin.Cache;
 import jalview.datamodel.SequenceGroup;
 import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -62,8 +62,13 @@ import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
 /**
- * A dialog where a user can choose and action Tree or PCA calculation options
+ * A dialog where a user can choose and action Tree or PCA calculation options.
+ * 
+ * Allows also for dialog-free static methods openPCAPanel(...) and
+ * openTreePanel(...) for scripted use.
+ * 
  */
+@SuppressWarnings("serial")
 public class CalculationChooser extends JPanel
 {
   /*
@@ -74,7 +79,7 @@ public class CalculationChooser extends JPanel
    */
   private static boolean treeMatchGaps = true;
 
-  private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
+  private static Font VERDANA_11PT;
 
   private static final int MIN_TREE_SELECTION = 3;
 
@@ -102,7 +107,7 @@ public class CalculationChooser extends JPanel
 
   private JCheckBox shorterSequence;
 
-  final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
+  private static ComboBoxTooltipRenderer renderer; // BH was not static
 
   List<String> tips = new ArrayList<>();
 
@@ -112,6 +117,37 @@ public class CalculationChooser extends JPanel
   private PCAPanel pcaPanel;
 
   /**
+   * Open a new Tree panel on the desktop statically. Params are standard (not
+   * set by Groovy). No dialog is opened.
+   * 
+   * @param af
+   * @param treeType
+   * @param modelName
+   * @return null if successful; the string
+   *         "label.you_need_at_least_n_sequences" if number of sequences
+   *         selected is inappropriate
+   */
+  public static Object openTreePanel(AlignFrame af, String treeType,
+          String modelName)
+  {
+    return openTreePanel(af, treeType, modelName, null);
+  }
+
+  /**
+   * public static method for JalviewJS API to open a PCAPanel without
+   * necessarily using a dialog.
+   * 
+   * @param af
+   * @param modelName
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  public static Object openPcaPanel(AlignFrame af, String modelName)
+  {
+    return openPcaPanel(af, modelName, null);
+  }
+
+  /**
    * Constructor
    * 
    * @param af
@@ -232,6 +268,10 @@ public class CalculationChooser extends JPanel
     paramsPanel.add(includeGappedColumns);
     paramsPanel.add(shorterSequence);
 
+    if (VERDANA_11PT == null)
+    {
+      VERDANA_11PT = new Font("Verdana", 0, 11);
+    }
     /*
      * OK / Cancel buttons
      */
@@ -279,7 +319,7 @@ public class CalculationChooser extends JPanel
       title = title + " (" + af.getViewport().getViewName() + ")";
     }
 
-    Desktop.addInternalFrame(frame, title, width, height, false);
+    Desktop.addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, width, height, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     calcChoicePanel.doLayout();
     revalidate();
     /*
@@ -380,7 +420,11 @@ public class CalculationChooser extends JPanel
    */
   protected JComboBox<String> buildModelOptionsList()
   {
-    final JComboBox<String> scoreModelsCombo = new JComboBox<>();
+    JComboBox<String> scoreModelsCombo = new JComboBox<>();
+    if (renderer == null)
+    {
+      renderer = new ComboBoxTooltipRenderer();
+    }
     scoreModelsCombo.setRenderer(renderer);
 
     /*
@@ -500,7 +544,8 @@ public class CalculationChooser extends JPanel
      * for backwards compatibility with Jalview < 2.8 (JAL-2962)
      */
     if (nucleotide && forPca
-            && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
+            && Cache.getDefault(Preferences.BLOSUM62_PCA_FOR_NUCLEOTIDE,
+                    false))
     {
       filtered.add(scoreModels.getBlosum62());
     }
@@ -537,64 +582,122 @@ public class CalculationChooser extends JPanel
    */
   protected void openTreePanel(String modelName, SimilarityParamsI params)
   {
+    Object ret = openTreePanel(af,
+            neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING
+                    : TreeBuilder.AVERAGE_DISTANCE,
+            modelName, params);
+    if (ret instanceof String)
+    {
+      JvOptionPane.showMessageDialog(this, // was opening on Desktop?
+              MessageManager.formatMessage(
+                      (String) ret,
+                      MIN_TREE_SELECTION),
+              MessageManager.getString("label.not_enough_sequences"),
+              JvOptionPane.WARNING_MESSAGE);
+
+    }
+  }
+
+  /**
+   * Open a new PCA panel on the desktop
+   * 
+   * @param modelName
+   * @param params
+   */
+  protected void openPcaPanel(String modelName, SimilarityParamsI params)
+  {
+    Object ret = openPcaPanel(af, modelName, params);
+    if (ret instanceof String)
+    {
+      JvOptionPane.showInternalMessageDialog(this,
+              MessageManager.formatMessage(
+                      (String) ret,
+                      MIN_PCA_SELECTION),
+              MessageManager
+                      .getString("label.sequence_selection_insufficient"),
+              JvOptionPane.WARNING_MESSAGE);
+    }
+    else
+    {
+      // only used for test suite
+      pcaPanel = (PCAPanel) ret;
+    }
+
+  }
+
+  /**
+   * Open a new Tree panel on the desktop statically
+   * 
+   * @param af
+   * @param treeType
+   * @param modelName
+   * @param params
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  public static Object openTreePanel(AlignFrame af, String treeType,
+          String modelName, SimilarityParamsI params)
+  {
+
     /*
      * gui validation shouldn't allow insufficient sequences here, but leave
      * this check in in case this method gets exposed programmatically in future
      */
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
     SequenceGroup sg = viewport.getSelectionGroup();
     if (sg != null && sg.getSize() < MIN_TREE_SELECTION)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.you_need_at_least_n_sequences",
-                      MIN_TREE_SELECTION),
-              MessageManager.getString("label.not_enough_sequences"),
-              JvOptionPane.WARNING_MESSAGE);
-      return;
+      return "label.you_need_at_least_n_sequences";
+    }
+
+    if (params == null)
+    {
+      params = getSimilarityParameters(false);
     }
 
-    String treeType = neighbourJoining.isSelected()
-            ? TreeBuilder.NEIGHBOUR_JOINING
-            : TreeBuilder.AVERAGE_DISTANCE;
     af.newTreePanel(treeType, modelName, params);
+    return null;
   }
 
   /**
-   * Open a new PCA panel on the desktop
+   * public static method for JalviewJS API
    * 
+   * @param af
    * @param modelName
    * @param params
+   * @return the PCAPanel, or null if number of sequences selected is
+   *         inappropriate
    */
-  protected void openPcaPanel(String modelName, SimilarityParamsI params)
+  public static Object openPcaPanel(AlignFrame af, String modelName,
+          SimilarityParamsI params)
   {
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
 
     /*
      * gui validation shouldn't allow insufficient sequences here, but leave
      * this check in in case this method gets exposed programmatically in future
+     * 
+     * 
      */
     if (((viewport.getSelectionGroup() != null)
             && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION)
             && (viewport.getSelectionGroup().getSize() > 0))
             || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
     {
-      JvOptionPane.showInternalMessageDialog(this,
-              MessageManager.formatMessage(
-                      "label.you_need_at_least_n_sequences",
-                      MIN_PCA_SELECTION),
-              MessageManager
-                      .getString("label.sequence_selection_insufficient"),
-              JvOptionPane.WARNING_MESSAGE);
-      return;
+      return "label.you_need_at_least_n_sequences";
+    }
+
+    if (params == null)
+    {
+      params = getSimilarityParameters(true);
     }
 
     /*
      * construct the panel and kick off its calculation thread
      */
-    pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
-    new Thread(pcaPanel).start();
-
+    PCAPanel pcap = new PCAPanel(af.alignPanel, modelName, params);
+    new Thread(pcap).start();
+    return pcap;
   }
 
   /**
@@ -610,6 +713,7 @@ public class CalculationChooser extends JPanel
     }
   }
 
+
   /**
    * Returns a data bean holding parameters for similarity (or distance) model
    * calculation
@@ -617,7 +721,8 @@ public class CalculationChooser extends JPanel
    * @param doPCA
    * @return
    */
-  protected SimilarityParamsI getSimilarityParameters(boolean doPCA)
+  public static SimilarityParamsI getSimilarityParameters(
+          boolean doPCA)
   {
     // commented out: parameter choices read from gui widgets
     // SimilarityParamsI params = new SimilarityParams(
@@ -638,6 +743,7 @@ public class CalculationChooser extends JPanel
 
     return new SimilarityParams(includeGapGap, matchGap, includeGapResidue,
             matchOnShortestLength);
+
   }
 
   /**
index cc6a785..62e7b38 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
@@ -51,7 +52,6 @@ import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-
 /**
  * GUI elements for handling an external chimera display
  * 
@@ -62,6 +62,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 {
   private JalviewChimeraBinding jmb;
 
+
   /*
    * Path to Chimera session file. This is set when an open Jalview/Chimera
    * session is saved, or on restore from a Jalview project (if it holds the
@@ -69,6 +70,7 @@ public class ChimeraViewFrame extends StructureViewerBase
    */
   private String chimeraSessionFile = null;
 
+
   private int myWidth = 500;
 
   private int myHeight = 150;
@@ -237,7 +239,6 @@ public class ChimeraViewFrame extends StructureViewerBase
     return new JalviewChimeraBindingModel(this,
             ap.getStructureSelectionManager(), pdbentrys, seqs, null);
   }
-
   /**
    * Create a new viewer from saved session state data including Chimera session
    * file
@@ -328,7 +329,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     if (!jmb.launchChimera())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage("label.open_viewer_failed",
                       getViewerName()),
               MessageManager.getString("label.error_loading_file"),
@@ -426,7 +427,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     if (errormsgs.length() > 0)
     {
 
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.pdb_entries_couldnt_be_retrieved", new Object[]
                       { errormsgs.toString() }),
@@ -543,6 +544,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     worker = null;
   }
 
+
   @Override
   public void makePDBImage(TYPE imageType)
   {
@@ -556,6 +558,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     return jmb;
   }
 
+
   @Override
   public ViewerType getViewerType()
   {
index c1c75f1..3447f66 100644 (file)
@@ -158,7 +158,7 @@ public class ColourMenuHelper
             ActionListener al = radioItem.getActionListeners()[0];
             radioItem.removeActionListener(al);
             int option = JvOptionPane.showInternalConfirmDialog(
-                    Desktop.desktop,
+                    Desktop.getDesktopPane(),
                     MessageManager
                             .getString("label.remove_from_default_list"),
                     MessageManager
@@ -314,7 +314,7 @@ public class ColourMenuHelper
     }
     else
     {
-      Cache.applicationProperties.remove("USER_DEFINED_COLOURS");
+      Cache.removePropertyNoSave("USER_DEFINED_COLOURS");
     }
   }
 }
index c15cf2d..d518d6b 100644 (file)
@@ -456,7 +456,7 @@ public class CrossRefAction implements Runnable
     copyAlignment.setGapCharacter(alignFrame.viewport.getGapCharacter());
 
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
 
     /*
      * register any new mappings for sequence mouseover etc
index 112d502..2b7110c 100644 (file)
@@ -235,7 +235,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
               .println(MessageManager.getString("label.couldnt_read_data"));
       if (!Jalview.isHeadlessMode())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
@@ -254,7 +254,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
     } catch (IOException ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), MessageManager
               .formatMessage("label.couldnt_read_pasted_text", new String[]
               { ex.toString() }),
               MessageManager.getString("label.error_parsing_text"),
@@ -339,7 +339,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
               .println(MessageManager.getString("label.couldnt_read_data"));
       if (!Jalview.isHeadlessMode())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
index 16603df..b125cc2 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -62,10 +63,12 @@ import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Locale;
 import java.util.Vector;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.Semaphore;
 
 import javax.swing.AbstractAction;
@@ -99,6 +102,9 @@ import org.stackoverflowusers.file.WindowsShortcut;
 
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.gui.ImageExporter.ImageWriterI;
@@ -113,6 +119,8 @@ import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.jbgui.APQHandlers;
+import jalview.jbgui.GDesktop;
 import jalview.jbgui.GSplitFrame;
 import jalview.jbgui.GStructureViewer;
 import jalview.project.Jalview2XML;
@@ -124,9 +132,9 @@ import jalview.util.ImageMaker.TYPE;
 import jalview.util.LaunchUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.util.ShortcutKeyMaskExWrapper;
 import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.params.ParamManager;
 import jalview.ws.utils.UrlDownloadClient;
 
@@ -137,17 +145,16 @@ import jalview.ws.utils.UrlDownloadClient;
  * @author $author$
  * @version $Revision: 1.155 $
  */
-public class Desktop extends jalview.jbgui.GDesktop
+@SuppressWarnings("serial")
+public class Desktop extends GDesktop
         implements DropTargetListener, ClipboardOwner, IProgressIndicator,
-        jalview.api.StructureSelectionManagerProvider
+        StructureSelectionManagerProvider, ApplicationSingletonI
+
 {
   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(
@@ -156,12 +163,9 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       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 <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and join <a href=\"https://discourse.jalview.org\">discourse.jalview.org</a>");
     sb.append("<br><br>If  you use Jalview, please cite:"
@@ -187,6 +191,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
 
+  @SuppressWarnings("deprecation")
   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
 
   public static boolean nosplash = false;
@@ -202,6 +207,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @param listener
    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
    */
+  @Deprecated
   public void addJalviewPropertyChangeListener(
           PropertyChangeListener listener)
   {
@@ -214,6 +220,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
    *      java.beans.PropertyChangeListener)
    */
+  @Deprecated
   public void addJalviewPropertyChangeListener(String propertyName,
           PropertyChangeListener listener)
   {
@@ -226,6 +233,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
    *      java.beans.PropertyChangeListener)
    */
+  @Deprecated
   public void removeJalviewPropertyChangeListener(String propertyName,
           PropertyChangeListener listener)
   {
@@ -233,31 +241,47 @@ public class Desktop extends jalview.jbgui.GDesktop
             listener);
   }
 
-  /** Singleton Desktop instance */
-  public static Desktop instance;
+  private MyDesktopPane desktopPane;
+
+  public static MyDesktopPane getDesktopPane()
+  {
+    Desktop desktop = getInstance();
+    return desktop == null ? null : desktop.desktopPane;
+  }
 
-  public static MyDesktopPane desktop;
+  /**
+   * Answers an 'application scope' singleton instance of this class. Separate
+   * SwingJS 'applets' running in the same browser page will each have a
+   * distinct instance of Desktop.
+   * 
+   * @return
+   */
+  public static Desktop getInstance()
+  {
+    return Jalview.isHeadlessMode() ? null
+            : (Desktop) ApplicationSingletonProvider
+                    .getInstance(Desktop.class);
+  }
 
-  public static MyDesktopPane getDesktop()
+  public static StructureSelectionManager getStructureSelectionManager()
   {
-    // BH 2018 could use currentThread() here as a reference to a
-    // Hashtable<Thread, MyDesktopPane> in JavaScript
-    return desktop;
+    return StructureSelectionManager
+            .getStructureSelectionManager(getInstance());
   }
 
-  static int openFrameCount = 0;
+  int openFrameCount = 0;
 
-  static final int xOffset = 30;
+  final int xOffset = 30;
 
-  static final int yOffset = 30;
+  final int yOffset = 30;
 
-  public static jalview.ws.jws1.Discoverer discoverer;
+  public jalview.ws.jws1.Discoverer discoverer;
 
-  public static Object[] jalviewClipboard;
+  public Object[] jalviewClipboard;
 
-  public static boolean internalCopy = false;
+  public boolean internalCopy = false;
 
-  static int fileLoadingCount = 0;
+  int fileLoadingCount = 0;
 
   class MyDesktopManager implements DesktopManager
   {
@@ -278,7 +302,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       } catch (NullPointerException npe)
       {
         Point p = getMousePosition();
-        instance.showPasteMenu(p.x, p.y);
+        showPasteMenu(p.x, p.y);
       }
     }
 
@@ -326,14 +350,14 @@ public class Desktop extends jalview.jbgui.GDesktop
     public void endDraggingFrame(JComponent f)
     {
       delegate.endDraggingFrame(f);
-      desktop.repaint();
+      desktopPane.repaint();
     }
 
     @Override
     public void endResizingFrame(JComponent f)
     {
       delegate.endResizingFrame(f);
-      desktop.repaint();
+      desktopPane.repaint();
     }
 
     @Override
@@ -383,33 +407,33 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * Creates a new Desktop object.
+   * Private constructor enforces singleton pattern. It is called by reflection
+   * from ApplicationSingletonProvider.getInstance().
    */
-  public Desktop()
+  private 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.
-     */
-    instance = this;
+    try
+    {
+      /**
+       * 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.
+       */
 
-    doConfigureStructurePrefs();
-    setTitle(ChannelProperties.getProperty("app_name") + " "
-            + Cache.getProperty("VERSION"));
+      doConfigureStructurePrefs();
+    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
+     * 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())
@@ -425,16 +449,12 @@ public class Desktop extends jalview.jbgui.GDesktop
         Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
         Field awtAppClassNameField = null;
 
-        if (Arrays.stream(declaredFields)
-                .anyMatch(f -> f.getName().equals("awtAppClassName")))
-        {
-          awtAppClassNameField = xToolkit.getClass()
-                  .getDeclaredField("awtAppClassName");
+        if (Arrays.stream(declaredFields).anyMatch(f -> f.getName().equals("awtAppClassName"))) {
+          awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
         }
 
         String title = ChannelProperties.getProperty("app_name");
-        if (awtAppClassNameField != null)
-        {
+        if (awtAppClassNameField != null) {
           awtAppClassNameField.setAccessible(true);
           awtAppClassNameField.set(xToolkit, title);
         }
@@ -449,159 +469,180 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     }
 
+    /**
+     * 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 (Exception e) {
+      System.out.println("Cannot set APQHandlers");
+      // e.printStackTrace();
+    } catch (Throwable t) {
+      jalview.bin.Console.warn("Error setting APQHandlers: " + t.toString());
+      jalview.bin.Console.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();
       }
     });
 
-    boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
+      boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
 
-    boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
-    desktop = new MyDesktopPane(selmemusage);
+      boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
+      desktopPane = new MyDesktopPane(selmemusage);
 
-    showMemusage.setSelected(selmemusage);
-    desktop.setBackground(Color.white);
+      showMemusage.setSelected(selmemusage);
+      desktopPane.setBackground(Color.white);
 
-    getContentPane().setLayout(new BorderLayout());
-    // alternate config - have scrollbars - see notes in JAL-153
-    // JScrollPane sp = new JScrollPane();
-    // sp.getViewport().setView(desktop);
-    // getContentPane().add(sp, BorderLayout.CENTER);
+      getContentPane().setLayout(new BorderLayout());
+      // alternate config - have scrollbars - see notes in JAL-153
+      // JScrollPane sp = new JScrollPane();
+      // sp.getViewport().setView(desktop);
+      // getContentPane().add(sp, BorderLayout.CENTER);
 
-    // BH 2018 - just an experiment to try unclipped JInternalFrames.
-    if (Platform.isJS())
-    {
-      getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
-    }
+      // BH 2018 - just an experiment to try unclipped JInternalFrames.
+      if (Platform.isJS())
+      {
+        getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
+      }
 
-    getContentPane().add(desktop, BorderLayout.CENTER);
-    desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
+      getContentPane().add(desktopPane, BorderLayout.CENTER);
+      desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
 
-    // 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())));
 
-    Rectangle dims = getLastKnownDimensions("");
-    if (dims != null)
-    {
-      setBounds(dims);
-    }
-    else
-    {
-      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-      int xPos = Math.max(5, (screenSize.width - 900) / 2);
-      int yPos = Math.max(5, (screenSize.height - 650) / 2);
-      setBounds(xPos, yPos, 900, 650);
-    }
+      // This line prevents Windows Look&Feel resizing all new windows to
+      // maximum
+      // if previous window was maximised
+      desktopPane.setDesktopManager(new MyDesktopManager(
+              (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
+                      : Platform.isAMacAndNotJS()
+                              ? new AquaInternalFrameManager(
+                                      desktopPane.getDesktopManager())
+                              : desktopPane.getDesktopManager())));
 
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      jconsole = new Console(this, showjconsole);
-      jconsole.setHeader(Cache.getVersionDetailsForConsole());
-      showConsole(showjconsole);
+      Rectangle dims = getLastKnownDimensions("");
+      if (dims != null)
+      {
+        setBounds(dims);
+      }
+      else
+      {
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        int xPos = Math.max(5, (screenSize.width - 900) / 2);
+        int yPos = Math.max(5, (screenSize.height - 650) / 2);
+        setBounds(xPos, yPos, 900, 650);
+      }
 
-      showNews.setVisible(false);
+      if (!Platform.isJS())
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+        jconsole = new Console(this, showjconsole);
+        jconsole.setHeader(Cache.getVersionDetailsForConsole());
+        showConsole(showjconsole);
 
-      experimentalFeatures.setSelected(showExperimental());
+        showNews.setVisible(false); // not sure if we should only do this for interactive session?
 
-      getIdentifiersOrgData();
+        experimentalFeatures.setSelected(showExperimental());
 
-      checkURLLinks();
+        getIdentifiersOrgData();
 
-      // Spawn a thread that shows the splashscreen
-      if (!nosplash)
-      {
-        SwingUtilities.invokeLater(new Runnable()
+        if (Jalview.isInteractive())
         {
-          @Override
-          public void run()
-          {
-            new SplashScreen(true);
+          // disabled for SeqCanvasTest
+          checkURLLinks();
+
+          // Spawn a thread that shows the splashscreen
+          if (!nosplash) {
+          SwingUtilities.invokeLater(new Runnable()
+           {
+             @Override
+             public void run()
+             {
+               new SplashScreen(true);
+             }
+           });
           }
-        });
+
+          // Thread off a new instance of the file chooser - this reduces the
+          // time
+          // it
+          // takes to open it later on.
+          new Thread(new Runnable()
+          {
+            @Override
+            public void run()
+            {
+              jalview.bin.Console.debug("Filechooser init thread started.");
+              String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
+              JalviewFileChooser.forRead(
+                      Cache.getProperty("LAST_DIRECTORY"), fileFormat);
+              jalview.bin.Console.debug("Filechooser init thread finished.");
+            }
+          }).start();
+          // Add the service change listener
+          changeSupport.addJalviewPropertyChangeListener("services",
+                  new PropertyChangeListener()
+                  {
+
+                    @Override
+                    public void propertyChange(PropertyChangeEvent evt)
+                    {
+                      jalview.bin.Console.debug("Firing service changed event for "
+                              + evt.getNewValue());
+                      JalviewServicesChanged(evt);
+                    }
+                  });
+        }
       }
 
-      // Thread off a new instance of the file chooser - this reduces the time
-      // it
-      // takes to open it later on.
-      new Thread(new Runnable()
+      this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
+
+      this.addWindowListener(new WindowAdapter()
       {
         @Override
-        public void run()
+        public void windowClosing(WindowEvent evt)
         {
-          jalview.bin.Console.debug("Filechooser init thread started.");
-          String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
-          JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
-                  fileFormat);
-          jalview.bin.Console.debug("Filechooser init thread finished.");
+          quit();
         }
-      }).start();
-      // Add the service change listener
-      changeSupport.addJalviewPropertyChangeListener("services",
-              new PropertyChangeListener()
-              {
-
-                @Override
-                public void propertyChange(PropertyChangeEvent evt)
-                {
-                  jalview.bin.Console
-                          .debug("Firing service changed event for "
-                                  + evt.getNewValue());
-                  JalviewServicesChanged(evt);
-                }
-              });
-    }
-
-    this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
-
-    this.addWindowListener(new WindowAdapter()
-    {
-      @Override
-      public void windowClosing(WindowEvent evt)
-      {
-        quit();
-      }
-    });
+      });
 
-    MouseAdapter ma;
-    this.addMouseListener(ma = new MouseAdapter()
-    {
-      @Override
-      public void mousePressed(MouseEvent evt)
+      MouseAdapter ma;
+      this.addMouseListener(ma = new MouseAdapter()
       {
-        if (evt.isPopupTrigger()) // Mac
+        @Override
+        public void mousePressed(MouseEvent evt)
         {
-          showPasteMenu(evt.getX(), evt.getY());
+          if (evt.isPopupTrigger()) // Mac
+          {
+            showPasteMenu(evt.getX(), evt.getY());
+          }
         }
-      }
-
-      @Override
-      public void mouseReleased(MouseEvent evt)
-      {
-        if (evt.isPopupTrigger()) // Windows
+        @Override
+        public void mouseReleased(MouseEvent evt)
         {
-          showPasteMenu(evt.getX(), evt.getY());
+          if (evt.isPopupTrigger()) // Windows
+          {
+            showPasteMenu(evt.getX(), evt.getY());
+          }
         }
-      }
-    });
-    desktop.addMouseListener(ma);
+      });
+      desktopPane.addMouseListener(ma);
+    } catch (Throwable t)
+    {
+      t.printStackTrace();
+    }
   }
 
   /**
@@ -620,32 +661,23 @@ public class Desktop extends jalview.jbgui.GDesktop
   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));
+    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));
       // JAL-3915 - RNAView is no longer an option so this has no effect
-      ssm.setSecStructServices(
-              Cache.getDefault(Preferences.USE_RNAVIEW, false));
-    }
-    else
-    {
+      ssm.setSecStructServices(Cache.getDefault(Preferences.USE_RNAVIEW, false));
+    } 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()
       {
@@ -657,36 +689,30 @@ public class Desktop extends jalview.jbgui.GDesktop
     }).start();
   }
 
-  public void getIdentifiersOrgData()
-  {
-    if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
-    {// Thread off the identifiers fetcher
-      new Thread(new Runnable()
-      {
+  public void getIdentifiersOrgData() {
+    if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null) {
+      // Thread off the identifiers fetcher
+      new Thread(new Runnable() {
         @Override
         public void run()
         {
-          jalview.bin.Console
-                  .debug("Downloading data from identifiers.org");
+          jalview.bin.Console.debug("Downloading data from identifiers.org");
           try
           {
             UrlDownloadClient.download(IdOrgSettings.getUrl(),
                     IdOrgSettings.getDownloadLocation());
           } catch (IOException e)
           {
-            jalview.bin.Console
-                    .debug("Exception downloading identifiers.org data"
+            jalview.bin.Console.debug("Exception downloading identifiers.org data"
                             + e.getMessage());
           }
         }
       }).start();
-      ;
     }
   }
 
   @Override
-  protected void showNews_actionPerformed(ActionEvent e)
-  {
+  protected void showNews_actionPerformed(ActionEvent e) {
     showNews(showNews.isSelected());
   }
 
@@ -702,10 +728,10 @@ public class Desktop extends jalview.jbgui.GDesktop
         public void run()
         {
           long now = System.currentTimeMillis();
-          Desktop.instance.setProgressBar(
-                  MessageManager.getString("status.refreshing_news"), now);
+          setProgressBar(MessageManager.getString("status.refreshing_news"),
+                  now);
           jvnews.refreshNews();
-          Desktop.instance.setProgressBar(null, now);
+          setProgressBar(null, now);
           jvnews.showNews();
         }
       }).start();
@@ -728,19 +754,15 @@ public class Desktop extends jalview.jbgui.GDesktop
     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);
@@ -811,48 +833,11 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
   }
 
-  /**
-   * 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)
-  {
-    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)
-  {
-    addInternalFrame(frame, title, makeVisible, w, h, true, false);
-  }
 
   /**
-   * Add an internal frame to the Jalview desktop and make it visible
+   * Adds and opens the given frame to the desktop that is visible, allowed to
+   * resize, and has a 300px minimum width.
    * 
    * @param frame
    *          Frame to show
@@ -862,18 +847,19 @@ public class Desktop extends jalview.jbgui.GDesktop
    *          width
    * @param h
    *          height
-   * @param resizable
-   *          Allow resize
    */
   public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, int w, int h,
-          boolean resizable)
+          final JInternalFrame frame, String title, int w, int h)
   {
-    addInternalFrame(frame, title, true, w, h, resizable, false);
+    // 58 classes
+    
+    addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h, 
+            FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
   }
 
   /**
-   * Add an internal frame to the Jalview desktop
+   * Add an internal frame to the Jalview desktop that may optionally be
+   * visible, resizable, and allowed to be any size
    * 
    * @param frame
    *          Frame to show
@@ -895,6 +881,8 @@ public class Desktop extends jalview.jbgui.GDesktop
           final JInternalFrame frame, String title, boolean makeVisible,
           int w, int h, boolean resizable, boolean ignoreMinSize)
   {
+    // 15 classes call this method directly.
+    
 
     // TODO: allow callers to determine X and Y position of frame (eg. via
     // bounds object).
@@ -906,22 +894,40 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       frame.setSize(w, h);
     }
-    // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
-    // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
-    // IF JALVIEW IS RUNNING HEADLESS
-    // ///////////////////////////////////////////////
-    if (instance == null || (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true")))
-    {
-      return;
-    }
+    if (getInstance() != null)
+      getInstance().addFrame(frame, makeVisible, resizable,
+            ignoreMinSize);
+  }
+
+  // These can now by put into a single int flag, if desired:
+  
+  public final static boolean FRAME_MAKE_VISIBLE = true;
+
+  public final static boolean FRAME_NOT_VISIBLE = false;
+
+  public final static boolean FRAME_ALLOW_RESIZE = true;
+
+  public final static boolean FRAME_NOT_RESIZABLE = false;
+
+  public final static boolean FRAME_ALLOW_ANY_SIZE = true;
+
+  public final static boolean FRAME_SET_MIN_SIZE_300 = false;
+  
+  private void addFrame(JInternalFrame frame,
+          boolean makeVisible, boolean resizable,
+          boolean ignoreMinSize)
+  {
 
     openFrameCount++;
 
+    
+    boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
+    boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
+    // Web page embedding allows us to ignore minimum size
+    ignoreMinSize |= hasEmbeddedSize;
+    
     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,
@@ -931,6 +937,10 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
                 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
+      } else {
+        frame.setMinimumSize(
+                new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
+        
       }
     }
 
@@ -941,23 +951,23 @@ public class Desktop extends jalview.jbgui.GDesktop
     frame.setIconifiable(resizable);
     frame.setOpaque(Platform.isJS());
 
-    if (frame.getX() < 1 && frame.getY() < 1)
+    if (!isEmbedded && 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);
+    final JMenuItem menuItem = new JMenuItem(frame.getTitle());
     frame.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
       public void internalFrameActivated(InternalFrameEvent evt)
       {
-        JInternalFrame itf = desktop.getSelectedFrame();
+        JInternalFrame itf = getDesktopPane().getSelectedFrame();
         if (itf != null)
         {
           if (itf instanceof AlignFrame)
@@ -974,7 +984,8 @@ public class Desktop extends jalview.jbgui.GDesktop
         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)
         {
@@ -988,7 +999,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         {
           menuItem.removeActionListener(menuItem.getActionListeners()[0]);
         }
-        windowMenu.remove(menuItem);
+        getInstance().windowMenu.remove(menuItem);
       }
     });
 
@@ -1003,6 +1014,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           frame.setIcon(false);
         } catch (java.beans.PropertyVetoException ex)
         {
+          // System.err.println(ex.toString());
 
         }
       }
@@ -1010,9 +1022,9 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     setKeyBindings(frame);
 
-    desktop.add(frame);
+    getDesktopPane().add(frame);
 
-    windowMenu.add(menuItem);
+    getInstance().windowMenu.add(menuItem);
 
     frame.toFront();
     try
@@ -1050,10 +1062,8 @@ 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, Platform.SHORTCUT_KEY_MASK);
 
     InputMap inputMap = frame
             .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
@@ -1070,7 +1080,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (!internalCopy)
     {
-      Desktop.jalviewClipboard = null;
+      jalviewClipboard = null;
     }
 
     internalCopy = false;
@@ -1115,7 +1125,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     try
     {
-      Desktop.transferFromDropTarget(files, protocols, evt, t);
+      transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -1192,8 +1202,8 @@ 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))
         {
@@ -1231,8 +1241,9 @@ public class Desktop extends jalview.jbgui.GDesktop
     panel.add(label);
 
     /*
-     * the URL to fetch is input in Java: an editable combobox with history JS:
-     * (pending JAL-3038) a plain text field
+     * the URL to fetch is
+     * Java: an editable combobox with history
+     * JS: (pending JAL-3038) a plain text field
      */
     JComponent history;
     String urlBase = "https://www.";
@@ -1271,26 +1282,16 @@ public class Desktop extends jalview.jbgui.GDesktop
       public void run()
       {
         @SuppressWarnings("unchecked")
-        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);
+        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
-        {
+        } else {
           FileFormatI format = null;
           try
           {
@@ -1305,7 +1306,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           {
             String msg = MessageManager
                     .formatMessage("label.couldnt_locate", url);
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
+            JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
                     MessageManager.getString("label.url_not_found"),
                     JvOptionPane.WARNING_MESSAGE);
 
@@ -1326,7 +1327,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     };
     String dialogOption = MessageManager
             .getString("label.input_alignment_from_url");
-    JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
+    JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
             .showInternalDialog(panel, dialogOption,
                     JvOptionPane.YES_NO_CANCEL_OPTION,
                     JvOptionPane.PLAIN_MESSAGE, null, options,
@@ -1346,9 +1347,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(viewPanel);
-    Desktop.addInternalFrame(cap,
-            MessageManager.getString("label.cut_paste_alignmen_file"), true,
-            600, 500);
+    addInternalFrame(cap,
+            MessageManager.getString("label.cut_paste_alignmen_file"),
+            FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
+            FRAME_SET_MIN_SIZE_300);
   }
 
   /*
@@ -1429,40 +1431,28 @@ public class Desktop extends jalview.jbgui.GDesktop
   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(Locale.ROOT)
-              .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>");
       }
     }
@@ -1479,15 +1469,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
        * 
@@ -1496,8 +1482,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         Help.showHelpWindow();
       }
-    } catch (Exception ex)
-    {
+    } catch (Exception ex) {
       System.err.println("Error opening help: " + ex.getMessage());
     }
   }
@@ -1506,7 +1491,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void closeAll_actionPerformed(ActionEvent e)
   {
     // TODO show a progress bar while closing?
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     for (int i = 0; i < frames.length; i++)
     {
       try
@@ -1566,13 +1551,14 @@ 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)
   {
-    desktop.showMemoryUsage(showMemusage.isSelected());
+    desktopPane.showMemoryUsage(showMemusage.isSelected());
   }
 
   /*
@@ -1609,13 +1595,14 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   void reorderAssociatedWindows(boolean minimize, boolean close)
   {
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     if (frames == null || frames.length < 1)
     {
       return;
     }
 
-    AlignmentViewport source = null, target = null;
+    AlignViewportI source = null;
+    AlignViewportI target = null;
     if (frames[0] instanceof AlignFrame)
     {
       source = ((AlignFrame) frames[0]).getCurrentView();
@@ -1691,8 +1678,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    *          DOCUMENT ME!
    */
   @Override
-  protected void preferences_actionPerformed(ActionEvent e)
-  {
+  protected void preferences_actionPerformed(ActionEvent e) {
     Preferences.openPreferences();
   }
 
@@ -1736,8 +1722,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     }
 
-    if (approveSave || autoSave)
-    {
+    if (approveSave || autoSave) {
       final Desktop me = this;
       final java.io.File chosenFile = projectFile;
       new Thread(new Runnable()
@@ -1846,7 +1831,7 @@ public class Desktop extends jalview.jbgui.GDesktop
             {
               jalview.bin.Console.error(
                       "Problems whilst loading project from " + choice, ex);
-              JvOptionPane.showMessageDialog(Desktop.desktop,
+              JvOptionPane.showMessageDialog(getDesktopPane(),
                       MessageManager.formatMessage(
                               "label.error_whilst_loading_project_from",
                               new Object[]
@@ -1890,7 +1875,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       progressPanel = new JPanel(new GridLayout(1, 1));
       totalProgressCount = 0;
-      instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
+      getContentPane().add(progressPanel, BorderLayout.SOUTH);
     }
     JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
     JProgressBar progressBar = new JProgressBar();
@@ -1903,7 +1888,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     ((GridLayout) progressPanel.getLayout()).setRows(
             ((GridLayout) progressPanel.getLayout()).getRows() + 1);
     ++totalProgressCount;
-    instance.validate();
+    validate();
     return thisprogress;
   }
 
@@ -1957,7 +1942,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
   {
-    if (Desktop.desktop == null)
+    if (getDesktopPane() == null)
     {
       // no frames created and in headless mode
       // TODO: verify that frames are recoverable when in headless mode
@@ -1999,9 +1984,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   public static AlignmentViewport[] getViewports(String sequenceSetId)
   {
     List<AlignmentViewport> viewp = new ArrayList<>();
-    if (desktop != null)
+    if (getDesktopPane() != null)
     {
-      AlignFrame[] frames = Desktop.getAlignFrames();
+      AlignFrame[] frames = getAlignFrames();
 
       for (AlignFrame afr : frames)
       {
@@ -2067,8 +2052,9 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       /*
-       * 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)
@@ -2109,7 +2095,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     source.viewport.setGatherViewsHere(true);
     source.viewport.setExplodedGeometry(source.getBounds());
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     String viewId = source.viewport.getSequenceSetId();
     for (int t = 0; t < frames.length; t++)
     {
@@ -2162,7 +2148,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public JInternalFrame[] getAllFrames()
   {
-    return desktop.getAllFrames();
+    return desktopPane.getAllFrames();
   }
 
   /**
@@ -2248,7 +2234,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           });
           msgPanel.add(jcb);
 
-          JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
+          JvOptionPane.showMessageDialog(desktopPane, msgPanel,
                   MessageManager
                           .getString("label.SEQUENCE_ID_no_longer_used"),
                   JvOptionPane.WARNING_MESSAGE);
@@ -2353,7 +2339,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       // output debug scale message. Important for jalview.bin.HiDPISettingTest2
-      Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
+      Desktop.debugScaleMessage(Desktop.getDesktopPane().getGraphics());
     }
   }
 
@@ -2366,11 +2352,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (Jalview.isHeadlessMode())
     {
-      // Desktop.desktop is null in headless mode
-      return new AlignFrame[] { Jalview.currentAlignFrame };
+      return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
     }
 
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = getDesktopPane().getAllFrames();
 
     if (frames == null)
     {
@@ -2415,7 +2400,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public GStructureViewer[] getJmols()
   {
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
 
     if (frames == null)
     {
@@ -2451,7 +2436,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     } catch (Exception ex)
     {
       jalview.bin.Console.error("Groovy Shell Creation failed.", ex);
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(desktopPane,
 
               MessageManager.getString("label.couldnt_create_groovy_shell"),
               MessageManager.getString("label.groovy_support_failed"),
@@ -2472,9 +2457,9 @@ public class Desktop extends jalview.jbgui.GDesktop
 
       /*
        * 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()
@@ -2497,8 +2482,8 @@ 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);
   }
@@ -2509,12 +2494,10 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   protected void addQuitHandler()
   {
-    getRootPane()
-            .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                    KeyStroke
-                            .getKeyStroke(KeyEvent.VK_Q,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx()),
+
+    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+            .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
+                    Platform.SHORTCUT_KEY_MASK),
                     "Quit");
     getRootPane().getActionMap().put("Quit", new AbstractAction()
     {
@@ -2535,8 +2518,8 @@ public class Desktop extends jalview.jbgui.GDesktop
   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);
 
@@ -2565,6 +2548,8 @@ public class Desktop extends jalview.jbgui.GDesktop
   @Override
   public void setProgressBar(String message, long id)
   {
+    // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
+
     if (progressBars == null)
     {
       progressBars = new Hashtable<>();
@@ -2585,6 +2570,13 @@ public class Desktop extends jalview.jbgui.GDesktop
       progressBars.put(Long.valueOf(id), addProgressPanel(message));
     }
   }
+  
+  @Override
+  public void removeProgressBar(long id)
+  {
+    //TODO
+    throw new UnsupportedOperationException("not implemented");
+  }
 
   /*
    * (non-Javadoc)
@@ -2650,7 +2642,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
   {
-    if (desktop != null)
+    if (getDesktopPane() != null)
     {
       AlignmentPanel[] aps = getAlignmentPanels(
               viewport.getSequenceSetId());
@@ -2712,61 +2704,48 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public void startServiceDiscovery(boolean blocking)
   {
-    startServiceDiscovery(blocking, false);
-  }
+    jalview.bin.Console.debug("Starting service discovery");
 
-  /**
-   * 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;
+    var tasks = new ArrayList<Future<?>>();
     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
-    if (true)
+
+    System.out.println("loading services");
+    
+    /** @j2sIgnore */
     {
       // todo: changesupport handlers need to be transferred
       if (discoverer == null)
       {
-        discoverer = new jalview.ws.jws1.Discoverer();
+        discoverer = jalview.ws.jws1.Discoverer.getInstance();
         // register PCS handler for desktop.
         discoverer.addPropertyChangeListener(changeSupport);
       }
       // JAL-940 - disabled JWS1 service configuration - always start discoverer
       // until we phase out completely
-      (t0 = new Thread(discoverer)).start();
+      var f = new FutureTask<Void>(discoverer, null);
+      new Thread(f).start();
+      tasks.add(f);
     }
 
-    if (ignore_SHOW_JWS2_SERVICES_preference
-            || Cache.getDefault("SHOW_JWS2_SERVICES", true))
+    if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
     {
-      t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .startDiscoverer(changeSupport);
+      tasks.add(jalview.ws.jws2.Jws2Discoverer.getInstance().startDiscoverer());
     }
-    Thread t3 = null;
+    if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
     {
-      // TODO: do rest service discovery
+      tasks.add(jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance().startDiscoverer());
     }
     if (blocking)
     {
-      while (alive)
-      {
+      for (Future<?> task : tasks) {
         try
         {
-          Thread.sleep(15);
+          // block until all discovery tasks are done
+          task.get();
         } catch (Exception e)
         {
+          e.printStackTrace();
         }
-        alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
-                || (t3 != null && t3.isAlive())
-                || (t0 != null && t0.isAlive());
       }
     }
   }
@@ -2780,8 +2759,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
     {
-      final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .getErrorMessages();
+      final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
+          .getInstance();
+      final String ermsg = discoverer.getErrorMessages();
+      // CONFLICT:ALT:?     final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
       if (ermsg != null)
       {
         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
@@ -2798,23 +2779,28 @@ public class Desktop extends jalview.jbgui.GDesktop
                 /*
                  * 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,
+                JvOptionPane.showConfirmDialog(desktopPane,
                         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,"
@@ -2851,7 +2837,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static void showUrl(final String url)
   {
-    showUrl(url, Desktop.instance);
+    showUrl(url, getInstance());
   }
 
   /**
@@ -2880,7 +2866,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           jalview.util.BrowserLauncher.openURL(url);
         } catch (Exception ex)
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+          JvOptionPane.showInternalMessageDialog(getDesktopPane(),
                   MessageManager
                           .getString("label.web_browser_not_found_unix"),
                   MessageManager.getString("label.web_browser_not_found"),
@@ -2920,13 +2906,13 @@ public class Desktop extends jalview.jbgui.GDesktop
       try
       {
         url = e.getURL().toString();
-        Desktop.showUrl(url);
+        showUrl(url);
       } catch (Exception x)
       {
         if (url != null)
         {
-          jalview.bin.Console
-                  .error("Couldn't handle string " + url + " as a URL.");
+          // TODO does error send to stderr if no log exists ?
+          jalview.bin.Console.error("Couldn't handle string " + url + " as a URL.");
         }
         // ignore any exceptions due to dud links.
       }
@@ -2972,7 +2958,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           {
           }
         }
-        if (instance == null)
+        if (Jalview.isHeadlessMode())
         {
           return;
         }
@@ -2981,8 +2967,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           SwingUtilities.invokeAndWait(prompter);
         } catch (Exception q)
         {
-          jalview.bin.Console.warn("Unexpected Exception in dialog thread.",
-                  q);
+          jalview.bin.Console.warn("Unexpected Exception in dialog thread.", q);
         }
       }
     });
@@ -3055,15 +3040,16 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
 
     /*
-     * 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--)
     {
       /*
-       * 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
@@ -3098,12 +3084,12 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         splitFrame.setLocation(geometry.getLocation());
       }
-      Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
+      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();
@@ -3136,7 +3122,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     String topViewId = myTopFrame.viewport.getSequenceSetId();
     String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
 
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     for (JInternalFrame frame : frames)
     {
       if (frame instanceof SplitFrame && frame != source)
@@ -3199,11 +3185,34 @@ public class Desktop extends jalview.jbgui.GDesktop
    *          - the payload from the drop event
    * @throws Exception
    */
+  @SuppressWarnings("unchecked")
   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
@@ -3249,7 +3258,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       // Works on Windows and MacOSX
       jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
-      for (Object file : (List) t
+      for (File file : (List<File>) t
               .getTransferData(DataFlavor.javaFileListFlavor))
       {
         files.add(file);
@@ -3345,8 +3354,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           }
           else
           {
-            jalview.bin.Console
-                    .debug("Couldn't resolve dataflavor for drop: "
+            jalview.bin.Console.debug("Couldn't resolve dataflavor for drop: "
                             + t.toString());
           }
         }
@@ -3354,8 +3362,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
     if (Platform.isWindowsAndNotJS())
     {
-      jalview.bin.Console
-              .debug("Scanning dropped content for Windows Link Files");
+      jalview.bin.Console.debug("Scanning dropped content for Windows Link Files");
 
       // resolve any .lnk files in the file drop
       for (int f = 0; f < files.size(); f++)
@@ -3416,7 +3423,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           Class<? extends StructureViewerBase> structureViewerClass)
   {
     List<StructureViewerBase> result = new ArrayList<>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
+    JInternalFrame[] frames = getAllFrames();
 
     for (JInternalFrame frame : frames)
     {
@@ -3440,18 +3447,14 @@ 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();
index 844eee4..b53e57c 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
+import jalview.io.FeaturesFile;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+import jalview.util.MessageManager;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -48,17 +59,6 @@ import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
-import jalview.api.FeatureColourI;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.gui.JalviewColourChooser.ColourChooserListener;
-import jalview.io.FeaturesFile;
-import jalview.schemes.FeatureColour;
-import jalview.util.ColorUtils;
-import jalview.util.MessageManager;
-
 /**
  * Provides a dialog allowing the user to add new features, or amend or delete
  * existing features
@@ -190,30 +190,30 @@ public class FeatureEditor
     });
 
     description = new JTextArea(3, 25);
-
+    
     start = new JSpinner();
     end = new JSpinner();
     start.setPreferredSize(new Dimension(80, 20));
     end.setPreferredSize(new Dimension(80, 20));
-
+    
     /*
      * ensure that start can never be more than end
      */
-    start.addChangeListener(new ChangeListener()
+    start.addChangeListener(new ChangeListener() 
     {
       @Override
       public void stateChanged(ChangeEvent e)
       {
-        Integer startVal = (Integer) start.getValue();
+        Integer startVal = (Integer) start.getValue(); 
         ((SpinnerNumberModel) end.getModel()).setMinimum(startVal);
       }
     });
-    end.addChangeListener(new ChangeListener()
+    end.addChangeListener(new ChangeListener() 
     {
       @Override
       public void stateChanged(ChangeEvent e)
       {
-        Integer endVal = (Integer) end.getValue();
+        Integer endVal = (Integer) end.getValue(); 
         ((SpinnerNumberModel) start.getModel()).setMaximum(endVal);
       }
     });
@@ -242,7 +242,7 @@ public class FeatureEditor
               updateColourButton(mainPanel, colour, featureColour);
             };
           };
-          JalviewColourChooser.showColourChooser(Desktop.getDesktop(),
+          JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(),
                   title, featureColour.getColour(), listener);
         }
         else
@@ -401,10 +401,8 @@ public class FeatureEditor
 
     start.setValue(new Integer(firstFeature.getBegin()));
     end.setValue(new Integer(firstFeature.getEnd()));
-    ((SpinnerNumberModel) start.getModel())
-            .setMaximum(firstFeature.getEnd());
-    ((SpinnerNumberModel) end.getModel())
-            .setMinimum(firstFeature.getBegin());
+    ((SpinnerNumberModel) start.getModel()).setMaximum(firstFeature.getEnd());
+    ((SpinnerNumberModel) end.getModel()).setMinimum(firstFeature.getBegin());
 
     description.setText(firstFeature.getDescription());
     featureColour = fr.getFeatureStyle(featureType);
@@ -427,8 +425,8 @@ public class FeatureEditor
    */
   public void showDialog()
   {
-    Runnable okAction = forCreate ? getCreateAction() : getAmendAction();
-    Runnable cancelAction = getCancelAction();
+         Runnable okAction = forCreate ? getCreateAction() : getAmendAction();
+         Runnable cancelAction = getCancelAction();
 
     /*
      * set dialog action handlers for OK (create/Amend) and Cancel options
@@ -462,8 +460,9 @@ public class FeatureEditor
     }
 
     dialog.showInternalDialog(mainPanel, title,
-            JvOptionPane.YES_NO_CANCEL_OPTION, JvOptionPane.PLAIN_MESSAGE,
-            null, options, MessageManager.getString("action.ok"));
+            JvOptionPane.YES_NO_CANCEL_OPTION,
+            JvOptionPane.PLAIN_MESSAGE, null, options,
+            MessageManager.getString("action.ok"));
   }
 
   /**
@@ -476,7 +475,7 @@ public class FeatureEditor
    */
   protected Runnable getCancelAction()
   {
-    Runnable okAction = new Runnable()
+       Runnable okAction = new Runnable()
     {
       @Override
       public void run()
@@ -500,7 +499,7 @@ public class FeatureEditor
    */
   protected Runnable getCreateAction()
   {
-    Runnable okAction = new Runnable()
+       Runnable okAction = new Runnable()
     {
       boolean useLastDefaults = features.get(0).getType() == null;
 
@@ -559,7 +558,7 @@ public class FeatureEditor
    */
   protected Runnable getDeleteAction()
   {
-    Runnable deleteAction = new Runnable()
+         Runnable deleteAction = new Runnable()
     {
       @Override
       public void run()
@@ -662,14 +661,13 @@ public class FeatureEditor
    */
   protected Runnable getAmendAction()
   {
-    Runnable okAction = new Runnable()
+       Runnable okAction = new Runnable()
     {
       boolean useLastDefaults = features.get(0).getType() == null;
-
+  
       String featureType = name.getText();
-
+  
       String featureGroup = group.getText();
-
       @Override
       public void run()
       {
index bb15b55..c5501ea 100644 (file)
@@ -439,8 +439,8 @@ public class FeatureSettings extends JPanel
       }
       else
       {
-        Desktop.addInternalFrame(frame, title, false, bounds.width,
-                bounds.height);
+        Desktop.addInternalFrame(frame, title, Desktop.FRAME_NOT_VISIBLE, bounds.width,
+                bounds.height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
         frame.setBounds(bounds);
         frame.setVisible(true);
       }
index 358d9a4..1236c5a 100755 (executable)
@@ -139,7 +139,7 @@ public class Finder extends GFinder
     {
       title += " " + scope;
     }
-    Desktop.addInternalFrame(frame, title, MY_WIDTH, MY_HEIGHT);
+    Desktop.addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, MY_WIDTH, MY_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_ALLOW_ANY_SIZE);
     frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
     searchBox.getComponent().requestFocus();
   }
@@ -195,7 +195,7 @@ public class Finder extends GFinder
    */
   boolean getFocusedViewport()
   {
-    if (focusFixed || Desktop.desktop == null)
+    if (focusFixed || Desktop.getDesktopPane() == null)
     {
       if (ap != null && av != null)
       {
@@ -207,7 +207,7 @@ public class Finder extends GFinder
     }
     // now checks further down the window stack to fix bug
     // https://mantis.lifesci.dundee.ac.uk/view.php?id=36008
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
     for (int f = 0; f < frames.length; f++)
     {
       JInternalFrame alignFrame = frames[f];
index 92cc4c6..2d9682b 100755 (executable)
@@ -137,14 +137,14 @@ public class FontChooser extends GFontChooser
     if (isTreeFont())
     {
       Desktop.addInternalFrame(frame,
-              MessageManager.getString("action.change_font_tree_panel"),
-              400, 200, false);
+              MessageManager.getString("action.change_font_tree_panel"), Desktop.FRAME_MAKE_VISIBLE,
+              400, 200, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     }
     else
     {
       Desktop.addInternalFrame(frame,
-              MessageManager.getString("action.change_font"), 380, 220,
-              false);
+              MessageManager.getString("action.change_font"), Desktop.FRAME_MAKE_VISIBLE, 380, 220,
+              Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     }
 
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
index 35bd871..c250aca 100644 (file)
@@ -56,4 +56,11 @@ public interface IProgressIndicator
    */
   boolean operationInProgress();
 
+  /**
+   * Remove progress bar with a given id from the panel.
+   * 
+   * @param id
+   */
+  void removeProgressBar(long id);
+
 }
index c94dee0..4f4b796 100755 (executable)
@@ -55,17 +55,18 @@ public class IdCanvas extends JPanel implements ViewportListenerI
 
   BufferedImage image;
 
-  // Graphics2D gg;
+//  Graphics2D gg;
 
   int imgHeight = 0;
 
-  boolean fastPaint = false;
+  private boolean fastPaint = false;
 
   List<SequenceI> searchResults;
 
   AnnotationPanel ap;
 
   private Font idfont;
+  private boolean allowFastPaint;
 
   /**
    * Creates a new IdCanvas object.
@@ -79,7 +80,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     this.av = av;
     PaintRefresher.Register(this, av.getSequenceSetId());
     av.getRanges().addPropertyChangeListener(this);
-  }
+    }
 
   /**
    * DOCUMENT ME!
@@ -137,6 +138,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     g.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos,
             (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5));
 
+    // JAL-3253-applet was just hiddenRows here. ccfc48e (gmungoc) added getShowHiddenMarkers test
     if (hiddenRows && av.getShowHiddenMarkers())
     {
       drawMarker(g, av, i, starty, ypos);
@@ -203,7 +205,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     gg.translate(0, -transY);
 
     gg.dispose();
-
+    
     fastPaint = true;
 
     // Call repaint on alignment panel so that repaints from other alignment
@@ -221,44 +223,47 @@ public class IdCanvas extends JPanel implements ViewportListenerI
   @Override
   public void paintComponent(Graphics g)
   {
+    if (av.getAlignPanel().getHoldRepaint())
+    {
+      return;
+    }
     g.setColor(Color.white);
     g.fillRect(0, 0, getWidth(), getHeight());
-
-    if (fastPaint)
+    
+    if (allowFastPaint && fastPaint)
     {
       fastPaint = false;
       g.drawImage(image, 0, 0, this);
-
+    
       return;
     }
-
+    
     int oldHeight = imgHeight;
-
+    
     imgHeight = getHeight();
     imgHeight -= (imgHeight % av.getCharHeight());
-
+    
     if (imgHeight < 1)
     {
       return;
     }
-
+    
     if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
     {
       image = new BufferedImage(getWidth(), imgHeight,
-              BufferedImage.TYPE_INT_RGB);
+                BufferedImage.TYPE_INT_RGB);
     }
-
+    
     Graphics2D gg = image.createGraphics();
-
+    
     // Fill in the background
     gg.setColor(Color.white);
     gg.fillRect(0, 0, getWidth(), imgHeight);
-
-    drawIds(gg, av, av.getRanges().getStartSeq(),
-            av.getRanges().getEndSeq(), searchResults);
+    
+    drawIds(gg, av, av.getRanges().getStartSeq(), av.getRanges().getEndSeq(), searchResults);
 
     gg.dispose();
-
+    
     g.drawImage(image, 0, 0, this);
   }
 
@@ -274,13 +279,14 @@ public class IdCanvas extends JPanel implements ViewportListenerI
    * @param endSeq
    * @param selection
    */
-  void drawIds(Graphics2D g, AlignViewport alignViewport,
-          final int startSeq, final int endSeq, List<SequenceI> selection)
+  void drawIds(Graphics2D g, AlignViewport alignViewport, final int startSeq,
+          final int endSeq, List<SequenceI> selection)
   {
     Font font = alignViewport.getFont();
     if (alignViewport.isSeqNameItalics())
     {
-      setIdfont(new Font(font.getName(), Font.ITALIC, font.getSize()));
+      setIdfont(new Font(font.getName(), Font.ITALIC,
+              font.getSize()));
     }
     else
     {
@@ -348,7 +354,8 @@ public class IdCanvas extends JPanel implements ViewportListenerI
       g.setColor(currentColor);
 
       int charHeight = alignViewport.getCharHeight();
-      g.fillRect(0, (i - startSeq) * charHeight, getWidth(), charHeight);
+      g.fillRect(0, (i - startSeq) * charHeight,
+              getWidth(), charHeight);
 
       g.setColor(currentTextColor);
 
@@ -360,9 +367,8 @@ public class IdCanvas extends JPanel implements ViewportListenerI
         xPos = panelWidth - fm.stringWidth(string) - 4;
       }
 
-      g.drawString(string, xPos,
-              (((i - startSeq) * charHeight) + charHeight)
-                      - (charHeight / 5));
+      g.drawString(string, xPos, (((i - startSeq) * charHeight) + charHeight)
+              - (charHeight / 5));
 
       if (hasHiddenRows && av.getShowHiddenMarkers())
       {
@@ -385,17 +391,31 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     int alignmentWidth = alignViewport.getAlignment().getWidth();
     final int alheight = alignViewport.getAlignment().getHeight();
 
-    /*
+
+    /* (former)
      * assumption: SeqCanvas.calculateWrappedGeometry has been called
+     * 
+     * was based on the fact that SeqCanvas was added as a child prior to IdCanvas, 
+     * and children are processed in order of addition.
+     * 
+     * It's probably fine. But...
+     * 
      */
     SeqCanvas seqCanvas = alignViewport.getAlignPanel()
             .getSeqPanel().seqCanvas;
+    // ...better: let's call it now
+    seqCanvas.calculateWrappedGeometry();
 
     final int charHeight = alignViewport.getCharHeight();
 
     AnnotationLabels labels = null;
     if (alignViewport.isShowAnnotation())
     {
+       // BH when was ap == null?
+      if (ap == null)
+      {
+        ap = new AnnotationPanel(alignViewport);
+      }
       labels = new AnnotationLabels(alignViewport);
     }
 
@@ -448,8 +468,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
    * @param starty
    * @param yoffset
    */
-  void drawMarker(Graphics2D g, AlignViewport alignViewport, int seqIndex,
-          int starty, int yoffset)
+  void drawMarker(Graphics2D g, AlignViewport alignViewport, int seqIndex, int starty, int yoffset)
   {
     SequenceI[] hseqs = alignViewport.getAlignment()
             .getHiddenSequences().hiddenSequences;
@@ -492,7 +511,8 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     /*
      * vertices of the triangle, below or above hidden seqs
      */
-    int[] xPoints = new int[] { getWidth() - charHeight,
+    int[] xPoints = new int[]
+    { getWidth() - charHeight,
         getWidth() - charHeight, getWidth() };
     int yShift = seqIndex - starty;
 
@@ -569,20 +589,34 @@ public class IdCanvas extends JPanel implements ViewportListenerI
   public void propertyChange(PropertyChangeEvent evt)
   {
     String propertyName = evt.getPropertyName();
-    if (propertyName.equals(ViewportRanges.STARTSEQ)
-            || (av.getWrapAlignment()
-                    && propertyName.equals(ViewportRanges.STARTRES)))
+    switch (propertyName)
     {
+    case ViewportRanges.STARTSEQ:
       fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
-    }
-    else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
+      break;
+    case ViewportRanges.STARTRES:
+      if (av.getWrapAlignment())
+      {
+        fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
+      }
+      break;
+    case ViewportRanges.STARTRESANDSEQ:
       fastPaint(((int[]) evt.getNewValue())[1]
               - ((int[]) evt.getOldValue())[1]);
-    }
-    else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
-    {
+      break;
+    case ViewportRanges.MOVE_VIEWPORT:
       repaint();
+      break;
+    default:
     }
   }
+
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
 }
index 7b491e4..3aa960e 100755 (executable)
@@ -241,7 +241,7 @@ public class IdPanel extends JPanel
       jalview.util.BrowserLauncher.openURL(url);
     } catch (Exception ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.getString("label.web_browser_not_found_unix"),
               MessageManager.getString("label.web_browser_not_found"),
               JvOptionPane.WARNING_MESSAGE);
index 784e6c1..21c1af0 100644 (file)
@@ -23,6 +23,7 @@ package jalview.gui;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 
+@Deprecated
 public class JalviewChangeSupport implements PropertyChangeListener
 {
   public void propertyChange(PropertyChangeEvent evt)
index 1d7bf3d..0ff5606 100644 (file)
@@ -78,11 +78,11 @@ public abstract class JalviewDialog extends JPanel
           boolean block, String title, int width, int height)
   {
 
-    frame = new JDialog(Desktop.instance, modal);
+    frame = new JDialog(Desktop.getInstance(), modal);
     frame.setTitle(title);
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Rectangle deskr = Desktop.instance.getBounds();
+      Rectangle deskr = Desktop.getInstance().getBounds();
       frame.setBounds(new Rectangle((int) (deskr.getCenterX() - width / 2),
               (int) (deskr.getCenterY() - height / 2), width, height));
     }
index e7f992d..a022c85 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
+import java.awt.Container;
 import java.awt.Font;
-import java.awt.GridLayout;
-import java.awt.Rectangle;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -47,6 +45,7 @@ import javax.swing.border.Border;
 import javax.swing.border.TitledBorder;
 
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 /**
  * useful functions for building Swing GUIs
@@ -56,12 +55,17 @@ import jalview.util.MessageManager;
  */
 public final class JvSwingUtils
 {
+  static final String HTML_PREFIX = (Platform.isJS() ? 
+          "<html><div style=\"max-width:350px;overflow-wrap:break-word;display:inline-block\">"
+          : "<html><div style=\"width:350; text-align: justify; word-wrap: break-word;\">"
+            );
   /**
    * wrap a bare html safe string to around 60 characters per line using a CSS
    * style class specifying word-wrap and break-word
    * 
    * @param enclose
-   *          if true, add &lt;html&gt; wrapper tags
+   *          if true, add &lt;html&gt; wrapper tags (currently false for only
+   *          two references -- both in Jws2Discoverer --
    * @param ttext
    * 
    * @return
@@ -70,40 +74,40 @@ public final class JvSwingUtils
   {
     Objects.requireNonNull(ttext,
             "Tootip text to format must not be null!");
-    ttext = ttext.trim();
+    ttext = ttext.trim().replaceAll("<br/>", "<br>");
     boolean maxLengthExceeded = false;
 
-    if (ttext.contains("<br>"))
+    boolean isHTML = ttext.startsWith("<html>");
+    if (isHTML)
     {
-      String[] htmllines = ttext.split("<br>");
-      for (String line : htmllines)
-      {
-        maxLengthExceeded = line.length() > 60;
-        if (maxLengthExceeded)
-        {
+      ttext = ttext.substring(6);
+    }
+    if (ttext.endsWith("</html>"))
+    {
+      isHTML = true;
+      ttext = ttext.substring(0, ttext.length() - 7);
+    }
+    boolean hasBR = ttext.contains("<br>");
+    enclose |= isHTML || hasBR;
+    if (hasBR)
+    {  
+      int pt = -1, ptlast = -4;
+      while ((pt = ttext.indexOf("<br>", pt + 1)) >= 0) {
+        if (pt - ptlast - 4 > 60) {
+          maxLengthExceeded = true;
           break;
         }
       }
     }
-    else
+    else  
     {
       maxLengthExceeded = ttext.length() > 60;
     }
 
-    if (!maxLengthExceeded)
-    {
-      return enclose ? "<html>" + ttext + "</html>" : ttext;
-    }
-
-    return (enclose ? "<html>" : "")
-            // BH 2018
-            + "<style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style><div class=\"ttip\">"
-            // + "<style> p.ttip {width:350px;margin:-14px 0px -14px
-            // 0px;padding:2px;overflow-wrap:break-word;}"
-            // + "</style><p class=\"ttip\">"
-            + ttext + " </div>"
-            // + "</p>"
-            + ((enclose ? "</html>" : ""));
+    String ret = (!enclose ? ttext : maxLengthExceeded ? HTML_PREFIX + ttext + "</div></html>" :
+      "<html>" + ttext + "</html>");
+    //System.out.println("JvSwUtil " + enclose + " " + maxLengthExceeded + " " + ret);
+    return ret;
   }
 
   public static JButton makeButton(String label, String tooltip,
@@ -149,34 +153,26 @@ public final class JvSwingUtils
   }
 
   /**
+   * A convenience method that that adds a component with label to a container,
+   * sets a tooltip on both component and label, and optionally specifies layout
+   * constraints for the added component (but not the label)
    * 
-   * @param panel
+   * @param container
    * @param tooltip
    * @param label
-   * @param valBox
-   * @return the GUI element created that was added to the layout so it's
-   *         attributes can be changed.
+   * @param comp
+   * @param constraints
    */
-  public static JPanel addtoLayout(JPanel panel, String tooltip,
-          JComponent label, JComponent valBox)
+  public static void addtoLayout(Container container, String tooltip,
+          JComponent label, JComponent comp, String constraints)
   {
-    JPanel laypanel = new JPanel(new GridLayout(1, 2));
-    JPanel labPanel = new JPanel(new BorderLayout());
-    JPanel valPanel = new JPanel();
-    labPanel.setBounds(new Rectangle(7, 7, 158, 23));
-    valPanel.setBounds(new Rectangle(172, 7, 270, 23));
-    labPanel.add(label, BorderLayout.WEST);
-    valPanel.add(valBox);
-    laypanel.add(labPanel);
-    laypanel.add(valPanel);
-    valPanel.setToolTipText(tooltip);
-    labPanel.setToolTipText(tooltip);
-    valBox.setToolTipText(tooltip);
-    panel.add(laypanel);
-    panel.validate();
-    return laypanel;
+    container.add(label);
+    container.add(comp, constraints);
+    comp.setToolTipText(tooltip); // this doesn't seem to show?
+    label.setToolTipText(tooltip);
   }
 
+  // From 2.11.2 merge
   public static void mgAddtoLayout(JPanel cpanel, String tooltip,
           JLabel jLabel, JComponent name)
   {
@@ -346,7 +342,6 @@ public final class JvSwingUtils
           combo.setToolTipText(tooltips.get(j));
         }
       }
-
       @Override
       public void mouseExited(MouseEvent e)
       {
@@ -362,7 +357,7 @@ public final class JvSwingUtils
 
   /**
    * Adds a titled border to the component in the default font and position (top
-   * left), optionally witht italic text
+   * left), optionally with italic text
    * 
    * @param comp
    * @param title
index d55733c..62bdd35 100644 (file)
@@ -86,7 +86,7 @@ public class LineartOptions extends JPanel
       ex.printStackTrace();
     }
 
-    dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+    dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
   }
 
   /**
@@ -188,7 +188,7 @@ public class LineartOptions extends JPanel
     }
     else
     {
-      Cache.applicationProperties.remove(preferencesKey);
+      Cache.removePropertyNoSave(preferencesKey);
     }
   }
 
index 2bb01ee..a4c31c4 100644 (file)
@@ -64,7 +64,7 @@ public class OOMWarning implements Runnable
 
   public OOMWarning(String string, OutOfMemoryError oomerror)
   {
-    this(string, oomerror, Desktop.desktop);
+    this(string, oomerror, Desktop.getDesktopPane());
   }
 
   @Override
index e0427cc..577a1ef 100644 (file)
  */
 package jalview.gui;
 
+import jalview.bin.Cache;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.dm.JabaOption;
+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 jalview.ws.params.simple.FileParameter;
+import jalview.ws.params.simple.LogarithmicParameter;
+import jalview.ws.params.simple.RadioChoiceParameter;
+import jalview.ws.params.simple.StringParameter;
 import java.awt.BorderLayout;
+import java.awt.Color;
 import java.awt.Component;
+import java.awt.Container;
 import java.awt.Dimension;
+import java.awt.FlowLayout;
 import java.awt.Font;
-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.KeyAdapter;
 import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
+import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.swing.ButtonGroup;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
@@ -47,19 +69,15 @@ import javax.swing.JLabel;
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
+import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
+import javax.swing.JSlider;
 import javax.swing.JTextArea;
 import javax.swing.JTextField;
 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;
 
 /**
@@ -71,15 +89,33 @@ import net.miginfocom.swing.MigLayout;
  */
 public class OptsAndParamsPage
 {
-  /**
+  public static final int PARAM_WIDTH = 340;
+
+  public static final int PARAM_HEIGHT = 150;
+
+  public static final int PARAM_CLOSEDHEIGHT = 80;
+
+  URL linkImageURL = getClass().getResource("/images/link.gif");
+
+  Map<String, OptionBox> optSet = new LinkedHashMap<>();
+
+  Map<String, ParamBox> paramSet = new LinkedHashMap<>();
+
+  /*
    * compact or verbose style parameters
    */
   boolean compact = false;
 
+  OptsParametersContainerI poparent;
+
+  /**
+   * A class that models a panel rendering a single option (checkbox or choice
+   * list)
+   */
   public class OptionBox extends JPanel
           implements MouseListener, ActionListener
   {
-    JCheckBox enabled = new JCheckBox();
+    JCheckBox enabled;
 
     final URL finfo;
 
@@ -91,57 +127,91 @@ public class OptsAndParamsPage
 
     OptionI option;
 
-    JLabel optlabel = new JLabel();
+    JComboBox<Object> val;
 
-    JComboBox<String> val = new JComboBox<>();
+    /**
+     * Constructs and adds labels and controls to the panel for one Option
+     * 
+     * @param opt
+     */
 
     public OptionBox(OptionI opt)
     {
       option = opt;
-      setLayout(new BorderLayout());
-      enabled.setSelected(opt.isRequired()); // TODO: lock required options
+      setLayout(new FlowLayout(FlowLayout.LEFT));
+      enabled = new JCheckBox(opt.getLabel());
+      enabled.setSelected(opt.isRequired());
+
+      /*
+       * If option is required, show a label, if optional a checkbox
+       * (but not for Jabaws pending JWS-126 resolution)
+       */
+      if (opt.isRequired() && !(opt instanceof JabaOption))
+      {
+        finfo = null;
+        add(new JLabel(opt.getLabel()));
+      }
+      else
+      {
+        finfo = option.getFurtherDetails();
+        configureCheckbox(opt);
+        add(enabled);
+      }
+
+      /*
+       * construct the choice box with possible values, 
+       * or their display names if provided
+       */
+      val = buildComboBox(opt);
+      val.setSelectedItem(opt.getValue());
+
+      /*
+       * only show the choicebox if there is more than one option,
+       * or the option is mandatory
+       */
+      if (opt.getPossibleValues().size() > 1 || opt.isRequired())
+      {
+        val.addActionListener(this);
+        add(val);
+      }
+
+      setInitialValue();
+    }
+
+    /**
+     * Configures the checkbox that controls whether or not the option is
+     * selected
+     * 
+     * @param opt
+     */
+    protected void configureCheckbox(OptionI opt)
+    {
       enabled.setFont(new Font("Verdana", Font.PLAIN, 11));
-      enabled.setText("");
-      enabled.setText(opt.getName());
       enabled.addActionListener(this);
-      finfo = option.getFurtherDetails();
-      String desc = opt.getDescription();
+      final String desc = opt.getDescription();
       if (finfo != null)
       {
         hasLink = true;
 
-        enabled.setToolTipText(JvSwingUtils.wrapTooltip(true,
-                ((desc == null || desc.trim().length() == 0)
-                        ? MessageManager.getString(
-                                "label.opt_and_params_further_details")
-                        : desc) + "<br><img src=\"" + linkImageURL
-                        + "\"/>"));
-        enabled.addMouseListener(this);
+        String description = desc;
+        if (desc == null || desc.trim().isEmpty())
+        {
+          description = MessageManager
+                  .getString("label.opt_and_params_further_details");
+        }
+        description = description + "<br><img src=\"" + linkImageURL
+                + "\"/>";
+        String text = JvSwingUtils.wrapTooltip(true, description);
+        enabled.setToolTipText(text);
+        enabled.addMouseListener(this); // for popup menu to show link
       }
       else
       {
         if (desc != null && desc.trim().length() > 0)
         {
-          enabled.setToolTipText(
-                  JvSwingUtils.wrapTooltip(true, opt.getDescription()));
+          enabled.setToolTipText(JvSwingUtils.wrapTooltip(true, desc));
         }
       }
-      add(enabled, BorderLayout.NORTH);
-      for (String str : opt.getPossibleValues())
-      {
-        val.addItem(str);
-      }
-      val.setSelectedItem(opt.getValue());
-      if (opt.getPossibleValues().size() > 1)
-      {
-        setLayout(new GridLayout(1, 2));
-        val.addActionListener(this);
-        add(val, BorderLayout.SOUTH);
-      }
-      // TODO: add actionListeners for popup (to open further info),
-      // and to update list of parameters if an option is enabled
-      // that takes a value. JBPNote: is this TODO still valid ?
-      setInitialValue();
     }
 
     @Override
@@ -178,31 +248,21 @@ public class OptsAndParamsPage
       poparent.argSetModified(this, !notmod);
     }
 
-    public OptionI getOptionIfEnabled()
+    /**
+     * Answers null if the option is not selected, else a new Option holding the
+     * selected value
+     * 
+     * @return
+     */
+    public ArgumentI getSelectedOption()
     {
       if (!enabled.isSelected())
       {
         return null;
       }
+      String value = getSelectedValue(option, val.getSelectedIndex());
       OptionI opt = option.copy();
-      if (opt.getPossibleValues() != null
-              && opt.getPossibleValues().size() == 1)
-      {
-        // Hack to make sure the default value for an enabled option with only
-        // one value is actually returned
-        opt.setValue(opt.getPossibleValues().get(0));
-      }
-      if (val.getSelectedItem() != null)
-      {
-        opt.setValue((String) val.getSelectedItem());
-      }
-      else
-      {
-        if (option.getValue() != null)
-        {
-          opt.setValue(option.getValue());
-        }
-      }
+      opt.setValue(value);
       return opt;
     }
 
@@ -218,14 +278,12 @@ public class OptsAndParamsPage
     @Override
     public void mouseEntered(MouseEvent e)
     {
-      // TODO Auto-generated method stub
 
     }
 
     @Override
     public void mouseExited(MouseEvent e)
     {
-      // TODO Auto-generated method stub
 
     }
 
@@ -268,28 +326,57 @@ public class OptsAndParamsPage
       }
     }
 
+    /**
+     * toString representation for identification in the debugger only
+     */
+    @Override
+    public String toString()
+    {
+      return option == null ? super.toString() : option.toString();
+    }
   }
 
+  /**
+   * A class that models a panel rendering a single parameter
+   */
   public class ParamBox extends JPanel
           implements ChangeListener, ActionListener, MouseListener
   {
-    boolean adjusting = false;
+    /*
+     * parameter values (or their logs) are multiplied by this
+     * scaling factor to ensure an integer range for the slider
+     */
+    private int sliderScaleFactor = 1;
+
+    boolean isLogarithmicParameter;
+
+    boolean isChoiceParameter;
 
-    boolean choice = false;
+    boolean isIntegerParameter;
 
-    JComboBox<String> choicebox;
+    boolean isStringParameter;
 
-    JPanel controlPanel = new JPanel();
+    boolean adjusting;
 
-    boolean descisvisible = false;
+    /*
+     * drop-down list of choice options (if applicable)
+     */
+    JComboBox<Object> choicebox;
+
+    /*
+     * radio buttons as an alternative to combo box
+     */
+    ButtonGroup buttonGroup;
+
+    JPanel controlsPanel = new JPanel();
+
+    boolean descriptionIsVisible = false;
 
     JScrollPane descPanel = new JScrollPane();
 
     final URL finfo;
 
-    boolean integ = false;
-
-    String lastVal;
+    Object lastVal;
 
     ParameterI parameter;
 
@@ -297,58 +384,82 @@ public class OptsAndParamsPage
 
     JPanel settingPanel = new JPanel();
 
-    JButton showDesc = new JButton();
+    JSlider slider;
 
-    Slider slider = null;
+    JTextArea descriptionText = new JTextArea();
 
-    JTextArea string = new JTextArea();
+    ValueConstrainI validator;
 
-    ValueConstrainI validator = null;
+    JTextField valueField;
 
-    JTextField valueField = null;
+    private String descTooltip;
 
-    public ParamBox(final OptsParametersContainerI pmlayout,
+    public ParamBox(final OptsParametersContainerI paramContainer,
             ParameterI parm)
     {
-      pmdialogbox = pmlayout;
+      pmdialogbox = paramContainer;
       finfo = parm.getFurtherDetails();
       validator = parm.getValidValue();
       parameter = parm;
+      isLogarithmicParameter = parm instanceof LogarithmicParameter;
       if (validator != null)
       {
-        integ = validator.getType() == ValueType.Integer;
-      }
-      else
-      {
-        if (parameter.getPossibleValues() != null)
+        ValueType type = validator.getType();
+        isIntegerParameter = type == ValueType.Integer;
+        isStringParameter = type == ValueType.String
+                || type == ValueType.File;
+
+        /*
+         * ensure slider has an integer range corresponding to
+         * the min-max range of the parameter
+         */
+        if (validator.getMin() != null && validator.getMax() != null
+        // && !isIntegerParameter
+                && !isStringParameter)
         {
-          choice = true;
+          double min = validator.getMin().doubleValue();
+          double max = validator.getMax().doubleValue();
+          if (isLogarithmicParameter)
+          {
+            min = Math.log(min);
+            max = Math.log(max);
+          }
+          sliderScaleFactor = (int) (1000000 / (max - min));
+          // todo scaleMin, scaleMax could also be final fields
         }
       }
 
-      if (!compact)
+      List<String> possibleValues = parameter.getPossibleValues();
+      isChoiceParameter = possibleValues != null
+              && !possibleValues.isEmpty();
+
+      if (compact)
       {
-        makeExpanderParam(parm);
+        addCompactParameter(parm);
       }
       else
       {
-        makeCompactParam(parm);
+        addExpandableParam(parm);
 
       }
     }
 
-    private void makeCompactParam(ParameterI parm)
+    /**
+     * Adds a 'compact' format parameter, with any help text shown as a tooltip
+     * 
+     * @param parm
+     */
+    private void addCompactParameter(ParameterI parm)
     {
       setLayout(new MigLayout("", "[][grow]"));
 
       String ttipText = null;
 
-      controlPanel.setLayout(new BorderLayout());
+      controlsPanel.setLayout(new BorderLayout());
 
       if (parm.getDescription() != null
               && parm.getDescription().trim().length() > 0)
       {
-        // Only create description boxes if there actually is a description.
         ttipText = (JvSwingUtils.wrapTooltip(true,
                 parm.getDescription() + (finfo != null ? "<br><img src=\""
                         + linkImageURL + "\"/>"
@@ -357,91 +468,58 @@ public class OptsAndParamsPage
                         : "")));
       }
 
-      JvSwingUtils.mgAddtoLayout(this, ttipText, new JLabel(parm.getName()),
-              controlPanel, "");
+      JvSwingUtils.addtoLayout(this, ttipText, new JLabel(parm.getName()),
+              controlsPanel, "");
       updateControls(parm);
       validate();
     }
 
-    private void makeExpanderParam(final ParameterI parm)
+    /**
+     * Adds an 'expanded' format parameter, with any help shown in a panel that
+     * may be shown or hidden
+     * 
+     * @param parm
+     */
+    private void addExpandableParam(ParameterI parm)
     {
       setPreferredSize(new Dimension(PARAM_WIDTH, PARAM_CLOSEDHEIGHT));
       setBorder(new TitledBorder(parm.getName()));
       setLayout(null);
-      showDesc.setFont(new Font("Verdana", Font.PLAIN, 6));
-      showDesc.setText("+");
-      string.setFont(new Font("Verdana", Font.PLAIN, 11));
-      string.setBackground(getBackground());
+      descriptionText.setFont(new Font("Verdana", Font.PLAIN, 11));
+      descriptionText.setBackground(getBackground());
 
-      string.setEditable(false);
-      descPanel.getViewport().setView(string);
+      descriptionText.setEditable(false);
+      descPanel.getViewport().setView(descriptionText);
 
       descPanel.setVisible(false);
 
       JPanel firstrow = new JPanel();
       firstrow.setLayout(null);
-      controlPanel.setLayout(new BorderLayout());
-      controlPanel.setBounds(new Rectangle(39, 10, PARAM_WIDTH - 70,
+      controlsPanel.setLayout(new BorderLayout());
+      controlsPanel.setBounds(new Rectangle(39, 10, PARAM_WIDTH - 70,
               PARAM_CLOSEDHEIGHT - 50));
-      firstrow.add(controlPanel);
+      firstrow.add(controlsPanel);
       firstrow.setBounds(new Rectangle(10, 20, PARAM_WIDTH - 30,
               PARAM_CLOSEDHEIGHT - 30));
 
-      final ParamBox me = this;
 
       if (parm.getDescription() != null
               && parm.getDescription().trim().length() > 0)
       {
-        // Only create description boxes if there actually is a description.
-        if (finfo != null)
-        {
-          showDesc.setToolTipText(JvSwingUtils.wrapTooltip(true,
-                  MessageManager.formatMessage(
-                          "label.opt_and_params_show_brief_desc_image_link",
-                          new String[]
-                          { linkImageURL.toExternalForm() })));
-          showDesc.addMouseListener(this);
-        }
-        else
-        {
-          showDesc.setToolTipText(
-                  JvSwingUtils.wrapTooltip(true, MessageManager.getString(
-                          "label.opt_and_params_show_brief_desc")));
-        }
-        showDesc.addActionListener(new ActionListener()
-        {
-
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            descisvisible = !descisvisible;
-            descPanel.setVisible(descisvisible);
-            descPanel.getVerticalScrollBar().setValue(0);
-            me.setPreferredSize(new Dimension(PARAM_WIDTH,
-                    (descisvisible) ? PARAM_HEIGHT : PARAM_CLOSEDHEIGHT));
-            me.validate();
-            pmdialogbox.refreshParamLayout();
-          }
-        });
-        string.setWrapStyleWord(true);
-        string.setLineWrap(true);
-        string.setColumns(32);
-        string.setText(parm.getDescription());
-        showDesc.setBounds(new Rectangle(10, 10, 16, 16));
-        firstrow.add(showDesc);
+        addExpandableHelp(firstrow, parm);
       }
       add(firstrow);
       validator = parm.getValidValue();
       parameter = parm;
       if (validator != null)
       {
-        integ = validator.getType() == ValueType.Integer;
+        isIntegerParameter = validator.getType() == ValueType.Integer;
       }
       else
       {
         if (parameter.getPossibleValues() != null)
         {
-          choice = true;
+          isChoiceParameter = true;
         }
       }
       updateControls(parm);
@@ -452,8 +530,55 @@ public class OptsAndParamsPage
     }
 
     /**
-     * Action on input in text field
+     * Adds a button which can be clicked to show or hide help text
+     * 
+     * @param container
+     * @param param
      */
+    protected void addExpandableHelp(JPanel container, ParameterI param)
+    {
+      JButton showDescBtn = new JButton("+");
+      showDescBtn.setFont(new Font("Verdana", Font.PLAIN, 8));
+      if (finfo != null)
+      {
+        descTooltip = JvSwingUtils.wrapTooltip(true,
+                MessageManager.formatMessage(
+                        "label.opt_and_params_show_brief_desc_image_link",
+                        new String[]
+                        { linkImageURL.toExternalForm() }));
+        showDescBtn.addMouseListener(this);
+      }
+      else
+      {
+        descTooltip = JvSwingUtils.wrapTooltip(true, MessageManager
+                .getString("label.opt_and_params_show_brief_desc"));
+      }
+      showDescBtn.setToolTipText(descTooltip);
+      showDescBtn.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          descriptionIsVisible = !descriptionIsVisible;
+          showDescBtn.setText(descriptionIsVisible ? "-" : "+");
+          showDescBtn.setToolTipText(
+                  descriptionIsVisible ? null : descTooltip);
+          descPanel.setVisible(descriptionIsVisible);
+          descPanel.getVerticalScrollBar().setValue(0);
+          ParamBox.this.setPreferredSize(new Dimension(PARAM_WIDTH,
+                  (descriptionIsVisible) ? PARAM_HEIGHT
+                          : PARAM_CLOSEDHEIGHT));
+          ParamBox.this.validate();
+          pmdialogbox.refreshParamLayout();
+        }
+      });
+      descriptionText.setWrapStyleWord(true);
+      descriptionText.setLineWrap(true);
+      descriptionText.setColumns(32);
+      descriptionText.setText(param.getDescription());
+      showDescBtn.setBounds(new Rectangle(10, 10, 16, 16));
+      container.add(showDescBtn);
+    }
     @Override
     public void actionPerformed(ActionEvent e)
     {
@@ -461,30 +586,24 @@ public class OptsAndParamsPage
       {
         return;
       }
-      if (!choice)
-      {
-        updateSliderFromValueField();
-      }
       checkIfModified();
     }
 
+    /**
+     * Checks whether the value of this parameter has been changed and notifies
+     * the parent page accordingly
+     */
     private void checkIfModified()
     {
-      Object cstate = getCurrentValue();
-      boolean modified = !cstate.equals(lastVal);
+      Object newValue = updateSliderFromValueField();
+      boolean modified = true;
+      if (newValue.getClass() == lastVal.getClass())
+      {
+        modified = !newValue.equals(lastVal);
+      }
       pmdialogbox.argSetModified(this, modified);
     }
 
-    /**
-     * Answers the current value of the parameter, as text
-     * 
-     * @return
-     */
-    private String getCurrentValue()
-    {
-      return choice ? (String) choicebox.getSelectedItem()
-              : valueField.getText();
-    }
 
     @Override
     public int getBaseline(int width, int height)
@@ -501,22 +620,29 @@ public class OptsAndParamsPage
       return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
     }
 
-    public int getBoxHeight()
-    {
-      return (descisvisible ? PARAM_HEIGHT : PARAM_CLOSEDHEIGHT);
-    }
-
-    public ParameterI getParameter()
+    /**
+     * Answers an argument holding the value entered or selected in the dialog
+     * 
+     * @return
+     */
+    public ArgumentI getParameter()
     {
       ParameterI prm = parameter.copy();
-      if (choice)
+      String value = null;
+      if (parameter instanceof RadioChoiceParameter)
       {
-        prm.setValue((String) choicebox.getSelectedItem());
+        value = buttonGroup.getSelection().getActionCommand();
+      }
+      else if (isChoiceParameter)
+      {
+        value = getSelectedValue(this.parameter,
+                choicebox.getSelectedIndex());
       }
       else
       {
-        prm.setValue(valueField.getText());
+        value = valueField.getText();
       }
+      prm.setValue(value);
       return prm;
     }
 
@@ -538,14 +664,12 @@ public class OptsAndParamsPage
     @Override
     public void mouseEntered(MouseEvent e)
     {
-      // TODO Auto-generated method stub
 
     }
 
     @Override
     public void mouseExited(MouseEvent e)
     {
-      // TODO Auto-generated method stub
 
     }
 
@@ -561,53 +685,101 @@ public class OptsAndParamsPage
     @Override
     public void mouseReleased(MouseEvent e)
     {
-      // TODO Auto-generated method stub
 
     }
 
-    /**
-     * Action on change of slider value
-     */
     @Override
     public void stateChanged(ChangeEvent e)
     {
-      if (!adjusting)
+      if (adjusting)
       {
-        float value = slider.getSliderValue();
-        valueField.setText(integ ? Integer.toString((int) value)
-                : Float.toString(value));
+        return;
+      }
+      try
+      {
+        adjusting = true;
+        if (!isLogarithmicParameter)
+        {
+          /*
+           * set (int or float formatted) text field value
+           */
+          valueField.setText(isIntegerParameter
+                  ? String.valueOf(slider.getValue())
+                  : formatDouble(
+                          slider.getValue() / (float) sliderScaleFactor));
+        }
+        else
+        {
+          double value = Math.pow(Math.E,
+                  slider.getValue() / (double) sliderScaleFactor);
+          valueField.setText(formatDouble(value));
+        }
         checkIfModified();
+      } finally
+      {
+        adjusting = false;
       }
     }
 
-    public void updateControls(ParameterI parm)
+    /**
+     * Answers the value formatted as a string to 3 decimal places - in
+     * scientific notation if the value is less than 0.001
+     * 
+     * @param value
+     * @return
+     */
+    String formatDouble(double value)
+    {
+      String format = value < 0.001 ? "%3.1E" : "%3.3f";
+      return String.format(format, value);
+    }
+
+    /**
+     * Formats a number as integer or float (3dp) or scientific notation (1dp)
+     * 
+     * @param n
+     * @return
+     */
+    String formatNumber(Number n)
+    {
+      return n instanceof Integer ? String.valueOf(n.intValue())
+              : formatDouble(n.doubleValue());
+    }
+
+    void updateControls(ParameterI parm)
     {
       adjusting = true;
-      boolean init = (choicebox == null && valueField == null);
+      boolean init = (choicebox == null && valueField == null
+              && buttonGroup == null);
       if (init)
       {
-        if (choice)
+        if (parm instanceof RadioChoiceParameter)
+        {
+          buttonGroup = addRadioButtons(parameter, controlsPanel);
+        }
+        else if (isChoiceParameter)
         {
-          choicebox = new JComboBox<>();
+          choicebox = buildComboBox(parm);
           choicebox.addActionListener(this);
-          controlPanel.add(choicebox, BorderLayout.CENTER);
+          controlsPanel.add(choicebox, BorderLayout.CENTER);
         }
         else
         {
-          valueField = new JTextField();
+          slider = new JSlider();
+          slider.addChangeListener(this);
+          int cols = parm instanceof StringParameter ? 20 : 0;
+          valueField = new JTextField(cols);
           valueField.addActionListener(this);
-          valueField.addKeyListener(new KeyListener()
+          valueField.addKeyListener(new KeyAdapter()
           {
 
-            @Override
-            public void keyTyped(KeyEvent e)
-            {
-            }
 
             @Override
             public void keyReleased(KeyEvent e)
             {
-              if (e.isActionKey())
+              int keyCode = e.getKeyCode();
+              if (e.isActionKey() && keyCode != KeyEvent.VK_LEFT
+                      && keyCode != KeyEvent.VK_RIGHT)
               {
                 if (valueField.getText().trim().length() > 0)
                 {
@@ -616,173 +788,257 @@ public class OptsAndParamsPage
               }
             }
 
-            @Override
-            public void keyPressed(KeyEvent e)
-            {
-            }
           });
-          valueField.addFocusListener(new FocusAdapter()
-          {
+          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);
-          controlPanel.add(valueField, BorderLayout.EAST);
+          valueField.setPreferredSize(new Dimension(65, 25));
+          if (parm instanceof FileParameter)
+          {
+            valueField.setToolTipText(MessageManager
+                    .getString("label.double_click_to_browse"));
+            valueField.addMouseListener(new MouseAdapter()
+            {
+              @Override
+              public void mouseClicked(MouseEvent e)
+              {
+                if (e.getClickCount() == 2)
+                {
+                  String dir = Cache.getProperty("LAST_DIRECTORY");
+                  JalviewFileChooser chooser = new JalviewFileChooser(dir);
+                  chooser.setFileView(new JalviewFileView());
+                  chooser.setDialogTitle(
+                          MessageManager.getString("action.select_ddbb"));
+
+                  int val = chooser.showOpenDialog(ParamBox.this);
+                  if (val == JalviewFileChooser.APPROVE_OPTION)
+                  {
+                    File choice = chooser.getSelectedFile();
+                    String path = choice.getPath();
+                    valueField.setText(path);
+                    Cache.setProperty("LAST_DIRECTORY", choice.getParent());
+                    FileLoader.updateRecentlyOpened(path,
+                            DataSourceType.FILE);
+                  }
+                }
+              }
+            });
+          }
+          
+          controlsPanel.add(slider, BorderLayout.WEST);
+          controlsPanel.add(valueField, BorderLayout.EAST);
         }
       }
 
-      if (parm != null)
+      String value = parm.getValue();
+      if (value != null)
       {
-        if (choice)
+        if (isChoiceParameter)
         {
-          if (init)
+          if (!(parm instanceof RadioChoiceParameter))
           {
-            List<String> vals = parm.getPossibleValues();
-            for (String val : vals)
-            {
-              choicebox.addItem(val);
-            }
-          }
-
-          if (parm.getValue() != null)
-          {
-            choicebox.setSelectedItem(parm.getValue());
+            choicebox.setSelectedItem(value);
           }
         }
         else
         {
-          valueField.setText(parm.getValue());
+          valueField.setText(value);
         }
       }
-      lastVal = getCurrentValue();
+      lastVal = updateSliderFromValueField();
       adjusting = false;
     }
 
-    private Slider makeSlider(ValueConstrainI validValue)
+    /**
+     * Adds a panel to comp, containing a label and radio buttons for the choice
+     * of values of the given option. Returns a ButtonGroup whose members are
+     * the added radio buttons.
+     * 
+     * @param option
+     * @param comp
+     * 
+     * @return
+     */
+    protected ButtonGroup addRadioButtons(OptionI option, Container comp)
     {
-      if (validValue != null)
+      ButtonGroup bg = new ButtonGroup();
+      JPanel radioPanel = new JPanel();
+      radioPanel.add(new JLabel(option.getDescription()));
+
+      String value = option.getValue();
+
+      for (String opt : option.getPossibleValues())
       {
-        final Number minValue = validValue.getMin();
-        final Number maxValue = validValue.getMax();
-        if (minValue != null && maxValue != null)
-        {
-          return new Slider(minValue.floatValue(), maxValue.floatValue(),
-                  minValue.floatValue());
-        }
+        JRadioButton btn = new JRadioButton(opt);
+        btn.setActionCommand(opt);
+        boolean selected = opt.equals(value);
+        btn.setSelected(selected);
+        btn.addActionListener(this);
+        bg.add(btn);
+        radioPanel.add(btn);
       }
+      comp.add(radioPanel);
 
-      /*
-       * otherwise, a nominal slider which will not be visible
-       */
-      return new Slider(0, 100, 50);
+      return bg;
     }
 
-    public void updateSliderFromValueField()
+    /**
+     * Action depends on the type of the input parameter:
+     * <ul>
+     * <li>if a text input, returns the trimmed value</li>
+     * <li>if a choice list or radio button, returns the selected value</li>
+     * <li>if a value slider and input field, sets the value of the slider from
+     * the value in the text field, limiting it to any defined min-max
+     * range.</li>
+     * </ul>
+     * Answers the (possibly modified) input value, as a String, Integer, Float
+     * or Double.
+     * 
+     * @return
+     */
+    Object updateSliderFromValueField()
     {
-      if (validator != null)
+      if (validator == null || isStringParameter)
       {
-        final Number minValue = validator.getMin();
-        final Number maxValue = validator.getMax();
-        if (integ)
+        if (isChoiceParameter)
         {
-          int iVal = 0;
-          try
+          if (parameter instanceof RadioChoiceParameter)
           {
-            valueField.setText(valueField.getText().trim());
-            iVal = Integer.valueOf(valueField.getText());
-            if (minValue != null && minValue.intValue() > iVal)
-            {
-              iVal = minValue.intValue();
-              // TODO: provide visual indication that hard limit was reached for
-              // this parameter
-            }
-            if (maxValue != null && maxValue.intValue() < iVal)
-            {
-              iVal = maxValue.intValue();
-            }
-          } catch (NumberFormatException e)
-          {
-            System.err.println(e.toString());
-          }
-          if (minValue != null || maxValue != null)
-          {
-            valueField.setText(String.valueOf(iVal));
-            slider.setSliderValue(iVal);
+            return buttonGroup.getSelection().getActionCommand();
           }
           else
           {
-            slider.setVisible(false);
+            return getSelectedValue(this.parameter,
+                    choicebox.getSelectedIndex());
           }
         }
+        slider.setVisible(false);
+        return valueField.getText().trim();
+      }
+
+      if (validator.getMin() == null || validator.getMax() == null)
+      {
+        slider.setVisible(false);
+      }
+
+      valueField.setText(valueField.getText().trim());
+
+      /*
+       * ensure not outside min-max range
+       * TODO: provide some visual indicator if limit reached
+       */
+      try
+      {
+        valueField.setBackground(Color.WHITE);
+        double d = Double.parseDouble(valueField.getText());
+        if (validator.getMin() != null
+                && validator.getMin().doubleValue() > d)
+        {
+          valueField.setText(formatNumber(validator.getMin()));
+        }
+        if (validator.getMax() != null
+                && validator.getMax().doubleValue() < d)
+        {
+          valueField.setText(formatNumber(validator.getMax()));
+        }
+      } catch (NumberFormatException e)
+      {
+        valueField.setBackground(Color.yellow);
+        return Float.NaN;
+      }
+      if (isIntegerParameter)
+      {
+        int iVal = 0;
+        try
+        {
+          iVal = Integer.valueOf(valueField.getText());
+        } catch (Exception e)
+        {
+          valueField.setBackground(Color.yellow);
+          return Integer.valueOf(0);
+        }
+
+        if (validator.getMin() != null && validator.getMax() != null)
+        {
+          slider.getModel().setRangeProperties(iVal, 1,
+                  validator.getMin().intValue(),
+                  validator.getMax().intValue() + 1, true);
+        }
         else
         {
-          float fVal = 0f;
-          try
-          {
-            valueField.setText(valueField.getText().trim());
-            fVal = Float.valueOf(valueField.getText());
-            if (minValue != null && minValue.floatValue() > fVal)
-            {
-              fVal = minValue.floatValue();
-              // TODO: provide visual indication that hard limit was reached for
-              // this parameter
-              // update value field to reflect any bound checking we performed.
-              valueField.setText("" + fVal);
-            }
-            if (maxValue != null && maxValue.floatValue() < fVal)
-            {
-              fVal = maxValue.floatValue();
-              // TODO: provide visual indication that hard limit was reached for
-              // this parameter
-              // update value field to reflect any bound checking we performed.
-              valueField.setText("" + fVal);
-            }
-          } catch (NumberFormatException e)
-          {
-            System.err.println(e.toString());
-          }
-          if (minValue != null && maxValue != null)
-          {
-            slider.setSliderModel(minValue.floatValue(),
-                    maxValue.floatValue(), fVal);
-          }
-          else
-          {
-            slider.setVisible(false);
-          }
+          slider.setVisible(false);
         }
+        return Integer.valueOf(iVal);
       }
-      else
+      if (isLogarithmicParameter)
       {
-        if (!choice)
+        double dVal = 0d;
+        try
+        {
+          double eValue = Double.valueOf(valueField.getText());
+          dVal = Math.log(eValue);
+        } catch (Exception e)
+        {
+          // shouldn't be possible here
+          valueField.setBackground(Color.yellow);
+          return Double.NaN;
+        }
+        if (validator.getMin() != null && validator.getMax() != null)
+        {
+          double scaleMin = Math.log(validator.getMin().doubleValue())
+                  * sliderScaleFactor;
+          double scaleMax = Math.log(validator.getMax().doubleValue())
+                  * sliderScaleFactor;
+          slider.getModel().setRangeProperties(
+                  (int) (sliderScaleFactor * dVal), 1,
+                  (int) scaleMin, 1 + (int) scaleMax, true);
+        }
+        else
         {
           slider.setVisible(false);
         }
+        return Double.valueOf(dVal);
       }
+      float fVal = 0f;
+      try
+      {
+        fVal = Float.valueOf(valueField.getText());
+      } catch (Exception e)
+      {
+        return Float.valueOf(0f); // shouldn't happen
+      }
+      if (validator.getMin() != null && validator.getMax() != null)
+      {
+        float scaleMin = validator.getMin().floatValue()
+                * sliderScaleFactor;
+        float scaleMax = validator.getMax().floatValue()
+                * sliderScaleFactor;
+        slider.getModel().setRangeProperties(
+                (int) (fVal * sliderScaleFactor), 1, (int) scaleMin,
+                1 + (int) scaleMax, true);
+      }
+      else
+      {
+        slider.setVisible(false);
+      }
+      return Float.valueOf(fVal);
     }
   }
 
-  public static final int PARAM_WIDTH = 340;
-
-  public static final int PARAM_HEIGHT = 150;
-
-  public static final int PARAM_CLOSEDHEIGHT = 80;
-
-  public OptsAndParamsPage(OptsParametersContainerI paramContainer)
-  {
-    this(paramContainer, false);
-  }
+  /**
+   * Constructor with the option to show 'compact' format (parameter description
+   * as tooltip) or 'expanded' format (parameter description in a textbox which
+   * may be opened or closed). Use compact for simple description text, expanded
+   * for more wordy or formatted text.
+   * 
+   * @param paramContainer
+   */
 
   public OptsAndParamsPage(OptsParametersContainerI paramContainer,
           boolean compact)
@@ -813,11 +1069,6 @@ public class OptsAndParamsPage
     mnu.show(invoker, x, y);
   }
 
-  URL linkImageURL = getClass().getResource("/images/link.gif");
-
-  Map<String, OptionBox> optSet = new java.util.LinkedHashMap<>();
-
-  Map<String, ParamBox> paramSet = new java.util.LinkedHashMap<>();
 
   public Map<String, OptionBox> getOptSet()
   {
@@ -839,7 +1090,6 @@ public class OptsAndParamsPage
     this.paramSet = paramSet;
   }
 
-  OptsParametersContainerI poparent;
 
   OptionBox addOption(OptionI opt)
   {
@@ -912,7 +1162,9 @@ public class OptsAndParamsPage
   }
 
   /**
-   * recover options and parameters from GUI
+   * Answers a list of arguments representing all the options and arguments
+   * selected on the dialog, holding their chosen or input values. Optional
+   * parameters which were not selected are not included.
    * 
    * @return
    */
@@ -921,7 +1173,7 @@ public class OptsAndParamsPage
     List<ArgumentI> argSet = new ArrayList<>();
     for (OptionBox opts : getOptSet().values())
     {
-      OptionI opt = opts.getOptionIfEnabled();
+      ArgumentI opt = opts.getSelectedOption();
       if (opt != null)
       {
         argSet.add(opt);
@@ -929,7 +1181,7 @@ public class OptsAndParamsPage
     }
     for (ParamBox parambox : getParamSet().values())
     {
-      ParameterI parm = parambox.getParameter();
+      ArgumentI parm = parambox.getParameter();
       if (parm != null)
       {
         argSet.add(parm);
@@ -939,4 +1191,57 @@ public class OptsAndParamsPage
     return argSet;
   }
 
+  /**
+   * A helper method that constructs and returns a CombBox for choice of the
+   * possible option values. If display names are provided, then these are added
+   * as options, otherwise the actual values are added.
+   * 
+   * @param opt
+   * @return
+   */
+  protected static JComboBox<Object> buildComboBox(OptionI opt)
+  {
+    JComboBox<Object> cb = null;
+    List<String> displayNames = opt.getDisplayNames();
+    if (displayNames != null)
+    {
+      List<Object> displayNamesObjects = new ArrayList<>();
+      displayNamesObjects.addAll(displayNames);
+      cb = JvSwingUtils.buildComboWithTooltips(displayNamesObjects,
+              opt.getPossibleValues());
+    }
+    else
+    {
+      cb = new JComboBox<>();
+      for (String v : opt.getPossibleValues())
+      {
+        cb.addItem(v);
+      }
+    }
+    return cb;
+  }
+
+  /**
+   * Answers the value corresponding to the selected item in the choice combo
+   * box. Note that this returns the underlying value even if a different
+   * display name is used in the combo box.
+   * 
+   * @return
+   */
+  protected static String getSelectedValue(OptionI opt, int sel)
+  {
+    List<String> possibleValues = opt.getPossibleValues();
+    String value = null;
+    if (possibleValues != null && possibleValues.size() == 1)
+    {
+      // Hack to make sure the default value for an enabled option with only
+      // one value is actually returned even if this.val is not displayed
+      value = possibleValues.get(0);
+    }
+    else if (sel >= 0 && sel < possibleValues.size())
+    {
+      value = possibleValues.get(sel);
+    }
+    return value;
+  }
 }
index adde919..5da4ae0 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.api.AlignViewportI;
 import jalview.bin.Cache;
 import jalview.renderer.OverviewRenderer;
 import jalview.util.MessageManager;
@@ -63,7 +64,7 @@ public class OverviewPanel extends JPanel
 
   private OverviewCanvas oviewCanvas;
 
-  protected AlignViewport av;
+  private AlignViewportI av;
 
   private AlignmentPanel ap;
 
@@ -73,34 +74,40 @@ public class OverviewPanel extends JPanel
 
   protected boolean draggingBox = false;
 
+  private Dimension dim;
+  
+  private boolean showProgress = !Platform.isJS();
   protected ProgressPanel progressPanel;
 
+  
   /**
-   * Creates a new OverviewPanel object.
-   * 
-   * @param alPanel
-   *          The alignment panel which is shown in the overview panel
+   * Creates the appropriate type of OverviewDimensions, with the desired size
    */
-  public OverviewPanel(AlignmentPanel alPanel)
+  private void createOverviewDimensions()
   {
-    this.av = alPanel.av;
-    this.ap = alPanel;
-
-    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
-            false);
+    boolean showAnnotation = (av.isShowAnnotation()
+            && av.getAlignmentConservationAnnotation() != null);
     if (showHidden)
     {
-      od = new OverviewDimensionsShowHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
+      od = new OverviewDimensionsShowHidden(av.getRanges(), showAnnotation,
+              dim);
     }
     else
     {
-      od = new OverviewDimensionsHideHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
+      od = new OverviewDimensionsHideHidden(av.getRanges(), showAnnotation,
+              dim);
     }
+  }
+
+  public OverviewPanel(AlignmentPanel alPanel, Dimension dim)
+  {
+    this.av = alPanel.av;
+    this.ap = alPanel;
+    this.dim = dim;
 
+    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
+            false);
+    createOverviewDimensions();
     setLayout(new BorderLayout());
     progressPanel = new ProgressPanel(OverviewRenderer.UPDATE,
             MessageManager.getString("label.oview_calc"), getWidth());
@@ -113,7 +120,7 @@ public class OverviewPanel extends JPanel
 
     // without this the overview window does not size to fit the overview canvas
     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
-
+    
     addComponentListener(new ComponentAdapter()
     {
       @Override
@@ -195,39 +202,37 @@ public class OverviewPanel extends JPanel
       @Override
       public void mousePressed(MouseEvent evt)
       {
-
-        if (Platform.isWinRightButton(evt))
-        {
-          showPopupMenu(evt);
-          return;
-        }
-        if (SwingUtilities.isRightMouseButton(evt))
-        {
-          return;
+         
+       if (Platform.isWinRightButton(evt)) {
+               showPopupMenu(evt);
+               return;
+       }
+        if (SwingUtilities.isRightMouseButton(evt)) {
+               return;
         }
-        // don't do anything if the mouse press is in the overview's box
-        // (wait to see if it's a drag instead)
-        // otherwise update the viewport
-        if (!od.isPositionInBox(evt.getX(), evt.getY()))
-        {
-          draggingBox = false;
+          // don't do anything if the mouse press is in the overview's box
+          // (wait to see if it's a drag instead)
+          // otherwise update the viewport
+          if (!od.isPositionInBox(evt.getX(), evt.getY()))
+          {
+            draggingBox = false;
 
-          // display drag cursor at mouse position
-          setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+            // display drag cursor at mouse position
+            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
 
-          od.updateViewportFromMouse(evt.getX(), evt.getY(),
-                  av.getAlignment().getHiddenSequences(),
-                  av.getAlignment().getHiddenColumns());
-          getParent().setCursor(
-                  Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        }
-        else
-        {
-          draggingBox = true;
-          od.setDragPoint(evt.getX(), evt.getY(),
-                  av.getAlignment().getHiddenSequences(),
-                  av.getAlignment().getHiddenColumns());
-        }
+            od.updateViewportFromMouse(evt.getX(), evt.getY(),
+                    av.getAlignment().getHiddenSequences(),
+                    av.getAlignment().getHiddenColumns());
+            getParent().setCursor(
+                    Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+          }
+          else
+          {
+            draggingBox = true;
+            od.setDragPoint(evt.getX(), evt.getY(),
+                    av.getAlignment().getHiddenSequences(),
+                    av.getAlignment().getHiddenColumns());
+          }
       }
 
       @Override
@@ -287,20 +292,8 @@ public class OverviewPanel extends JPanel
    */
   protected void toggleHiddenColumns()
   {
-    if (showHidden)
-    {
-      showHidden = false;
-      od = new OverviewDimensionsHideHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
-    }
-    else
-    {
-      showHidden = true;
-      od = new OverviewDimensionsShowHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
-    }
+    showHidden = !showHidden;
+    createOverviewDimensions();
     oviewCanvas.resetOviewDims(od);
     updateOverviewImage();
     setBoxPosition();
@@ -324,7 +317,7 @@ public class OverviewPanel extends JPanel
       od.setWidth(getWidth());
       od.setHeight(getHeight() - progressPanel.getHeight());
     }
-
+    
     setPreferredSize(new Dimension(od.getWidth(),
             od.getHeight() + progressPanel.getHeight()));
 
@@ -337,6 +330,7 @@ public class OverviewPanel extends JPanel
     thread.start();
     repaint();
 
+    
   }
 
   @Override
@@ -407,9 +401,8 @@ public class OverviewPanel extends JPanel
        * close the parent frame (which also removes it from the
        * Desktop Windows menu)
        */
-      ((JInternalFrame) SwingUtilities
-              .getAncestorOfClass(JInternalFrame.class, (this)))
-                      .setClosed(true);
+      ((JInternalFrame) SwingUtilities.getAncestorOfClass(
+              JInternalFrame.class, (this))).setClosed(true);
     } catch (PropertyVetoException e)
     {
       // ignore
index e6b6b83..000dd76 100644 (file)
@@ -36,6 +36,7 @@ import jalview.jbgui.GPCAPanel;
 import jalview.math.RotatableMatrix.Axis;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.PCAModel;
 
@@ -206,10 +207,7 @@ public class PCAPanel extends GPCAPanel
     repaint();
     if (getParent() == null)
     {
-      Desktop.addInternalFrame(this,
-              MessageManager.formatMessage("label.calc_title", "PCA",
-                      getPcaModel().getScoreModelName()),
-              475, 450);
+      addToDesktop(this, getPcaModel().getScoreModelName());
       this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
     }
     working = false;
@@ -615,6 +613,12 @@ public class PCAPanel extends GPCAPanel
     // // setMenusForViewport();
     // validate();
   }
+  
+  @Override
+  public void removeProgressBar(long id)
+  {
+    progressBar.removeProgressBar(id);
+  }
 
   @Override
   public void registerHandler(final long id,
@@ -769,4 +773,12 @@ public class PCAPanel extends GPCAPanel
     getRotatableCanvas().ap = panel;
     PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId());
   }
+
+  public static void addToDesktop(PCAPanel panel, String modelName)
+  {
+    Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450);
+    Desktop.addInternalFrame(panel, MessageManager.formatMessage(
+            "label.calc_title", "PCA", modelName), dim.width,
+            dim.height);
+  }
 }
index 953fdc5..7e203b2 100755 (executable)
@@ -39,6 +39,9 @@ import java.util.Map;
  */
 public class PaintRefresher
 {
+  private static final int ALIGNMENT_CHANGED = 1 << 0;
+  private static final int VALIDATE_SEQUENCES = 1 << 1;
+  
   static Map<String, List<Component>> components = new HashMap<>();
 
   /**
@@ -101,25 +104,32 @@ public class PaintRefresher
   {
     List<Component> comps = components.get(id);
 
+    int mode = (alignmentChanged ? ALIGNMENT_CHANGED : 0) | (validateSequences ? VALIDATE_SEQUENCES : 0);
     if (comps == null)
     {
       return;
     }
+    repaintComponents(source, mode, comps.toArray(new Component[comps.size()]));
+  }
 
-    for (Component comp : comps)
+  public static void repaintComponents(Component source, int mode,
+          Component... comps)
+  {
+    for (int i = 0; i < comps.length; i++)
     {
-      if (comp == source)
+      Component comp = comps[i];
+      if (comp == null)
       {
         continue;
       }
       if (comp instanceof AlignmentPanel)
       {
-        if (validateSequences && source instanceof AlignmentPanel)
+        if ((mode & VALIDATE_SEQUENCES) != 0 && source instanceof AlignmentPanel)
         {
           validateSequences(((AlignmentPanel) source).av.getAlignment(),
                   ((AlignmentPanel) comp).av.getAlignment());
         }
-        if (alignmentChanged)
+        if ((mode & ALIGNMENT_CHANGED) != 0)
         {
           ((AlignmentPanel) comp).alignmentChanged();
         }
@@ -128,13 +138,13 @@ public class PaintRefresher
       {
         // BH 2019.04.22 fixes JS problem of repaint() consolidation
         // that occurs in JavaScript but not Java [JAL-3226]
-        ((IdCanvas) comp).fastPaint = false;
+        ((IdCanvas) comp).setNoFastPaint();
       }
       else if (comp instanceof SeqCanvas)
       {
         // BH 2019.04.22 fixes JS problem of repaint() consolidation
         // that occurs in JavaScript but not Java [JAL-3226]
-        ((SeqCanvas) comp).fastPaint = false;
+        ((SeqCanvas) comp).setNoFastPaint();
       }
       comp.repaint();
     }
@@ -262,4 +272,5 @@ public class PaintRefresher
     return tmp.toArray(new AlignmentPanel[tmp.size()]);
   }
 
+  
 }
index c4b5367..f33b2d6 100755 (executable)
 package jalview.gui;
 
 import jalview.analysis.AlignSeq;
+import jalview.api.AlignViewportI;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GPairwiseAlignPanel;
 import jalview.util.MessageManager;
-import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.event.ActionEvent;
 import java.util.Vector;
@@ -43,7 +43,7 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
 
   private static final String DASHES = "---------------------\n";
 
-  AlignmentViewport av;
+  AlignViewportI av;
 
   Vector<SequenceI> sequences;
 
@@ -51,14 +51,13 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel
    * Creates a new PairwiseAlignPanel object.
    * 
    * @param viewport
-   *          DOCUMENT ME!
    */
-  public PairwiseAlignPanel(AlignmentViewport viewport)
+  public PairwiseAlignPanel(AlignViewportI viewport)
   {
     super();
     this.av = viewport;
 
-    sequences = new Vector<SequenceI>();
+    sequences = new Vector<>();
 
     SequenceGroup selectionGroup = viewport.getSelectionGroup();
     boolean isSelection = selectionGroup != null
index 6903034..3c32180 100644 (file)
@@ -66,11 +66,13 @@ import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.MappedFeatures;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.ResidueCount;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
 import jalview.gui.JalviewColourChooser.ColourChooserListener;
+import jalview.io.CountReader;
 import jalview.io.FileFormatI;
 import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
@@ -89,6 +91,8 @@ import jalview.util.StringUtils;
 import jalview.util.UrlLink;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
+import java.io.IOException;
+import java.net.MalformedURLException;
 /**
  * The popup menu that is displayed on right-click on a sequence id, or in the
  * sequence alignment.
@@ -303,7 +307,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       jalview.util.BrowserLauncher.openURL(url);
     } catch (Exception ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.getString("label.web_browser_not_found_unix"),
               MessageManager.getString("label.web_browser_not_found"),
               JvOptionPane.WARNING_MESSAGE);
@@ -359,15 +363,14 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   }
 
   /**
-   * Constructor for a PopupMenu for a click in the alignment panel (on a
-   * residue)
+   * Constructor for a PopupMenu for a click in the alignment panel (on a residue)
    * 
    * @param ap
-   *          the panel in which the mouse is clicked
+   *              the panel in which the mouse is clicked
    * @param seq
-   *          the sequence under the mouse
+   *              the sequence under the mouse
    * @throws NullPointerException
-   *           if seq is null
+   *                                if seq is null
    */
   public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column)
   {
@@ -378,13 +381,13 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * Constructor for a PopupMenu for a click in the sequence id panel
    * 
    * @param alignPanel
-   *          the panel in which the mouse is clicked
+   *                     the panel in which the mouse is clicked
    * @param seq
-   *          the sequence under the mouse click
+   *                     the sequence under the mouse click
    * @param groupLinks
-   *          templates for sequence external links
+   *                     templates for sequence external links
    * @throws NullPointerException
-   *           if seq is null
+   *                                if seq is null
    */
   public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq,
           List<String> groupLinks)
@@ -400,10 +403,11 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * @param alignPanel
    * @param seq
    * @param column
-   *          aligned column position (0...)
+   *                      aligned column position (0...)
    * @param groupLinks
    */
-  private PopupMenu(boolean fromIdPanel, final AlignmentPanel alignPanel,
+  private PopupMenu(boolean fromIdPanel,
+          final AlignmentPanel alignPanel,
           final SequenceI seq, final int column, List<String> groupLinks)
   {
     Objects.requireNonNull(seq);
@@ -443,10 +447,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     /*
      * And repeat for the current selection group (if there is one):
      */
-    final List<SequenceI> selectedGroup = (alignPanel.av
-            .getSelectionGroup() == null
-                    ? Collections.<SequenceI> emptyList()
-                    : alignPanel.av.getSelectionGroup().getSequences());
+    final List<SequenceI> selectedGroup = (alignPanel.av.getSelectionGroup() == null
+            ? Collections.<SequenceI> emptyList()
+            : alignPanel.av.getSelectionGroup().getSequences());
     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
             groupHideAnnotationsMenu, selectedGroup);
     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
@@ -548,6 +551,35 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         }
       }
 
+      if (seq.hasHMMProfile())
+      {
+        menuItem = new JMenuItem(MessageManager
+                .getString("action.add_background_frequencies"));
+        menuItem.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            try
+            {
+              ResidueCount counts = CountReader.getBackgroundFrequencies(ap,
+                      seq);
+              if (counts != null)
+              {
+                seq.getHMM().setBackgroundFrequencies(counts);
+                ap.alignFrame.buildColourMenu();
+              }
+            } catch (MalformedURLException e1)
+            {
+              e1.printStackTrace();
+            } catch (IOException e1)
+            {
+              e1.printStackTrace();
+            }
+          }
+        });
+        add(menuItem);
+      }
       menuItem = new JMenuItem(
               MessageManager.getString("action.hide_sequences"));
       menuItem.addActionListener(new ActionListener()
@@ -671,9 +703,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       {
         buildGroupURLMenu(sg, groupLinks);
       }
+      // TODO REMOVE FOR 2.12 ?
       // Add a 'show all structures' for the current selection
-      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(),
-              reppdb = new Hashtable<>();
+      Hashtable<String, PDBEntry> pdbe = new Hashtable<>();
+      Hashtable<String, PDBEntry> reppdb = new Hashtable<>();
 
       SequenceI sqass = null;
       for (SequenceI sq : alignPanel.av.getSequenceSelection())
@@ -710,8 +743,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     {
       createGroupMenuItem.setVisible(true);
       unGroupMenuItem.setVisible(false);
-      editGroupMenu
-              .setText(MessageManager.getString("action.edit_new_group"));
+      editGroupMenu.setText(MessageManager.getString("action.edit_new_group"));
     }
     else
     {
@@ -738,8 +770,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * <li>positional feature links (alignment panel popup menu)</li>
    * <li>feature details links (alignment panel popup menu)</li>
    * </ul>
-   * If this panel is also showed complementary (CDS/protein) features, then
-   * links to their feature details are also added.
+   * If this panel is also showed complementary (CDS/protein) features, then links
+   * to their feature details are also added.
    * 
    * @param seq
    * @param column
@@ -1147,8 +1179,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
           if (((String[]) sarray[1])[sq] == null)
           {
-            if (!e.hasMap()
-                    || (e.getMap().locateMappedRange(start, end) != null))
+            if (!e.hasMap() || (e.getMap()
+                    .locateMappedRange(start, end) != null))
             {
               ((String[]) sarray[1])[sq] = e.getAccessionId();
               ((int[]) sarray[0])[0]++;
@@ -1787,8 +1819,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         }
         inserts.and(sq.getInsertionsAsBits());
       }
-      hidden.clearAndHideColumns(inserts,
-              ap.av.getSelectionGroup().getStartRes(),
+      hidden.clearAndHideColumns(inserts, ap.av.getSelectionGroup().getStartRes(),
               ap.av.getSelectionGroup().getEndRes());
     }
 
@@ -2018,8 +2049,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   /**
    * Shows a dialog where the sequence name and description may be edited. If a
-   * name containing spaces is entered, these are converted to underscores, with
-   * a warning message.
+   * name containing spaces is entered, these are converted to underscores, with a
+   * warning message.
    */
   void sequenceName_actionPerformed()
   {
@@ -2027,8 +2058,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
             sequence.getDescription(),
             MessageManager.getString("label.sequence_name"),
             MessageManager.getString("label.sequence_description"));
-    dialog.showDialog(ap.alignFrame, MessageManager.getString(
-            "label.edit_sequence_name_description"), new Runnable()
+    dialog.showDialog(ap.alignFrame,
+            MessageManager.getString(
+                    "label.edit_sequence_name_description"),
+            new Runnable()
             {
               @Override
               public void run()
@@ -2048,8 +2081,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                   ap.paintAlignment(false, false);
                 }
                 sequence.setDescription(dialog.getDescription());
-                ap.av.firePropertyChange("alignment", null,
-                        ap.av.getAlignment().getSequences());
+                ap.av.notifyAlignment();
               }
             });
   }
@@ -2080,7 +2112,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    */
   protected void outline_actionPerformed()
   {
-    String title = MessageManager.getString("label.select_outline_colour");
+    String title = MessageManager
+            .getString("label.select_outline_colour");
     ColourChooserListener listener = new ColourChooserListener()
     {
       @Override
@@ -2090,8 +2123,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         refresh();
       }
     };
-    JalviewColourChooser.showColourChooser(Desktop.getDesktop(), title,
-            Color.BLUE, listener);
+    JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(),
+            title, Color.BLUE, listener);
   }
 
   /**
@@ -2179,9 +2212,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
               startEnd, caseChange);
 
       ap.alignFrame.addHistoryItem(caseCommand);
+      ap.av.notifyAlignment();
 
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
 
     }
   }
@@ -2288,8 +2320,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                           sg.getStartRes(), sg.getEndRes() + 1,
                           ap.av.getAlignment());
                   ap.alignFrame.addHistoryItem(editCommand);
-                  ap.av.firePropertyChange("alignment", null,
-                          ap.av.getAlignment().getSequences());
+                  ap.av.notifyAlignment();
                 }
               });
     }
index 06d3a60..19d929e 100755 (executable)
@@ -27,6 +27,9 @@ import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.MouseEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
@@ -34,8 +37,10 @@ import java.util.concurrent.CompletableFuture;
 
 import javax.help.HelpSetException;
 import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
+import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
 import javax.swing.RowFilter;
 import javax.swing.RowSorter;
@@ -49,9 +54,12 @@ import javax.swing.table.TableColumn;
 import javax.swing.table.TableModel;
 import javax.swing.table.TableRowSorter;
 
-//import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import jalview.hmmer.HmmerCommand;
+import jalview.util.FileUtils;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.bin.MemorySetting;
@@ -83,51 +91,154 @@ import jalview.ws.sifts.SiftsSettings;
  * @author $author$
  * @version $Revision$
  */
-/*
- * for merge with Jalview-JS
- public class Preferences extends GPreferences implements ApplicationSingletonI
- */
-public class Preferences extends GPreferences
+public class Preferences extends GPreferences implements ApplicationSingletonI
 {
-  public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
+  // suggested list delimiter character
+  public static final String COMMA = ",";
 
-  public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
-
-  public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
+  public static final String HMMSEARCH_SEQCOUNT = "HMMSEARCH_SEQCOUNT";
 
-  public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
+  public static final String HMMINFO_GLOBAL_BACKGROUND = "HMMINFO_GLOBAL_BACKGROUND";
 
-  public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
+  public static final String HMMALIGN_TRIM_TERMINI = "HMMALIGN_TRIM_TERMINI";
+  
+  public static final String ADD_SS_ANN = "ADD_SS_ANN";
 
   public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
 
-  public static final String ADD_SS_ANN = "ADD_SS_ANN";
+  public static final String ALLOW_UNPUBLISHED_PDB_QUERYING = "ALLOW_UNPUBLISHED_PDB_QUERYING";
 
-  public static final String USE_RNAVIEW = "USE_RNAVIEW";
+  public static final String ANNOTATIONCOLOUR_MAX = "ANNOTATIONCOLOUR_MAX";
 
-  public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
+  public static final String ANNOTATIONCOLOUR_MIN = "ANNOTATIONCOLOUR_MIN";
 
-  public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
+  public static final String ANTI_ALIAS = "ANTI_ALIAS";
+
+  public static final String AUTO_CALC_CONSENSUS = "AUTO_CALC_CONSENSUS";
+
+  public static final String AUTOASSOCIATE_PDBANDSEQS = "AUTOASSOCIATE_PDBANDSEQS";
+
+  public static final String BLOSUM62_PCA_FOR_NUCLEOTIDE = "BLOSUM62_PCA_FOR_NUCLEOTIDE";
+
+  public static final String CENTRE_COLUMN_LABELS = "CENTRE_COLUMN_LABELS";
 
   public static final String CHIMERA_PATH = "CHIMERA_PATH";
 
   public static final String CHIMERAX_PATH = "CHIMERAX_PATH";
 
+  public static final String DBREFFETCH_USEPICR = "DBREFFETCH_USEPICR";
+
+  public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
+
+  public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
+  public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
+
+  public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
+
+  public static final String FIGURE_AUTOIDWIDTH = "FIGURE_AUTOIDWIDTH";
+
+  public static final String FIGURE_FIXEDIDWIDTH = "FIGURE_FIXEDIDWIDTH";
+
+  public static final String FOLLOW_SELECTIONS = "FOLLOW_SELECTIONS";
+
+  public static final String FONT_NAME = "FONT_NAME";
+
+  public static final String FONT_SIZE = "FONT_SIZE";
+
+  public static final String FONT_STYLE = "FONT_STYLE";
+  
+  public static final String HMMER_PATH = "HMMER_PATH";
+
+  public static final String CYGWIN_PATH = "CYGWIN_PATH";
+
+  public static final String HMMSEARCH_DBS = "HMMSEARCH_DBS";
+
+  public static final String GAP_COLOUR = "GAP_COLOUR";
+
+  public static final String GAP_SYMBOL = "GAP_SYMBOL";
+
+  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
+
+  public static final String HIDE_INTRONS = "HIDE_INTRONS";
+
+  public static final String ID_ITALICS = "ID_ITALICS";
+
+  public static final String ID_ORG_HOSTURL = "ID_ORG_HOSTURL";
+
+  public static final String MAP_WITH_SIFTS = "MAP_WITH_SIFTS";
+
+  public static final String NOQUESTIONNAIRES = "NOQUESTIONNAIRES";
+
+  public static final String NORMALISE_CONSENSUS_LOGO = "NORMALISE_CONSENSUS_LOGO";
+
+  public static final String NORMALISE_LOGO = "NORMALISE_LOGO";
+
+  public static final String PAD_GAPS = "PAD_GAPS";
+
+  public static final String PDB_DOWNLOAD_FORMAT = "PDB_DOWNLOAD_FORMAT";
   public static final String PYMOL_PATH = "PYMOL_PATH";
 
-  public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
+  public static final String QUESTIONNAIRE = "QUESTIONNAIRE";
+
+  public static final String RELAXEDSEQIDMATCHING = "RELAXEDSEQIDMATCHING";
+
+  public static final String RIGHT_ALIGN_IDS = "RIGHT_ALIGN_IDS";
+
+  public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
+
+  public static final String SHOW_ANNOTATIONS = "SHOW_ANNOTATIONS";
 
   public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
 
+  public static final String SHOW_CONSENSUS = "SHOW_CONSENSUS";
+
+  public static final String SHOW_CONSENSUS_HISTOGRAM = "SHOW_CONSENSUS_HISTOGRAM";
+
+  public static final String SHOW_CONSENSUS_LOGO = "SHOW_CONSENSUS_LOGO";
+
+  public static final String SHOW_CONSERVATION = "SHOW_CONSERVATION";
+
+  public static final String SHOW_DBREFS_TOOLTIP = "SHOW_DBREFS_TOOLTIP";
+
+  public static final String SHOW_GROUP_CONSENSUS = "SHOW_GROUP_CONSENSUS";
+
+  public static final String SHOW_GROUP_CONSERVATION = "SHOW_GROUP_CONSERVATION";
+
+  public static final String SHOW_JVSUFFIX = "SHOW_JVSUFFIX";
+
+  public static final String SHOW_NPFEATS_TOOLTIP = "SHOW_NPFEATS_TOOLTIP";
   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
 
   public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
 
+  public static final String SHOW_OVERVIEW = "SHOW_OVERVIEW";
+
+  public static final String SHOW_QUALITY = "SHOW_QUALITY";
+
+  public static final String SHOW_UNCONSERVED = "SHOW_UNCONSERVED";
+
+  public static final String SORT_ALIGNMENT = "SORT_ALIGNMENT";
+
+  public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
+
+  public static final String SORT_BY_TREE = "SORT_BY_TREE";
+
+  public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
+
+  public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
+
+  public static final String STRUCTURE_DIMENSIONS = "STRUCTURE_DIMENSIONS";
+
+  public static final String UNIPROT_DOMAIN = "UNIPROT_DOMAIN";
+
+  public static final String USE_FULL_SO = "USE_FULL_SO";
   public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
 
-  public static final String GAP_COLOUR = "GAP_COLOUR";
+  public static final String USE_RNAVIEW = "USE_RNAVIEW";
 
-  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
+  public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
+
+  public static final String WRAP_ALIGNMENT = "WRAP_ALIGNMENT";
 
   private static final int MIN_FONT_SIZE = 1;
 
@@ -147,10 +258,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;
@@ -186,6 +297,7 @@ public class Preferences extends GPreferences
 
   private WsPreferences wsPrefs;
 
+  private SlivkaPreferences slivkaPrefs;
   private OptionsParam promptEachTimeOpt = new OptionsParam(
           MessageManager.getString("label.prompt_each_time"),
           "Prompt each time");
@@ -260,6 +372,10 @@ public class Preferences extends GPreferences
       wsPrefs = new WsPreferences();
       wsTab.add(wsPrefs, BorderLayout.CENTER);
     }
+
+    slivkaPrefs = new SlivkaPreferences();
+    slivkaTab.add(slivkaPrefs, BorderLayout.CENTER);
+   
     int width = 500, height = 450;
     if (Platform.isAMacAndNotJS())
     {
@@ -272,6 +388,63 @@ public class Preferences extends GPreferences
     frame.setMinimumSize(new Dimension(width, height));
 
     /*
+     * Set HMMER tab defaults
+     */
+    hmmrTrimTermini.setSelected(Cache.getDefault(HMMALIGN_TRIM_TERMINI, false));
+    if (Cache.getDefault(HMMINFO_GLOBAL_BACKGROUND, false))
+    {
+      hmmerBackgroundUniprot.setSelected(true);
+    }
+    else
+    {
+      hmmerBackgroundAlignment.setSelected(true);
+    }
+    hmmerSequenceCount
+            .setText(Cache.getProperty(HMMSEARCH_SEQCOUNT));
+    hmmerPath.setText(Cache.getProperty(HMMER_PATH));
+    hmmerPath.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        validateHmmerPath();
+      }
+    });
+    hmmerPath.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        validateHmmerPath();
+      }
+    });
+    if (cygwinPath != null)
+    {
+      String path = Cache.getProperty(CYGWIN_PATH);
+      if (path == null)
+      {
+        path = FileUtils.getPathTo("bash");
+      }
+      cygwinPath.setText(path);
+      cygwinPath.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          validateCygwinPath();
+        }
+      });
+      cygwinPath.addFocusListener(new FocusAdapter()
+      {
+        @Override
+        public void focusLost(FocusEvent e)
+        {
+          validateCygwinPath();
+        }
+      });
+    }
+
+    /*
      * Set Visual tab defaults
      */
     seqLimit.setSelected(Cache.getDefault("SHOW_JVSUFFIX", true));
@@ -294,6 +467,9 @@ public class Preferences extends GPreferences
             Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true));
     showConsensLogo
             .setSelected(Cache.getDefault("SHOW_CONSENSUS_LOGO", false));
+    showInformationHistogram.setSelected(
+            Cache.getDefault("SHOW_INFORMATION_HISTOGRAM", true));
+    showHMMLogo.setSelected(Cache.getDefault("SHOW_HMM_LOGO", false));
     showNpTooltip
             .setSelected(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
     showDbRefTooltip
@@ -408,8 +584,13 @@ public class Preferences extends GPreferences
      * set choice of structure viewer, and path if saved as a preference;
      * default to Jmol (first choice) if an unexpected value is found
      */
-    String viewerType = Cache.getDefault(STRUCTURE_DISPLAY,
+    String viewerType = ViewerType.JMOL.name();
+    if (!Platform.isJS())
+    {
+      Cache.getDefault(STRUCTURE_DISPLAY,
             ViewerType.JMOL.name());
+    }
+    // TODO - disable external viewers for JS
     structViewer.setSelectedItem(viewerType);
     String viewerPath = "";
     ViewerType type = null;
@@ -638,6 +819,8 @@ public class Preferences extends GPreferences
     setCustomProxyEnabled();
     applyProxyButtonEnabled(false);
 
+    defaultBrowser.setText(Cache.getDefault("DEFAULT_BROWSER", ""));
+
     usagestats.setSelected(Cache.getDefault("USAGESTATS", false));
     // note antisense here: default is true
     questionnaire
@@ -679,15 +862,12 @@ public class Preferences extends GPreferences
     annotations_actionPerformed(null); // update the display of the annotation
                                        // settings
 
+    
+    
     /*
      * Set Backups tab defaults
      */
     loadLastSavedBackupsOptions();
-
-    /*
-     * Set Startup tab defaults
-     */
-
   }
 
   /**
@@ -704,6 +884,7 @@ public class Preferences extends GPreferences
     comboBox.addItem(lineArtOpt);
     comboBox.addItem(textOpt);
 
+    
     /*
      * JalviewJS doesn't support Lineart so force it to Text
      */
@@ -745,65 +926,69 @@ public class Preferences extends GPreferences
     /*
      * Save Visual settings
      */
-    Cache.applicationProperties.setProperty("SHOW_JVSUFFIX",
+    Cache.setPropertyNoSave("SHOW_JVSUFFIX",
             Boolean.toString(seqLimit.isSelected()));
-    Cache.applicationProperties.setProperty("RIGHT_ALIGN_IDS",
+    Cache.setPropertyNoSave("RIGHT_ALIGN_IDS",
             Boolean.toString(rightAlign.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_FULLSCREEN",
+    Cache.setPropertyNoSave("SHOW_FULLSCREEN",
             Boolean.toString(fullScreen.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_OVERVIEW",
+    Cache.setPropertyNoSave("SHOW_OVERVIEW",
             Boolean.toString(openoverv.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.toString(annotations.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.toString(conservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
+    Cache.setPropertyNoSave("SHOW_QUALITY",
             Boolean.toString(quality.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.toString(identity.isSelected()));
 
-    Cache.applicationProperties.setProperty("GAP_SYMBOL",
+    Cache.setPropertyNoSave("GAP_SYMBOL",
             gapSymbolCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("FONT_NAME",
+    Cache.setPropertyNoSave("FONT_NAME",
             fontNameCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_STYLE",
+    Cache.setPropertyNoSave("FONT_STYLE",
             fontStyleCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_SIZE",
+    Cache.setPropertyNoSave("FONT_SIZE",
             fontSizeCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("ID_ITALICS",
+    Cache.setPropertyNoSave("ID_ITALICS",
             Boolean.toString(idItalics.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_UNCONSERVED",
+    Cache.setPropertyNoSave("SHOW_UNCONSERVED",
             Boolean.toString(showUnconserved.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OCCUPANCY,
+    Cache.setPropertyNoSave(SHOW_OCCUPANCY,
             Boolean.toString(showOccupancy.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSENSUS",
+    Cache.setPropertyNoSave("SHOW_GROUP_CONSENSUS",
             Boolean.toString(showGroupConsensus.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_GROUP_CONSERVATION",
             Boolean.toString(showGroupConservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_HISTOGRAM",
+    Cache.setPropertyNoSave("SHOW_CONSENSUS_HISTOGRAM",
             Boolean.toString(showConsensHistogram.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_LOGO",
+    Cache.setPropertyNoSave("SHOW_CONSENSUS_LOGO",
             Boolean.toString(showConsensLogo.isSelected()));
-    Cache.applicationProperties.setProperty("ANTI_ALIAS",
+    Cache.setPropertyNoSave("SHOW_INFORMATION_HISTOGRAM",
+            Boolean.toString(showConsensHistogram.isSelected()));
+    Cache.setPropertyNoSave("SHOW_HMM_LOGO",
+            Boolean.toString(showHMMLogo.isSelected()));
+    Cache.setPropertyNoSave("ANTI_ALIAS",
             Boolean.toString(smoothFont.isSelected()));
-    Cache.applicationProperties.setProperty(SCALE_PROTEIN_TO_CDNA,
+    Cache.setPropertyNoSave(SCALE_PROTEIN_TO_CDNA,
             Boolean.toString(scaleProteinToCdna.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_NPFEATS_TOOLTIP",
+    Cache.setPropertyNoSave("SHOW_NPFEATS_TOOLTIP",
             Boolean.toString(showNpTooltip.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_DBREFS_TOOLTIP",
+    Cache.setPropertyNoSave("SHOW_DBREFS_TOOLTIP",
             Boolean.toString(showDbRefTooltip.isSelected()));
 
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT",
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT",
             Boolean.toString(wrap.isSelected()));
 
-    Cache.applicationProperties.setProperty("STARTUP_FILE",
+    Cache.setPropertyNoSave("STARTUP_FILE",
             startupFileTextfield.getText());
-    Cache.applicationProperties.setProperty("SHOW_STARTUP_FILE",
+    Cache.setPropertyNoSave("SHOW_STARTUP_FILE",
             Boolean.toString(startupCheckbox.isSelected()));
 
-    Cache.applicationProperties.setProperty("SORT_ALIGNMENT",
+    Cache.setPropertyNoSave("SORT_ALIGNMENT",
             sortby.getSelectedItem().toString());
 
     // convert description of sort order to enum name for save
@@ -811,48 +996,85 @@ public class Preferences extends GPreferences
             .forDescription(sortAnnBy.getSelectedItem().toString());
     if (annSortOrder != null)
     {
-      Cache.applicationProperties.setProperty(SORT_ANNOTATIONS,
+      Cache.setPropertyNoSave(SORT_ANNOTATIONS,
               annSortOrder.name());
     }
 
     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
-    Cache.applicationProperties.setProperty(SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave(SHOW_AUTOCALC_ABOVE,
             Boolean.valueOf(showAutocalcFirst).toString());
 
     /*
      * Save Colours settings
      */
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_PROT,
             protColour.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_NUC,
             nucColour.getSelectedItem().toString());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
+    Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MIN",
             minColour.getBackground());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MAX",
+    Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MAX",
             maxColour.getBackground());
 
     /*
+     * Save HMMER settings
+     */
+    Cache.setPropertyNoSave(HMMALIGN_TRIM_TERMINI,
+            Boolean.toString(hmmrTrimTermini.isSelected()));
+    Cache.setPropertyNoSave(HMMINFO_GLOBAL_BACKGROUND,
+            Boolean.toString(hmmerBackgroundUniprot.isSelected()));
+    Cache.setPropertyNoSave(HMMSEARCH_SEQCOUNT,
+            hmmerSequenceCount.getText());
+    Cache.setOrRemove(HMMER_PATH, hmmerPath.getText());
+    if (cygwinPath != null)
+    {
+      Cache.setOrRemove(CYGWIN_PATH, cygwinPath.getText());
+    }
+    AlignFrame[] frames = Desktop.getAlignFrames();
+    if (frames != null && frames.length > 0)
+    {
+      for (AlignFrame f : frames)
+      {
+        f.updateHMMERStatus();
+      }
+    }
+    
+    hmmrTrimTermini.setSelected(Cache.getDefault(HMMALIGN_TRIM_TERMINI, false));
+    if (Cache.getDefault(HMMINFO_GLOBAL_BACKGROUND, false))
+    {
+      hmmerBackgroundUniprot.setSelected(true);
+    }
+    else
+    {
+      hmmerBackgroundAlignment.setSelected(true);
+    }
+    hmmerSequenceCount
+            .setText(Cache.getProperty(HMMSEARCH_SEQCOUNT));
+    hmmerPath.setText(Cache.getProperty(HMMER_PATH));
+
+    /*
      * Save Overview settings
      */
-    Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
-    Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
-    Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
+    Cache.setColourPropertyNoSave(GAP_COLOUR, gapColour.getBackground());
+    Cache.setColourPropertyNoSave(HIDDEN_COLOUR, hiddenColour.getBackground());
+    Cache.setPropertyNoSave(USE_LEGACY_GAP,
             Boolean.toString(useLegacyGap.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
+    Cache.setPropertyNoSave(SHOW_OV_HIDDEN_AT_START,
             Boolean.toString(showHiddenAtStart.isSelected()));
 
     /*
      * Save Structure settings
      */
-    Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
+    Cache.setPropertyNoSave(ADD_TEMPFACT_ANN,
             Boolean.toString(addTempFactor.isSelected()));
-    Cache.applicationProperties.setProperty(ADD_SS_ANN,
+    Cache.setPropertyNoSave(ADD_SS_ANN,
             Boolean.toString(addSecondaryStructure.isSelected()));
-    Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
+    Cache.setPropertyNoSave(STRUCT_FROM_PDB,
             Boolean.toString(structFromPdb.isSelected()));
+    if (!Platform.isJS()) {
     String viewer = structViewer.getSelectedItem().toString();
     String viewerPath = structureViewerPath.getText();
-    Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY, viewer);
+    Cache.setPropertyNoSave(STRUCTURE_DISPLAY, viewer);
     if (viewer.equals(ViewerType.CHIMERA.name()))
     {
       Cache.setOrRemove(CHIMERA_PATH, viewerPath);
@@ -865,49 +1087,53 @@ public class Preferences extends GPreferences
     {
       Cache.setOrRemove(PYMOL_PATH, viewerPath);
     }
-    Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
+    } // nojs
+    Cache.setPropertyNoSave("MAP_WITH_SIFTS",
             Boolean.toString(siftsMapping.isSelected()));
     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
 
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("EPS_RENDERING",
+    Cache.setPropertyNoSave("EPS_RENDERING",
             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("HTML_RENDERING",
+    Cache.setPropertyNoSave("HTML_RENDERING",
             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("SVG_RENDERING",
+    Cache.setPropertyNoSave("SVG_RENDERING",
             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
 
     /*
      * Save Connections settings
      */
-    // Proxy settings set first (to catch web services)
+    // Proxy settings were already set first (to catch web services)
+    Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
+
+    jalview.util.BrowserLauncher.resetBrowser();
 
     // save user-defined and selected links
     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
     if (menuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("SEQUENCE_LINKS");
+      Cache.removePropertyNoSave("SEQUENCE_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
+      Cache.setPropertyNoSave("SEQUENCE_LINKS",
               menuLinks.toString());
     }
 
     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
     if (nonMenuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("STORED_LINKS");
+      Cache.removePropertyNoSave("STORED_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("STORED_LINKS",
+      Cache.setPropertyNoSave("STORED_LINKS",
               nonMenuLinks.toString());
     }
 
-    Cache.applicationProperties.setProperty("DEFAULT_URL",
+    Cache.setPropertyNoSave("DEFAULT_URL",
             sequenceUrlLinks.getPrimaryUrlId());
 
     Cache.setProperty("VERSION_CHECK",
@@ -932,40 +1158,40 @@ public class Preferences extends GPreferences
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("BLC_JVSUFFIX",
+    Cache.setPropertyNoSave("BLC_JVSUFFIX",
             Boolean.toString(blcjv.isSelected()));
-    Cache.applicationProperties.setProperty("CLUSTAL_JVSUFFIX",
+    Cache.setPropertyNoSave("CLUSTAL_JVSUFFIX",
             Boolean.toString(clustaljv.isSelected()));
-    Cache.applicationProperties.setProperty("FASTA_JVSUFFIX",
+    Cache.setPropertyNoSave("FASTA_JVSUFFIX",
             Boolean.toString(fastajv.isSelected()));
-    Cache.applicationProperties.setProperty("MSF_JVSUFFIX",
+    Cache.setPropertyNoSave("MSF_JVSUFFIX",
             Boolean.toString(msfjv.isSelected()));
-    Cache.applicationProperties.setProperty("PFAM_JVSUFFIX",
+    Cache.setPropertyNoSave("PFAM_JVSUFFIX",
             Boolean.toString(pfamjv.isSelected()));
-    Cache.applicationProperties.setProperty("PILEUP_JVSUFFIX",
+    Cache.setPropertyNoSave("PILEUP_JVSUFFIX",
             Boolean.toString(pileupjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_JVSUFFIX",
+    Cache.setPropertyNoSave("PIR_JVSUFFIX",
             Boolean.toString(pirjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_MODELLER",
+    Cache.setPropertyNoSave("PIR_MODELLER",
             Boolean.toString(modellerOutput.isSelected()));
-    Cache.applicationProperties.setProperty("EXPORT_EMBBED_BIOJSON",
+    Cache.setPropertyNoSave("EXPORT_EMBBED_BIOJSON",
             Boolean.toString(embbedBioJSON.isSelected()));
     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
 
-    Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
+    Cache.setPropertyNoSave("FIGURE_AUTOIDWIDTH",
             Boolean.toString(autoIdWidth.isSelected()));
     userIdWidth_actionPerformed();
-    Cache.applicationProperties.setProperty("FIGURE_FIXEDIDWIDTH",
+    Cache.setPropertyNoSave("FIGURE_FIXEDIDWIDTH",
             userIdWidth.getText());
 
     /*
      * Save Editing settings
      */
-    Cache.applicationProperties.setProperty("AUTO_CALC_CONSENSUS",
+    Cache.setPropertyNoSave("AUTO_CALC_CONSENSUS",
             Boolean.toString(autoCalculateConsCheck.isSelected()));
-    Cache.applicationProperties.setProperty("SORT_BY_TREE",
+    Cache.setPropertyNoSave("SORT_BY_TREE",
             Boolean.toString(sortByTree.isSelected()));
-    Cache.applicationProperties.setProperty("PAD_GAPS",
+    Cache.setPropertyNoSave("PAD_GAPS",
             Boolean.toString(padGaps.isSelected()));
 
     if (!Platform.isJS())
@@ -976,43 +1202,41 @@ public class Preferences extends GPreferences
     /*
      * Save Backups settings
      */
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enableBackupFiles.isSelected()));
     int preset = getComboIntStringKey(backupfilesPresetsCombo);
-    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET",
-            Integer.toString(preset));
+    Cache.setPropertyNoSave(BackupFiles.NS + "_PRESET", Integer.toString(preset));
 
     if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
     {
       BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
       BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
               BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
-      Cache.applicationProperties.setProperty(
-              BackupFilesPresetEntry.CUSTOMCONFIG, customBFPE.toString());
+      Cache.setPropertyNoSave(BackupFilesPresetEntry.CUSTOMCONFIG,
+                      customBFPE.toString());
     }
 
     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
             .get(preset);
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
 
     /*
      * Save Memory Settings
      */
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             MemorySetting.CUSTOMISED_SETTINGS,
             Boolean.toString(customiseMemorySetting.isSelected()));
-    Cache.applicationProperties.setProperty(MemorySetting.MEMORY_JVMMEMPC,
+    Cache.setPropertyNoSave(MemorySetting.MEMORY_JVMMEMPC,
             Integer.toString(jvmMemoryPercentSlider.getValue()));
-    Cache.applicationProperties.setProperty(MemorySetting.MEMORY_JVMMEMMAX,
+    Cache.setPropertyNoSave(MemorySetting.MEMORY_JVMMEMMAX,
             jvmMemoryMaxTextField.getText());
 
     /*
      * save and close Preferences
      */
-
     Cache.saveProperties();
-    Desktop.instance.doConfigureStructurePrefs();
+    Desktop.getInstance().doConfigureStructurePrefs();
     try
     {
       frame.setClosed(true);
@@ -1026,7 +1250,7 @@ public class Preferences extends GPreferences
     String newProxyType = customProxy.isSelected() ? Cache.PROXYTYPE_CUSTOM
             : noProxy.isSelected() ? Cache.PROXYTYPE_NONE
                     : Cache.PROXYTYPE_SYSTEM;
-    Cache.applicationProperties.setProperty("USE_PROXY", newProxyType);
+    Cache.setPropertyNoSave("USE_PROXY", newProxyType);
     Cache.setOrRemove("PROXY_SERVER", proxyServerHttpTB.getText());
     Cache.setOrRemove("PROXY_PORT", proxyPortHttpTB.getText());
     Cache.setOrRemove("PROXY_SERVER_HTTPS", proxyServerHttpsTB.getText());
@@ -1040,12 +1264,48 @@ public class Preferences extends GPreferences
             || !newProxyType.equals(previousProxyType))
     {
       // force a re-lookup of ws if proxytype is custom or has changed
-      wsPrefs.update++;
+      wsPrefs.refreshWs_actionPerformed(null);
     }
     previousProxyType = newProxyType;
   }
 
-  /**
+  public static void setAppletDefaults()
+  {
+
+    // http://www.jalview.org/old/v2_8/examples/appletParameters.html
+
+    // showConservation true or false Default is true.
+    // showQuality true or false Default is true.
+    // showConsensus true or false Default is true.
+    // showFeatureSettings true or false Shows the feature settings window when
+    // startin
+    // showTreeBootstraps true or false (default is true) show or hide branch
+    // bootstraps
+    // showTreeDistances true or false (default is true) show or hide branch
+    // lengths
+    // showUnlinkedTreeNodes true or false (default is false) indicate if
+    // unassociated nodes should be highlighted in the tree view
+    // showUnconserved true of false (default is false) When true, only gaps and
+    // symbols different to the consensus sequence ions of the alignment
+    // showGroupConsensus true of false (default is false) When true, shows
+    // consensus annotation row for any groups on the alignment. (since 2.7)
+    // showGroupConservation true of false (default is false) When true, shows
+    // amino-acid property conservation annotation row for any groups on the
+    // showConsensusHistogram true of false (default is true) When true, shows
+    // the percentage occurence of the consensus symbol for each column as a
+    // showSequenceLogo true of false (default is false) When true, shows a
+    // sequence logo above the consensus sequence (overlaid above the Consensus
+
+    Cache.setPropertyNoSave(SHOW_CONSERVATION, "true");
+    Cache.setPropertyNoSave(SHOW_QUALITY, "false");
+    Cache.setPropertyNoSave(SHOW_CONSENSUS, "true");
+    Cache.setPropertyNoSave(SHOW_UNCONSERVED, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSERVATION, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSENSUS, "false");
+
+    // TODO -- just a start here
+  }
+ /**
    * Do any necessary validation before saving settings. Return focus to the
    * first tab which fails validation.
    * 
@@ -1089,7 +1349,7 @@ public class Preferences extends GPreferences
       FileFormatI format = chooser.getSelectedFormat();
       if (format != null)
       {
-        Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
+        Cache.setPropertyNoSave("DEFAULT_FILE_FORMAT",
                 format.getName());
       }
       startupFileTextfield
@@ -1138,6 +1398,8 @@ public class Preferences extends GPreferences
             && (identity.isSelected() || showGroupConsensus.isSelected()));
     showConsensLogo.setEnabled(annotations.isSelected()
             && (identity.isSelected() || showGroupConsensus.isSelected()));
+    showInformationHistogram.setEnabled(annotations.isSelected());
+    showHMMLogo.setEnabled(annotations.isSelected());
   }
 
   @Override
@@ -1147,7 +1409,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.new_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1199,7 +1461,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.edit_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1248,6 +1510,30 @@ public class Preferences extends GPreferences
     ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
   }
 
+  @Override
+  public void defaultBrowser_mouseClicked(MouseEvent e)
+  {
+    // TODO: JAL-3048 not needed for j2s
+    if (!Platform.isJS()) // BH 2019
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
+    {
+      JFileChooser chooser = new JFileChooser(".");
+      chooser.setDialogTitle(
+              MessageManager.getString("label.select_default_browser"));
+
+      int value = chooser.showOpenDialog(this);
+
+      if (value == JFileChooser.APPROVE_OPTION)
+      {
+        defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
+      }
+    }
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -1289,7 +1575,8 @@ public class Preferences extends GPreferences
     if (!useLegacyGap.isSelected())
     {
       JalviewColourChooser.showColourChooser(this,
-              MessageManager.getString("label.select_gap_colour"), gap);
+              MessageManager.getString("label.select_gap_colour"),
+              gap);
     }
   }
 
@@ -1297,7 +1584,8 @@ public class Preferences extends GPreferences
   public void hiddenColour_actionPerformed(JPanel hidden)
   {
     JalviewColourChooser.showColourChooser(this,
-            MessageManager.getString("label.select_hidden_colour"), hidden);
+            MessageManager.getString("label.select_hidden_colour"),
+            hidden);
   }
 
   @Override
@@ -1346,7 +1634,7 @@ public class Preferences extends GPreferences
     } catch (NumberFormatException x)
     {
       userIdWidth.setText("");
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("warn.user_defined_width_requirements"),
               MessageManager.getString("label.invalid_id_column_width"),
@@ -1362,8 +1650,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()
@@ -1373,7 +1661,7 @@ public class Preferences extends GPreferences
       File f = new File(structureViewerPath.getText());
       if (!f.canExecute())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.invalid_viewer_path"),
                 MessageManager.getString("label.invalid_viewer_path"),
                 JvOptionPane.ERROR_MESSAGE);
@@ -1382,10 +1670,61 @@ public class Preferences extends GPreferences
     }
     return true;
   }
+  
+  /**
+   * Returns true if the given text field contains a path to a folder that
+   * contains an executable with the given name, else false (after showing a
+   * warning dialog). The executable name will be tried with .exe appended if not
+   * found.
+   * 
+   * @param textField
+   * @param executable
+   */
+  protected boolean validateExecutablePath(JTextField textField, String executable)
+  {
+    String folder = textField.getText().trim();
+
+    if (FileUtils.getExecutable(executable, folder) != null)
+    {
+      return true;
+    }
+    if (folder.length() > 0)
+    {
+      JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
+              MessageManager.formatMessage("label.executable_not_found",
+                      executable),
+              MessageManager.getString("label.invalid_folder"),
+              JvOptionPane.ERROR_MESSAGE);
+    }
+    return false;
+  }
 
   /**
-   * 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
+   * Checks if a file can be executed
+   * 
+   * @param path
+   *          the path to the file
+   * @return
+   */
+  public boolean canExecute(String path)
+  {
+    File file = new File(path);
+    if (!file.canExecute())
+    {
+      file = new File(path + ".exe");
+      {
+        if (!file.canExecute())
+        {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   * 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)
@@ -1447,7 +1786,7 @@ public class Preferences extends GPreferences
     if (!found)
     {
       String[] options = { "OK", "Help" };
-      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
+      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
               JvSwingUtils.wrapTooltip(true,
                       MessageManager.getString("label.viewer_missing")),
               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -1482,7 +1821,19 @@ public class Preferences extends GPreferences
           }
         });
       }
-    }
+     }
+  }
+
+  @Override
+  protected void validateHmmerPath()
+  {
+    validateExecutablePath(hmmerPath, HmmerCommand.HMMBUILD);
+  }
+
+  @Override
+  protected void validateCygwinPath()
+  {
+    validateExecutablePath(cygwinPath, "run");
   }
 
   public class OptionsParam
index 011d810..abf096f 100644 (file)
@@ -166,6 +166,23 @@ public class ProgressBar implements IProgressIndicator
     });
 
   }
+  
+  @Override
+  public void removeProgressBar(final long id)
+  {
+    SwingUtilities.invokeLater(() -> {
+      JPanel progressPanel = progressBars.get(id);
+      if (progressPanel != null)
+      {
+        progressBars.remove(id);
+        if (progressBarHandlers.containsKey(id))
+        {
+          progressBarHandlers.remove(id);
+        }
+        removeRow(progressPanel);
+      }
+    });
+  }
 
   /**
    * Lays out progress bar container hierarchy
index 6dbbbb0..e8fea51 100644 (file)
@@ -201,7 +201,7 @@ public class PromptUserConfig implements Runnable
     }
     try
     {
-      int reply = JvOptionPane.showConfirmDialog(Desktop.desktop, // component,
+      int reply = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(), // component,
               dialogText, dialogTitle,
               (allowCancel) ? JvOptionPane.YES_NO_CANCEL_OPTION
                       : JvOptionPane.YES_NO_OPTION,
index 9dc28c8..7ac3123 100644 (file)
@@ -335,7 +335,7 @@ public class PymolViewer extends StructureViewerBase
 
     if (!binding.launchPymol())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+       JvOptionPane.showMessageDialog(Desktop.getInstance(),
               MessageManager.formatMessage("label.open_viewer_failed",
                       getViewerName()),
               MessageManager.getString("label.error_loading_file"),
index 6ed3248..c7e2b8e 100755 (executable)
@@ -103,8 +103,8 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     frame.setContentPane(this);
     Desktop.addInternalFrame(frame,
             MessageManager
-                    .getString("label.redundancy_threshold_selection"),
-            true, FRAME_WIDTH, FRAME_HEIGHT, false, true);
+                    .getString("label.redundancy_threshold_selection"), Desktop.FRAME_MAKE_VISIBLE,
+            FRAME_WIDTH, FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
     frame.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
@@ -263,8 +263,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
       ap.alignFrame.addHistoryItem(cut);
 
       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
   }
@@ -289,8 +288,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     {
       command.undoCommand(af.getViewAlignments());
       ap.av.getHistoryList().remove(command);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
       af.updateEditMenuBar();
     }
 
index 8ae5408..34d456f 100644 (file)
  */
 package jalview.gui;
 
-import jalview.jbgui.GRestInputParamEditDialog;
-import jalview.ws.params.InvalidArgumentException;
-import jalview.ws.params.OptionI;
-import jalview.ws.params.ParameterI;
-import jalview.ws.rest.InputType;
-import jalview.ws.rest.RestServiceDescription;
-
 import java.util.ArrayList;
 import java.util.Hashtable;
 
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.event.ListSelectionEvent;
 
+import jalview.jbgui.GRestInputParamEditDialog;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.OptionI;
+import jalview.ws.params.ParameterI;
+import jalview.ws.rest.InputType;
+import jalview.ws.rest.RestServiceDescription;
 import net.miginfocom.swing.MigLayout;
 
 public class RestInputParamEditDialog extends GRestInputParamEditDialog
index cda76d9..213a979 100644 (file)
@@ -476,10 +476,13 @@ public class RestServiceEditorPane extends GRestServiceEditorPane
             final Thread runner = Thread.currentThread();
             JFrame df = new JFrame();
             df.getContentPane().setLayout(new BorderLayout());
-            df.getContentPane().add((nulserv = !nulserv)
-                    ? new RestServiceEditorPane(jalview.ws.rest.RestClient
-                            .makeShmmrRestClient().getRestDescription())
-                    : new RestServiceEditorPane(), BorderLayout.CENTER);
+            df.getContentPane().add(
+                    (nulserv = !nulserv) ? new RestServiceEditorPane(
+                            jalview.ws.rest.clientdefs.ShmrRestClient
+                                    .makeShmmrRestClient()
+                                    .getRestDescription())
+                            : new RestServiceEditorPane(),
+                    BorderLayout.CENTER);
             df.setBounds(100, 100, 600, 400);
             df.addComponentListener(new ComponentListener()
             {
index ca3faf8..890ef35 100755 (executable)
@@ -180,9 +180,7 @@ public class ScalePanel extends JPanel
         {
           av.showColumn(hiddenRange[0]);
           reveal = null;
-          ap.updateLayout();
-          ap.paintAlignment(true, true);
-          av.sendSelection();
+          updatePanel();
         }
       });
       pop.add(item);
@@ -198,9 +196,7 @@ public class ScalePanel extends JPanel
           {
             av.showAllHiddenColumns();
             reveal = null;
-            ap.updateLayout();
-            ap.paintAlignment(true, true);
-            av.sendSelection();
+            updatePanel();
           }
         });
         pop.add(item);
@@ -222,10 +218,7 @@ public class ScalePanel extends JPanel
           {
             av.setSelectionGroup(null);
           }
-
-          ap.updateLayout();
-          ap.paintAlignment(true, true);
-          av.sendSelection();
+          updatePanel();
         }
       });
       pop.add(item);
@@ -233,6 +226,14 @@ public class ScalePanel extends JPanel
     return pop;
   }
 
+  protected void updatePanel()
+  {
+    ap.updateLayout();
+    ap.paintAlignment(true, true);
+    ap.updateScrollBarsFromRanges();
+    av.sendSelection();
+  }
+
   /**
    * Handles left mouse button press
    * 
index d15cdcf..1137153 100755 (executable)
  */
 package jalview.gui;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.VisibleContigsIterator;
+import jalview.renderer.ScaleRenderer;
+import jalview.renderer.ScaleRenderer.ScaleMark;
+import jalview.util.Comparison;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -35,18 +47,6 @@ import java.util.List;
 
 import javax.swing.JPanel;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.VisibleContigsIterator;
-import jalview.renderer.ScaleRenderer;
-import jalview.renderer.ScaleRenderer.ScaleMark;
-import jalview.util.Comparison;
-import jalview.viewmodel.ViewportListenerI;
-import jalview.viewmodel.ViewportRanges;
-
 /**
  * The Swing component on which the alignment sequences, and annotations (if
  * shown), are drawn. This includes scales above, left and right (if shown) in
@@ -76,7 +76,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
   private final SequenceRenderer seqRdr;
 
-  boolean fastPaint = false;
+  private boolean fastPaint = false;
 
   private boolean fastpainting = false;
 
@@ -95,6 +95,11 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
   private int wrappedVisibleWidths; // number of wrapped widths displayed
 
+  private int availWidth;
+
+  private int availHeight;
+
+  private boolean allowFastPaint;
   // Don't do this! Graphics handles are supposed to be transient
   // private Graphics2D gg;
 
@@ -203,7 +208,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     int yPos = ypos + charHeight;
     int startX = startx;
     int endX = endx;
-
+    
     if (av.hasHiddenColumns())
     {
       HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
@@ -343,6 +348,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         }
       }
 
+
       // System.err.println(">>> FastPaint to " + transX + " " + transY + " "
       // + horizontal + " " + vertical + " " + startRes + " " + endRes
       // + " " + startSeq + " " + endSeq);
@@ -352,7 +358,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
               img.getWidth(), img.getHeight(), -horizontal * charWidth,
               -vertical * charHeight);
 
-      /** @j2sNative xxi = this.img */
 
       gg.translate(transX, transY);
       drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
@@ -362,7 +367,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       // Call repaint on alignment panel so that repaints from other alignment
       // panel components can be aggregated. Otherwise performance of the
       // overview window and others may be adversely affected.
-      // System.out.println("SeqCanvas fastPaint() repaint() request...");
       av.getAlignPanel().repaint();
     } finally
     {
@@ -373,19 +377,14 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   @Override
   public void paintComponent(Graphics g)
   {
+    if (av.getAlignPanel().getHoldRepaint())
+    {
+      return;
+    }
 
-    int charHeight = av.getCharHeight();
-    int charWidth = av.getCharWidth();
-
-    int width = getWidth();
-    int height = getHeight();
-
-    width -= (width % charWidth);
-    height -= (height % charHeight);
-
-    // BH 2019 can't possibly fastPaint if either width or height is 0
+    getAvailSizes();
 
-    if (width == 0 || height == 0)
+    if (availWidth == 0 || availHeight == 0)
     {
       return;
     }
@@ -419,11 +418,9 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     // }
 
     Rectangle vis, clip;
-    if (img != null
-            && (fastPaint
-                    || (vis = getVisibleRect()).width != (clip = g
-                            .getClipBounds()).width
-                    || vis.height != clip.height))
+    if (allowFastPaint  && img != null
+            && (fastPaint || (vis = getVisibleRect()).width != (clip = g.getClipBounds()).width
+                          || vis.height != clip.height))
     {
       g.drawImage(img, 0, 0, this);
       drawSelectionGroup((Graphics2D) g, startRes, endRes, startSeq,
@@ -432,13 +429,15 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
     else
     {
+      allowFastPaint = true;
       // img is a cached version of the last view we drew.
       // If we have no img or the size has changed, make a new one.
       //
-      if (img == null || width != img.getWidth()
-              || height != img.getHeight())
+      if (img == null || availWidth != img.getWidth()
+              || availHeight != img.getHeight())
       {
-        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        img = new BufferedImage(availWidth, availHeight,
+                BufferedImage.TYPE_INT_RGB);
       }
 
       Graphics2D gg = (Graphics2D) img.getGraphics();
@@ -451,11 +450,11 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       }
 
       gg.setColor(Color.white);
-      gg.fillRect(0, 0, img.getWidth(), img.getHeight());
+      gg.fillRect(0, 0, availWidth, availHeight);
 
       if (av.getWrapAlignment())
       {
-        drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
+        drawWrappedPanel(gg, availWidth, availHeight, ranges.getStartRes());
       }
       else
       {
@@ -473,7 +472,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       drawCursor(g, startRes, endRes, startSeq, endSeq);
     }
   }
-
   /**
    * Draw an alignment panel for printing
    * 
@@ -537,7 +535,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     FontMetrics fm = getFontMetrics(av.getFont());
 
     int labelWidth = 0;
-
+    
     if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
     {
       labelWidth = getLabelWidth(fm);
@@ -573,6 +571,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
     }
 
+    // quick int log10
     int length = 0;
     for (int i = maxWidth; i > 0; i /= 10)
     {
@@ -587,18 +586,17 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
    * window
    * 
    * @param g
-   * @param canvasWidth
+   * @param availWidth
    *          available width in pixels
-   * @param canvasHeight
+   * @param availHeight
    *          available height in pixels
    * @param startColumn
    *          the first column (0...) of the alignment to draw
    */
-  public void drawWrappedPanel(Graphics g, int canvasWidth,
-          int canvasHeight, final int startColumn)
+  public void drawWrappedPanel(Graphics g, int availWidth, int availHeight,
+          final int startColumn)
   {
-    int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth,
-            canvasHeight);
+    int wrappedWidthInResidues = calculateWrappedGeometry();
 
     av.setWrappedWidth(wrappedWidthInResidues);
 
@@ -608,7 +606,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     // we need to call this again to make sure the startColumn +
     // wrappedWidthInResidues values are used to calculate wrappedVisibleWidths
     // correctly.
-    calculateWrappedGeometry(canvasWidth, canvasHeight);
+    calculateWrappedGeometry();
 
     /*
      * draw one width at a time (excluding any scales shown),
@@ -623,7 +621,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     {
       int endColumn = Math.min(maxWidth,
               start + wrappedWidthInResidues - 1);
-      drawWrappedWidth(g, ypos, start, endColumn, canvasHeight);
+      drawWrappedWidth(g, ypos, start, endColumn, availHeight);
       ypos += wrappedRepeatHeightPx;
       start += wrappedWidthInResidues;
       currentWidth++;
@@ -632,6 +630,15 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     drawWrappedDecorators(g, startColumn);
   }
 
+  private void getAvailSizes()
+  {
+    int charHeight = av.getCharHeight();
+    int charWidth = av.getCharWidth();
+    availWidth = getWidth();
+    availHeight = getHeight();
+    availWidth -= (availWidth % charWidth);
+    availHeight -= (availHeight % charHeight);
+  }
   /**
    * Calculates and saves values needed when rendering a wrapped alignment.
    * These depend on many factors, including
@@ -642,11 +649,24 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
    * <li>whether scales are shown left, right or above the alignment</li>
    * </ul>
    * 
+   * @param availWidth
+   * @param availHeight
+   * @return the number of residue columns in each width
+   */
+  protected int calculateWrappedGeometry()
+  {
+    getAvailSizes();
+    return calculateWrappedGeometry(availWidth, availHeight);
+
+  }
+
+  /**
+   * for test only
    * @param canvasWidth
    * @param canvasHeight
-   * @return the number of residue columns in each width
+   * @return
    */
-  protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight)
+  public int calculateWrappedGeometry(int canvasWidth, int canvasHeight)
   {
     int charHeight = av.getCharHeight();
 
@@ -661,8 +681,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
      * compute height in pixels of the wrapped widths
      * - start with space above plus sequences
      */
-    wrappedRepeatHeightPx = wrappedSpaceAboveAlignment;
-    wrappedRepeatHeightPx += av.getAlignment().getHeight() * charHeight;
+    wrappedRepeatHeightPx = wrappedSpaceAboveAlignment
+            + av.getAlignment().getHeight() * charHeight;
 
     /*
      * add annotations panel height if shown
@@ -690,7 +710,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
      * compute width in residues; this also sets East and West label widths
      */
     int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth);
-    av.setWrappedWidth(wrappedWidthInResidues); // update model accordingly
+
     /*
      *  limit visibleWidths to not exceed width of alignment
      */
@@ -800,7 +820,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       if (av.getScaleRightWrapped())
       {
         int x = labelWidthWest + viewportWidth * charWidth;
-
         g.translate(x, 0);
         drawVerticalScale(g, startCol, endColumn, ypos, false);
         g.translate(-x, 0);
@@ -881,6 +900,11 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
   }
 
+  private final static BasicStroke dottedStroke = new BasicStroke(1,
+          BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 3f, new float[]
+          { 5f, 3f }, 0f);
+
+  private final static BasicStroke basicStroke = new BasicStroke();
   /*
    * Draw a selection group over a wrapped alignment
    */
@@ -889,9 +913,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   {
     // chop the wrapped alignment extent up into panel-sized blocks and treat
     // each block as if it were a block from an unwrapped alignment
-    g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
-            BasicStroke.JOIN_ROUND, 3f, new float[]
-            { 5f, 3f }, 0f));
+    g.setStroke(dottedStroke);
     g.setColor(Color.RED);
 
     int charWidth = av.getCharWidth();
@@ -899,6 +921,20 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
             / charWidth;
     int startx = startRes;
     int maxwidth = av.getAlignment().getVisibleWidth();
+    // JAL-3253-applet had this:
+    // // height gap above each panel
+    // int charHeight = av.getCharHeight();
+    // int hgap = charHeight;
+    // if (av.getScaleAboveWrapped())
+    // {
+    // hgap += charHeight;
+    // }
+    // int dy = getAnnotationHeight() + hgap
+    // + av.getAlignment().getHeight() * charHeight;
+    // int ypos = hgap; // vertical offset
+
+    // this is from 0b573ed (gmungoc)
+    int dy = wrappedRepeatHeightPx;
     int ypos = wrappedSpaceAboveAlignment;
 
     while ((ypos <= canvasHeight) && (startx < maxwidth))
@@ -916,11 +952,13 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
               av.getAlignment().getHeight() - 1, ypos);
       g.translate(-labelWidthWest, 0);
 
-      ypos += wrappedRepeatHeightPx;
+      // update vertical offset
+      ypos += dy;
 
+      // update horizontal offset
       startx += cWidth;
     }
-    g.setStroke(new BasicStroke());
+    g.setStroke(basicStroke);
   }
 
   /**
@@ -1257,6 +1295,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
   }
 
+
   /**
    * Draw a selection group over an unwrapped alignment
    * 
@@ -1279,7 +1318,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
           int startRes, int endRes, int startSeq, int endSeq, int offset)
   {
     int charWidth = av.getCharWidth();
-
     if (!av.hasHiddenColumns())
     {
       drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
@@ -1472,7 +1510,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
     }
   }
-
   /**
    * Highlights search results in the visible region by rendering as white text
    * on a black background. Any previous highlighting is removed. Answers true
@@ -1488,7 +1525,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     return highlightSearchResults(results, false);
 
   }
-
   /**
    * Highlights search results in the visible region by rendering as white text
    * on a black background. Any previous highlighting is removed. Answers true
@@ -1665,92 +1701,157 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   public void propertyChange(PropertyChangeEvent evt)
   {
     String eventName = evt.getPropertyName();
-    // System.err.println(">>SeqCanvas propertyChange " + eventName);
-    if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+    // BH 2019.07.27 removes dead code introduced in aad3650 and simplifies
+    // logic, emphasizing no check for ENDRES or ENDSEQ
+
+    // Both scrolling and resizing change viewport ranges: scrolling changes
+    // both start and end points, but resize only changes end values.
+    // Here we only want to fastpaint on a scroll, with resize using a normal
+    // paint, so scroll events are identified as changes to the horizontal or
+    // vertical start value.
+
+    // Make sure we're not trying to draw a panel
+    // larger than the visible window
+    int scrollX = 0;
+    int scrollY = 0;
+    switch (eventName)
     {
+    case SequenceGroup.SEQ_GROUP_CHANGED:
       fastPaint = true;
       repaint();
       return;
-    }
-    else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
-    {
+    case ViewportRanges.MOVE_VIEWPORT:
       fastPaint = false;
-      // System.err.println("!!!! fastPaint false from MOVE_VIEWPORT");
       repaint();
       return;
-    }
-
-    int scrollX = 0;
-    if (eventName.equals(ViewportRanges.STARTRES)
-            || eventName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
-      // Make sure we're not trying to draw a panel
-      // larger than the visible window
-      if (eventName.equals(ViewportRanges.STARTRES))
-      {
-        scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
-      }
-      else
+    case ViewportRanges.STARTSEQ:
+      // meaning STARTOREND
+      // typically scroll, but possibly just the end changed
+      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+      return;
+    case ViewportRanges.STARTRES:
+      // meaning STARTOREND
+      scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+      break;
+    case ViewportRanges.STARTRESANDSEQ:
+      scrollX = ((int[]) evt.getNewValue())[0]
+              - ((int[]) evt.getOldValue())[0];
+      scrollY = ((int[]) evt.getNewValue())[1]
+              - ((int[]) evt.getOldValue())[1];
+      if (scrollX != 0 && scrollY != 0)
       {
-        scrollX = ((int[]) evt.getNewValue())[0]
-                - ((int[]) evt.getOldValue())[0];
-      }
-      ViewportRanges vpRanges = av.getRanges();
+        // all sorts of problems in JavaScript if this is commented out.
+        repaint();
+        return;
 
-      int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
-      if (scrollX > range)
-      {
-        scrollX = range;
-      }
-      else if (scrollX < -range)
-      {
-        scrollX = -range;
       }
+      break;
+    default:
+      return;
+    }
+
+    ViewportRanges vpRanges = av.getRanges();
+    int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+    scrollX = Math.max(Math.min(scrollX, range), -range);
+    // only STARTRES or STARTRESANDSEQ:
+    if (av.getWrapAlignment())
+    {
+      fastPaintWrapped(scrollX);
+    }
+    else
+    {
+      fastPaint(scrollX, scrollY);
     }
+
+    // BH 2019.07.27 was:
+    // if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+    // {
+    // fastPaint = true;
+    // repaint();
+    // return;
+    // }
+    // else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+    // {
+    // fastPaint = false;
+    // // System.err.println("!!!! fastPaint false from MOVE_VIEWPORT");
+    // repaint();
+    // return;
+    // }
+    //
+    // if (eventName.equals(ViewportRanges.STARTRES)
+    // || eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // // Make sure we're not trying to draw a panel
+    // // larger than the visible window
+    // if (eventName.equals(ViewportRanges.STARTRES))
+    // {
+    // scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+    // }
+    // else
+    // {
+    // scrollX = ((int[]) evt.getNewValue())[0]
+    // - ((int[]) evt.getOldValue())[0];
+    // }
+    // ViewportRanges vpRanges = av.getRanges();
+    //
+    // int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+    // if (scrollX > range)
+    // {
+    // scrollX = range;
+    // }
+    // else if (scrollX < -range)
+    // {
+    // scrollX = -range;
+    // }
+    // }
     // Both scrolling and resizing change viewport ranges: scrolling changes
     // both start and end points, but resize only changes end values.
     // Here we only want to fastpaint on a scroll, with resize using a normal
     // paint, so scroll events are identified as changes to the horizontal or
     // vertical start value.
-    if (eventName.equals(ViewportRanges.STARTRES))
-    {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
-      else
-      {
-        fastPaint(scrollX, 0);
-      }
-    }
-    else if (eventName.equals(ViewportRanges.STARTSEQ))
-    {
-      // scroll
-      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
-    }
-    else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
-      else
-      {
-        fastPaint(scrollX, 0);
-      }
-    }
-    else if (eventName.equals(ViewportRanges.STARTSEQ))
-    {
-      // scroll
-      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
-    }
-    else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
-    }
+    // BH 2019.07.27 was:
+    // if (eventName.equals(ViewportRanges.STARTRES))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // else
+    // {
+    // fastPaint(scrollX, 0);
+    // }
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTSEQ))
+    // {
+    // // scroll
+    // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // else
+    // {
+    // fastPaint(scrollX, 0);
+    // }
+    // }
+    //
+    // BH oops!
+    //
+    // else if (eventName.equals(ViewportRanges.STARTSEQ))
+    // {
+    // // scroll
+    // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // }
   }
 
   /**
@@ -1787,10 +1888,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
     try
     {
-
       Graphics gg = img.getGraphics();
-
-      calculateWrappedGeometry(getWidth(), getHeight());
+      calculateWrappedGeometry();
 
       /*
        * relocate the regions of the alignment that are still visible
@@ -1819,7 +1918,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       drawWrappedDecorators(gg, ranges.getStartRes());
 
       gg.dispose();
-
       repaint();
     } finally
     {
@@ -1844,7 +1942,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
 
     Graphics gg = img.getGraphics();
-
     ViewportRanges ranges = av.getRanges();
     int viewportWidth = ranges.getViewportWidth();
     int charWidth = av.getCharWidth();
@@ -1872,7 +1969,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       /*
        * white fill first to erase annotations
        */
-
       gg.translate(xOffset, 0);
       gg.setColor(Color.white);
       gg.fillRect(labelWidthWest, ypos, (endRes - startRes + 1) * charWidth,
@@ -1880,7 +1976,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       gg.translate(-xOffset, 0);
 
       drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight);
-
     }
 
     /*
@@ -2044,7 +2139,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
     boolean matchFound = false;
 
-    calculateWrappedGeometry(getWidth(), getHeight());
+    calculateWrappedGeometry();
     int wrappedWidth = av.getWrappedWidth();
     int wrappedHeight = wrappedRepeatHeightPx;
 
@@ -2142,7 +2237,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         }
       }
     }
-
     gg.dispose();
 
     return matchFound;
@@ -2158,4 +2252,13 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     return labelWidthWest;
   }
 
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
+
 }
index 111b4c0..c8a2ec9 100644 (file)
@@ -87,6 +87,7 @@ public class SeqPanel extends JPanel
         SequenceListener, SelectionListener
 {
   /*
+   * 
    * a class that holds computed mouse position
    * - column of the alignment (0...)
    * - sequence offset (0...)
@@ -242,11 +243,13 @@ public class SeqPanel extends JPanel
    */
   public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
   {
+    setName("SeqPanel");
     seqARep = new SequenceAnnotationReport(true);
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
-
+    
+    
     this.av = viewport;
     setBackground(Color.white);
 
@@ -292,8 +295,7 @@ public class SeqPanel extends JPanel
     int alignmentHeight = av.getAlignment().getHeight();
     if (av.getWrapAlignment())
     {
-      seqCanvas.calculateWrappedGeometry(seqCanvas.getWidth(),
-              seqCanvas.getHeight());
+      seqCanvas.calculateWrappedGeometry();
 
       /*
        * yPos modulo height of repeating width
@@ -341,7 +343,6 @@ public class SeqPanel extends JPanel
 
     return new MousePos(col, seqIndex, annIndex);
   }
-
   /**
    * Returns the aligned sequence position (base 0) at the mouse position, or
    * the closest visible one
@@ -429,8 +430,7 @@ public class SeqPanel extends JPanel
       if (editCommand != null && editCommand.getSize() > 0)
       {
         ap.alignFrame.addHistoryItem(editCommand);
-        av.firePropertyChange("alignment", null,
-                av.getAlignment().getSequences());
+        ap.av.notifyAlignment();
       }
     } finally
     {
@@ -483,9 +483,8 @@ public class SeqPanel extends JPanel
 
   void moveCursor(int dx, int dy)
   {
-    moveCursor(dx, dy, false);
+    moveCursor(dx, dy,false);
   }
-
   void moveCursor(int dx, int dy, boolean nextWord)
   {
     HiddenColumns hidden = av.getAlignment().getHiddenColumns();
@@ -493,13 +492,11 @@ public class SeqPanel extends JPanel
     if (nextWord)
     {
       int maxWidth = av.getAlignment().getWidth();
-      int maxHeight = av.getAlignment().getHeight();
-      SequenceI seqAtRow = av.getAlignment()
-              .getSequenceAt(seqCanvas.cursorY);
+      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;
+      boolean isGap = Comparison.isGap(seqAtRow.getCharAt(seqCanvas.cursorX));
+      int p = seqCanvas.cursorX,lastP,r=seqCanvas.cursorY,lastR;
       do
       {
         lastP = p;
@@ -520,23 +517,19 @@ public class SeqPanel extends JPanel
         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
-    {
+      seqCanvas.cursorX=p;
+      seqCanvas.cursorY=r;
+    } else {
       int maxWidth = av.getAlignment().getWidth();
-      seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX,
-              dx);
+      seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX, dx);
       seqCanvas.cursorY += dy;
     }
     scrollToVisible(false);
   }
 
-  private int nextVisible(HiddenColumns hidden, int maxWidth, int original,
-          int dx)
+  private int nextVisible(HiddenColumns hidden,int maxWidth, int original, int dx)
   {
-    int newCursorX = original + dx;
+    int newCursorX=original+dx;
     if (av.hasHiddenColumns() && !hidden.isVisible(newCursorX))
     {
       int visx = hidden.absoluteToVisibleColumn(newCursorX - dx);
@@ -557,13 +550,13 @@ public class SeqPanel extends JPanel
       }
     }
     newCursorX = (newCursorX < 0) ? 0 : newCursorX;
-    if (newCursorX >= maxWidth || !hidden.isVisible(newCursorX))
+    if (newCursorX >= maxWidth
+            || !hidden.isVisible(newCursorX))
     {
       newCursorX = original;
     }
     return newCursorX;
   }
-
   /**
    * Scroll to make the cursor visible in the viewport.
    * 
@@ -619,7 +612,7 @@ public class SeqPanel extends JPanel
     if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
     {
       setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
-              seqCanvas.cursorX, seqCanvas.cursorY);
+            seqCanvas.cursorX, seqCanvas.cursorY);
     }
 
     if (repaintNeeded)
@@ -628,6 +621,7 @@ public class SeqPanel extends JPanel
     }
   }
 
+
   void setSelectionAreaAtCursor(boolean topLeft)
   {
     SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
@@ -966,7 +960,8 @@ public class SeqPanel extends JPanel
         SequenceI seq = match.getSequence();
         SequenceI ds = seq.getDatasetSequence() == null ? seq
                 : seq.getDatasetSequence();
-        MappedFeatures mf = fr2.findComplementFeaturesAtResidue(ds, pos);
+        MappedFeatures mf = fr2
+                .findComplementFeaturesAtResidue(ds, pos);
         if (mf != null)
         {
           for (SequenceFeature sf : mf.features)
@@ -1109,8 +1104,9 @@ public class SeqPanel extends JPanel
     {
       List<SequenceFeature> features = ap.getFeatureRenderer()
               .findFeaturesAtColumn(sequence, column + 1);
-      unshownFeatures = seqARep.appendFeatures(tooltipText, pos, features,
-              this.ap.getSeqPanel().seqCanvas.fr, MAX_TOOLTIP_LENGTH);
+      unshownFeatures = seqARep.appendFeatures(tooltipText, pos,
+              features, this.ap.getSeqPanel().seqCanvas.fr,
+              MAX_TOOLTIP_LENGTH);
 
       /*
        * add features in CDS/protein complement at the corresponding
@@ -1128,8 +1124,8 @@ public class SeqPanel extends JPanel
                   pos);
           if (mf != null)
           {
-            unshownFeatures += seqARep.appendFeatures(tooltipText, pos, mf,
-                    fr2, MAX_TOOLTIP_LENGTH);
+            unshownFeatures += seqARep.appendFeatures(tooltipText,
+                    pos, mf, fr2, MAX_TOOLTIP_LENGTH);
           }
         }
       }
@@ -1157,7 +1153,8 @@ public class SeqPanel extends JPanel
       if (!textString.equals(lastTooltip))
       {
         lastTooltip = textString;
-        lastFormattedTooltip = JvSwingUtils.wrapTooltip(true, textString);
+        lastFormattedTooltip = JvSwingUtils.wrapTooltip(true,
+                textString);
         setToolTipText(lastFormattedTooltip);
       }
     }
@@ -1184,8 +1181,8 @@ public class SeqPanel extends JPanel
 
     String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
             anns);
-    if (tooltip == null ? tooltip != lastTooltip
-            : !tooltip.equals(lastTooltip))
+    boolean tooltipChanged = tooltip == null ? lastTooltip != null : !tooltip.equals(lastTooltip);
+    if (tooltipChanged)
     {
       lastTooltip = tooltip;
       lastFormattedTooltip = tooltip == null ? null
@@ -1241,8 +1238,8 @@ public class SeqPanel extends JPanel
 
     tempTip.setTipText(lastFormattedTooltip);
     int tipWidth = (int) tempTip.getPreferredSize().getWidth();
-
-    // was x += (w - x < 200) ? -(w / 2) : 5;
+    
+    // was      x += (w - x < 200) ? -(w / 2) : 5;
     x = (x + tipWidth < w ? x + 10 : w - tipWidth);
     Point p = new Point(x, y + av.getCharHeight()); // BH 2018 was - 20?
 
@@ -1256,8 +1253,7 @@ public class SeqPanel extends JPanel
    * changed, so selective redraws can be applied (ie. only structures, only
    * overview, etc)
    */
-  private boolean updateOverviewAndStructs = false; // TODO: refactor to
-                                                    // avcontroller
+  private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
 
   /**
    * set if av.getSelectionGroup() refers to a group that is defined on the
@@ -1319,7 +1315,8 @@ public class SeqPanel extends JPanel
      * Sequence number (if known), and sequence name.
      */
     String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
-    text.append("Sequence").append(seqno).append(" ID: ").append(seqName);
+    text.append("Sequence").append(seqno).append(" ID: ")
+            .append(seqName);
 
     String residue = null;
 
@@ -1578,12 +1575,12 @@ public class SeqPanel extends JPanel
     String label = null;
     if (groupEditing)
     {
-      message.append("Edit group:");
+        message.append("Edit group:");
       label = MessageManager.getString("action.edit_group");
     }
     else
     {
-      message.append("Edit sequence: " + seq.getName());
+        message.append("Edit sequence: " + seq.getName());
       label = seq.getName();
       if (label.length() > 10)
       {
@@ -1775,7 +1772,8 @@ public class SeqPanel extends JPanel
           {
             for (int j = 0; j < startres - editLastRes; j++)
             {
-              if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
+              if (!Comparison
+                      .isGap(groupSeqs[g].getCharAt(fixedRight - j)))
               {
                 blank = false;
                 break;
@@ -2167,8 +2165,8 @@ public class SeqPanel extends JPanel
          * highlight the first feature at the position on the alignment
          */
         SearchResultsI highlight = new SearchResults();
-        highlight.addResult(sequence, features.get(0).getBegin(),
-                features.get(0).getEnd());
+        highlight.addResult(sequence, features.get(0).getBegin(), features
+                .get(0).getEnd());
         seqCanvas.highlightSearchResults(highlight, true);
 
         /*
@@ -2432,7 +2430,7 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    res = Math.min(res, av.getAlignment().getWidth() - 1);
+    res = Math.min(res, av.getAlignment().getWidth()-1);
 
     if (stretchGroup.getEndRes() == res)
     {
@@ -2942,4 +2940,44 @@ public class SeqPanel extends JPanel
   {
     return lastSearchResults;
   }
+  
+  /**
+   * scroll to the given row/column - or nearest visible location
+   * 
+   * @param row
+   * @param column
+   */
+  public void scrollTo(int row, int column)
+  {
+
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, row, true, true);
+  }
+
+  /**
+   * scroll to the given row - or nearest visible location
+   * 
+   * @param row
+   */
+  public void scrollToRow(int row)
+  {
+
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    ap.scrollTo(ap.av.getRanges().getStartRes(),
+            ap.av.getRanges().getStartRes(), row, true, true);
+  }
+
+  /**
+   * scroll to the given column - or nearest visible location
+   * 
+   * @param column
+   */
+  public void scrollToColumn(int column)
+  {
+
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, ap.av.getRanges().getStartSeq(), true,
+            true);
+  }
 }
index e596fbf..db15079 100755 (executable)
@@ -186,8 +186,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(), Desktop.FRAME_MAKE_VISIBLE, 400, 
+               Platform.isAMacAndNotJS() ? 240 : 180, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   private String getFrameTitle()
@@ -915,7 +915,10 @@ public class SequenceFetcher extends JPanel implements Runnable
           }
         }
 
-        af.getViewport().applyFeaturesStyle(preferredFeatureColours);
+        if (preferredFeatureColours != null)
+        {
+          af.getViewport().applyFeaturesStyle(preferredFeatureColours);
+        }
         if (Cache.getDefault("HIDE_INTRONS", true))
         {
           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
@@ -949,7 +952,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void run()
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), error,
                 MessageManager.getString("label.error_retrieving_data"),
                 JvOptionPane.WARNING_MESSAGE);
       }
index 5e1357a..bd612b5 100755 (executable)
@@ -232,8 +232,8 @@ public class SliderPanel extends GSliderPanel
     if (!conservationSlider.isVisible())
     {
       Desktop.addInternalFrame(conservationSlider,
-              conservationSlider.getTitle(), true, FRAME_WIDTH,
-              FRAME_HEIGHT, false, true);
+              conservationSlider.getTitle(), Desktop.FRAME_MAKE_VISIBLE, FRAME_WIDTH,
+              FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
       conservationSlider.addInternalFrameListener(new InternalFrameAdapter()
       {
         @Override
@@ -308,8 +308,8 @@ public class SliderPanel extends GSliderPanel
 
     if (!PIDSlider.isVisible())
     {
-      Desktop.addInternalFrame(PIDSlider, PIDSlider.getTitle(), true,
-              FRAME_WIDTH, FRAME_HEIGHT, false, true);
+      Desktop.addInternalFrame(PIDSlider, PIDSlider.getTitle(), Desktop.FRAME_MAKE_VISIBLE,
+              FRAME_WIDTH, FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
       PIDSlider.setLayer(JLayeredPane.PALETTE_LAYER);
       PIDSlider.addInternalFrameListener(new InternalFrameAdapter()
       {
diff --git a/src/jalview/gui/SlivkaPreferences.java b/src/jalview/gui/SlivkaPreferences.java
new file mode 100644 (file)
index 0000000..6fc14b0
--- /dev/null
@@ -0,0 +1,382 @@
+package jalview.gui;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.util.MessageManager;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.slivkaws.SlivkaWSDiscoverer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CompletableFuture;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+
+@SuppressWarnings("serial")
+public class SlivkaPreferences extends JPanel
+{
+  {
+    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
+    setPreferredSize(new Dimension(500, 450));
+  }
+
+  WSDiscovererI discoverer;
+
+  private final ArrayList<String> urls = new ArrayList<>();
+
+  private final Map<String, Integer> statuses = new HashMap<>();
+
+  private final AbstractTableModel urlTableModel = new AbstractTableModel()
+  {
+    final String[] columnNames = { "Service URL", "Status" };
+
+    @Override
+    public String getColumnName(int col)
+    {
+      return columnNames[col];
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex)
+    {
+      switch (columnIndex)
+      {
+      case 0:
+        return urls.get(rowIndex);
+      case 1:
+        return statuses.getOrDefault(urls.get(rowIndex), WSDiscovererI.STATUS_UNKNOWN);
+      default:
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+    public int getRowCount()
+    {
+      return urls.size();
+    }
+
+    @Override
+    public int getColumnCount()
+    {
+      return 2;
+    }
+  };
+
+  private class WSStatusCellRenderer extends DefaultTableCellRenderer
+  {
+    @Override
+    public Component getTableCellRendererComponent(JTable table,
+        Object value, boolean isSelected, boolean hasFocus, int row,
+        int column)
+    {
+      setHorizontalAlignment(CENTER);
+      super.getTableCellRendererComponent(table, "\u25CF", isSelected,
+          hasFocus, row, column);
+      switch ((Integer) value)
+      {
+      case WSDiscovererI.STATUS_NO_SERVICES:
+        setForeground(Color.ORANGE);
+        break;
+      case WSDiscovererI.STATUS_OK:
+        setForeground(Color.GREEN);
+        break;
+      case WSDiscovererI.STATUS_INVALID:
+        setForeground(Color.RED);
+        break;
+      case WSDiscovererI.STATUS_UNKNOWN:
+      default:
+        setForeground(Color.LIGHT_GRAY);
+      }
+      return this;
+    }
+  }
+
+  private JTable urlListTable = new JTable(urlTableModel);
+  {
+    urlListTable.getColumnModel().getColumn(1).setMaxWidth(60);
+    urlListTable.getColumnModel().getColumn(1)
+        .setCellRenderer(new WSStatusCellRenderer());
+  }
+
+  // URL control panel buttons
+  JButton newWsUrl = new JButton(
+      MessageManager.getString("label.new_service_url"));
+
+  JButton editWsUrl = new JButton(
+      MessageManager.getString("label.edit_service_url"));
+
+  JButton deleteWsUrl = new JButton(
+      MessageManager.getString("label.delete_service_url"));
+
+  JButton moveUrlUp = new JButton(
+      MessageManager.getString("action.move_up"));
+
+  JButton moveUrlDown = new JButton(
+      MessageManager.getString("action.move_down"));
+
+  private String showEditUrlDialog(String oldUrl)
+  {
+    String input = (String) JvOptionPane
+        .showInternalInputDialog(
+            this,
+            MessageManager.getString("label.url:"),
+            UIManager.getString("OptionPane.inputDialogTitle", MessageManager.getLocale()),
+            JOptionPane.QUESTION_MESSAGE,
+            null,
+            null,
+            oldUrl);
+    if (input == null)
+    {
+      return null;
+    }
+    try
+    {
+      new URL(input);
+    } catch (MalformedURLException ex)
+    {
+      JvOptionPane.showInternalMessageDialog(this,
+          MessageManager.getString("label.invalid_url"),
+          UIManager.getString("OptionPane.messageDialogTitle",
+              MessageManager.getLocale()),
+          JOptionPane.WARNING_MESSAGE);
+      return null;
+    }
+    return input;
+  }
+
+  // Button Action Listeners
+  private ActionListener newUrlAction = (ActionEvent e) -> {
+    final String input = showEditUrlDialog("");
+    if (input != null)
+    {
+      urls.add(input);
+      reloadStatusForUrl(input);
+      urlTableModel.fireTableRowsInserted(urls.size(), urls.size());
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener editUrlAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0)
+    {
+      final String input = showEditUrlDialog(urls.get(i));
+      if (input != null)
+      {
+        urls.set(i, input);
+        statuses.remove(input);
+        reloadStatusForUrl(input);
+        urlTableModel.fireTableRowsUpdated(i, i);
+        discoverer.setServiceUrls(urls);
+      }
+    }
+  };
+
+  private ActionListener deleteUrlAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0)
+    {
+      urls.remove(i);
+      statuses.remove(i);
+      urlTableModel.fireTableRowsDeleted(i, i);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener moveUrlUpAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i > 0)
+    {
+      moveTableRow(i, i - 1);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener moveUrlDownAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0 && i < urls.size() - 1)
+    {
+      moveTableRow(i, i + 1);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private MouseListener tableClickListener = new MouseAdapter()
+  {
+    final ActionEvent actionEvent = new ActionEvent(urlListTable,
+        ActionEvent.ACTION_PERFORMED, "edit");
+
+    @Override
+    public void mouseClicked(MouseEvent e)
+    {
+      if (e.getClickCount() > 1)
+      {
+        editUrlAction.actionPerformed(actionEvent);
+      }
+    }
+  };
+
+  // Setting up URL list Pane
+  {
+    Font font = new Font("Verdana", Font.PLAIN, 10);
+    JPanel urlPaneContainer = new JPanel(new BorderLayout(5, 5));
+    urlPaneContainer.setBorder(BorderFactory.createCompoundBorder(
+        BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+            "Slivka Web Services"),
+        BorderFactory.createEmptyBorder(10, 5, 5, 5)));
+
+    newWsUrl.setFont(font);
+    editWsUrl.setFont(font);
+    deleteWsUrl.setFont(font);
+    moveUrlUp.setFont(font);
+    moveUrlDown.setFont(font);
+    JPanel editContainer = new JPanel();
+    editContainer.add(newWsUrl);
+    editContainer.add(editWsUrl);
+    editContainer.add(deleteWsUrl);
+    urlPaneContainer.add(editContainer, BorderLayout.PAGE_END);
+
+    JPanel moveContainer = new JPanel();
+    moveContainer
+        .setLayout(new BoxLayout(moveContainer, BoxLayout.PAGE_AXIS));
+    moveContainer.add(moveUrlUp);
+    moveContainer.add(Box.createRigidArea(new Dimension(0, 5)));
+    moveContainer.add(moveUrlDown);
+    urlPaneContainer.add(moveContainer, BorderLayout.LINE_START);
+
+    urlPaneContainer.add(new JScrollPane(urlListTable),
+        BorderLayout.CENTER);
+    this.add(urlPaneContainer);
+
+    // Connecting action listeners
+    urlListTable.addMouseListener(tableClickListener);
+    newWsUrl.addActionListener(newUrlAction);
+    editWsUrl.addActionListener(editUrlAction);
+    deleteWsUrl.addActionListener(deleteUrlAction);
+    moveUrlUp.addActionListener(moveUrlUpAction);
+    moveUrlDown.addActionListener(moveUrlDownAction);
+  }
+
+  private void moveTableRow(int fromIndex, int toIndex)
+  {
+    String url = urls.get(fromIndex);
+    int status = statuses.get(fromIndex);
+    urls.set(fromIndex, urls.get(toIndex));
+    urls.set(toIndex, url);
+    if (urlListTable.getSelectedRow() == fromIndex)
+    {
+      urlListTable.setRowSelectionInterval(toIndex, toIndex);
+    }
+    int firstRow = Math.min(toIndex, fromIndex);
+    int lastRow = Math.max(fromIndex, toIndex);
+    urlTableModel.fireTableRowsUpdated(firstRow, lastRow);
+  }
+
+  // Discoverer reloading buttons
+  JButton refreshServices = new JButton(
+      MessageManager.getString("action.refresh_services"));
+
+  JButton resetServices = new JButton(
+      MessageManager.getString("action.reset_services"));
+
+  JProgressBar progressBar = new JProgressBar();
+
+  // Discoverer buttons action listeners
+  private ActionListener refreshServicesAction = (ActionEvent e) -> {
+    progressBar.setVisible(true);
+    Console.info("Requesting service reload");
+    discoverer.startDiscoverer().handle((_discoverer, exception) -> {
+      if (exception == null)
+      {
+        Console.info("Reloading done");
+      }
+      else
+      {
+        Console.error("Reloading failed", exception);
+      }
+      SwingUtilities.invokeLater(() -> progressBar.setVisible(false));
+      return null;
+    });
+  };
+
+  private ActionListener resetServicesAction = (ActionEvent e) -> {
+    discoverer.setServiceUrls(null);
+    urls.clear();
+    statuses.clear();
+    urls.addAll(discoverer.getServiceUrls());
+    for (String url : urls)
+    {
+      reloadStatusForUrl(url);
+    }
+    urlTableModel.fireTableDataChanged();
+  };
+
+  {
+    Font font = new Font("Verdana", Font.PLAIN, 11);
+    refreshServices.setFont(font);
+    resetServices.setFont(font);
+    JPanel container = new JPanel();
+    container.add(refreshServices);
+    container.add(resetServices);
+    this.add(container);
+
+    // Connecting action listeners
+    refreshServices.addActionListener(refreshServicesAction);
+    resetServices.addActionListener(resetServicesAction);
+  }
+
+  {
+    progressBar.setVisible(false);
+    progressBar.setIndeterminate(true);
+    add(progressBar);
+  }
+
+  SlivkaPreferences()
+  {
+    // Initial URLs loading
+    discoverer = SlivkaWSDiscoverer.getInstance();
+    urls.addAll(discoverer.getServiceUrls());
+    for (String url : urls)
+    {
+      reloadStatusForUrl(url);
+    }
+  }
+
+  private void reloadStatusForUrl(String url)
+  {
+    CompletableFuture.supplyAsync(() -> discoverer.getServerStatusFor(url))
+        .thenAccept((status) -> {
+          statuses.put(url, status);
+          int row = urls.indexOf(url);
+          if (row >= 0)
+            urlTableModel.fireTableCellUpdated(row, 1);
+        });
+  }
+}
index 61273c7..f0eee54 100755 (executable)
@@ -42,18 +42,28 @@ import javax.swing.event.HyperlinkListener;
 import jalview.util.ChannelProperties;
 import jalview.util.Platform;
 
+import javajs.async.SwingJSUtils.StateHelper;
+import javajs.async.SwingJSUtils.StateMachine;
+
 /**
  * DOCUMENT ME!
  * 
  * @author $author$
  * @version $Revision$
  */
+@SuppressWarnings("serial")
 public class SplashScreen extends JPanel
-        implements Runnable, HyperlinkListener
+        implements HyperlinkListener, StateMachine
 {
+  
+  private static final int STATE_INIT = 0;
+
+  private static final int STATE_LOOP = 1;
+
+  private static final int STATE_DONE = 2;
   private static final int SHOW_FOR_SECS = 5;
 
-  private static final int FONT_SIZE = 11;
+  private static final int FONT_SIZE = (Platform.isJS() ? 14 : 11);
 
   private boolean visible = true;
 
@@ -67,6 +77,8 @@ public class SplashScreen extends JPanel
 
   private static Font font = new Font("SansSerif", Font.PLAIN, FONT_SIZE);
 
+  private JPanel imgPanel = new JPanel(new BorderLayout());
+
   /*
    * as JTextPane in Java, JLabel in javascript
    */
@@ -74,12 +86,13 @@ public class SplashScreen extends JPanel
 
   private JInternalFrame iframe;
 
-  private Image image;
+  private Image image, logo;
 
   private boolean transientDialog = false;
 
   private long oldTextLength = -1;
 
+  private StateHelper helper;
   public static int logoSize = 32;
 
   /*
@@ -95,7 +108,6 @@ public class SplashScreen extends JPanel
       {
         try
         {
-          visible = false;
           closeSplash();
         } catch (Exception ex)
         {
@@ -107,240 +119,194 @@ public class SplashScreen extends JPanel
   /**
    * Constructor that displays the splash screen
    * 
-   * @param isTransient
+   * @param isStartup
    *          if true the panel removes itself on click or after a few seconds;
-   *          if false it stays up until closed by the user
+   *          if false it stays up until closed by the user (from Help..About menu)
    */
-  public SplashScreen(boolean isTransient)
+  public SplashScreen(boolean isStartup)
   {
-    this.transientDialog = isTransient;
+    this.transientDialog = isStartup;
+    // we must get the image in JavaScript BEFORE starting the helper,
+    // as it will take a 1 ms clock tick to obtain width and height information.
+    image = ChannelProperties.getImage("banner");
+    logo = ChannelProperties.getImage("logo.48");
+    font = new Font("SansSerif", Font.PLAIN, FONT_SIZE);
+    helper = new StateHelper(this);
+    helper.next(STATE_INIT);
+  }
 
-    if (Platform.isJS()) // BH 2019
-    {
-      splashText = new JLabel("");
-      run();
-    }
-    else
-    {
-      /**
-       * Java only
-       *
-       * @j2sIgnore
-       */
-      {
-        splashText = new JTextPane();
-        splashText.setBackground(bg);
-        splashText.setForeground(fg);
-        splashText.setFont(font);
-        Thread t = new Thread(this);
-        t.start();
-      }
-    }
+  protected void initSplashScreenWindow()
+  {
+    addMouseListener(closer);
+    waitForImages();
+    setLayout(new BorderLayout());
+    iframe = new JInternalFrame();
+    iframe.setFrameIcon(null);
+    iframe.setClosable(true);
+    iframe.setContentPane(this);
+    iframe.setLayer(JLayeredPane.PALETTE_LAYER);  
+    SplashImage splashimg = new SplashImage(image);
+    imgPanel.add(splashimg, BorderLayout.CENTER);
+    add(imgPanel, BorderLayout.NORTH);
+    Desktop.getDesktopPane().add(iframe);
+    refreshText();
   }
 
   /**
-   * ping the jalview version page then create and display the jalview
-   * splashscreen window.
+   * Both Java and JavaScript have to wait for images, but this method will
+   * accomplish nothing for JavaScript. We have already taken care of image
+   * loading with our state loop in JavaScript.
+   * 
    */
-  void initSplashScreenWindow()
+  private void waitForImages()
   {
-    addMouseListener(closer);
-
-    try
+    if (Platform.isJS())
+      return;
+    MediaTracker mt = new MediaTracker(this);
+    mt.addImage(image, 0);
+    mt.addImage(logo, 1);
+    do
     {
-      if (!Platform.isJS())
+      try
+      {
+        mt.waitForAll();
+      } catch (InterruptedException x)
       {
-        image = ChannelProperties.getImage("banner");
-        Image logo = ChannelProperties.getImage("logo.48");
-        MediaTracker mt = new MediaTracker(this);
-        if (image != null)
-        {
-          mt.addImage(image, 0);
-        }
-        if (logo != null)
-        {
-          mt.addImage(logo, 1);
-        }
-        do
-        {
-          try
-          {
-            mt.waitForAll();
-          } catch (InterruptedException x)
-          {
-          }
-          if (mt.isErrorAny())
-          {
-            System.err.println("Error when loading images!");
-          }
-        } while (!mt.checkAll());
-        Desktop.instance.setIconImages(ChannelProperties.getIconList());
       }
-    } catch (Exception ex)
+      if (mt.isErrorAny())
+      {
+        System.err.println("Error when loading images!");
+        break;
+      }
+    } while (!mt.checkAll());
+    if (logo != null)
     {
+      Desktop.getInstance().setIconImage(logo);
     }
 
     this.setBackground(bg);
     this.setForeground(fg);
     this.setFont(font);
+  }
 
-    iframe = new JInternalFrame();
-    iframe.setFrameIcon(null);
-    iframe.setClosable(true);
-    this.setLayout(new BorderLayout());
-    iframe.setContentPane(this);
-    iframe.setLayer(JLayeredPane.PALETTE_LAYER);
-    iframe.setBackground(bg);
-    iframe.setForeground(fg);
-    iframe.setFont(font);
-
-    if (Platform.isJS())
+  /**
+   * update text in author text panel reflecting current version information
+   */
+  protected boolean refreshText()
+  {
+    String newtext = Desktop.getInstance().getAboutMessage();
+    // System.err.println("Text found: \n"+newtext+"\nEnd of newtext.");
+    if (oldTextLength == newtext.length())
     {
-      // ignore in JavaScript
+      return false;
+    }
+  
+    iframe.setVisible(false);
+    oldTextLength = newtext.length();
+    if (Platform.isJS()) // BH 2019
+    {
+      /*
+       * SwingJS doesn't have HTMLEditorKit, required for a JTextPane
+       * to display formatted html, so we use a simple alternative
+       */
+      String text = "<html><br><img src=\""
+              + ChannelProperties.getImageURL("banner") + "\"/>" + newtext
+              + "<br></html>";
+      JLabel ta = new JLabel(text);
+      ta.setOpaque(true);
+      ta.setBackground(Color.white);
+      splashText = ta;
     }
     else
     /**
      * Java only
-     * 
+     *
      * @j2sIgnore
      */
     {
-      ((JTextPane) splashText).setEditable(false);
-      splashText.setBackground(bg);
-      splashText.setForeground(fg);
-      splashText.setFont(font);
-
-      SplashImage splashimg = new SplashImage(image);
-      iconimg.add(splashimg, BorderLayout.LINE_START);
-      iconimg.setBackground(bg);
-      add(iconimg, BorderLayout.NORTH);
+      JTextPane jtp = new JTextPane();
+      jtp.setEditable(false);
+      jtp.setBackground(bg);
+      jtp.setForeground(fg);
+      jtp.setFont(font);
+      jtp.setContentType("text/html");
+      jtp.setText("<html>" + newtext + "</html>");
+      jtp.addHyperlinkListener(this);
+      splashText = jtp;
     }
-    add(splashText, BorderLayout.CENTER);
     splashText.addMouseListener(closer);
-    Desktop.desktop.add(iframe);
-    refreshText();
+
+    splashText.setVisible(true);
+    splashText.setSize(new Dimension(750,
+            375 + logoSize + (Platform.isJS() ? 40 : 0)));
+    splashText.setBackground(bg);
+    splashText.setForeground(fg);
+    splashText.setFont(font);
+    add(splashText, BorderLayout.CENTER);
+    revalidate();
+    int width = Math.max(splashText.getWidth(), iconimg.getWidth());
+    int height = splashText.getHeight() + iconimg.getHeight();
+    iframe.setBounds((iframe.getParent().getWidth() - width) / 2,
+            (iframe.getParent().getHeight() - height) / 2,
+            width,height);
+    iframe.validate();
+    iframe.setVisible(true);
+    return true;
   }
 
-  /**
-   * update text in author text panel reflecting current version information
-   */
-  protected boolean refreshText()
+  protected void closeSplash()
   {
-    String newtext = Desktop.instance.getAboutMessage();
-    // System.err.println("Text found: \n"+newtext+"\nEnd of newtext.");
-    if (oldTextLength != newtext.length())
+    try
+    {
+
+      iframe.setClosed(true);
+    } catch (Exception ex)
     {
-      iframe.setVisible(false);
-      oldTextLength = newtext.length();
-      if (Platform.isJS()) // BH 2019
-      {
-        /*
-         * SwingJS doesn't have HTMLEditorKit, required for a JTextPane
-         * to display formatted html, so we use a simple alternative
-         */
-        String text = "<html><br><img src=\""
-                + ChannelProperties.getImageURL("banner") + "\"/>" + newtext
-                + "<br></html>";
-        JLabel ta = new JLabel(text);
-        ta.setOpaque(true);
-        ta.setBackground(Color.white);
-        splashText = ta;
-      }
-      else
-      /**
-       * Java only
-       *
-       * @j2sIgnore
-       */
-      {
-        JTextPane jtp = new JTextPane();
-        jtp.setEditable(false);
-        jtp.setBackground(bg);
-        jtp.setForeground(fg);
-        jtp.setFont(font);
-        jtp.setContentType("text/html");
-        jtp.setText("<html>" + newtext + "</html>");
-        jtp.addHyperlinkListener(this);
-        splashText = jtp;
-      }
-      splashText.addMouseListener(closer);
-
-      splashText.setVisible(true);
-      splashText.setSize(new Dimension(750,
-              425 + logoSize + (Platform.isJS() ? 40 : 0)));
-      splashText.setBackground(bg);
-      splashText.setForeground(fg);
-      splashText.setFont(font);
-      add(splashText, BorderLayout.CENTER);
-      revalidate();
-      int width = Math.max(splashText.getWidth(), iconimg.getWidth());
-      int height = splashText.getHeight() + iconimg.getHeight();
-      iframe.setBounds(
-              Math.max(0, (Desktop.instance.getWidth() - width) / 2),
-              Math.max(0, (Desktop.instance.getHeight() - height) / 2),
-              width, height);
-      iframe.validate();
-      iframe.setVisible(true);
-      return true;
     }
-    return false;
   }
 
   /**
-   * Create splash screen, display it and clear it off again.
+   * A simple state machine with just three states: init, loop, and done. Ideal
+   * for a simple while/sleep loop that works in Java and JavaScript
+   * identically.
+   * 
    */
   @Override
-  public void run()
+  public boolean stateLoop()
   {
-    initSplashScreenWindow();
-
-    long startTime = System.currentTimeMillis() / 1000;
-
-    while (visible)
+    while (true)
     {
-      iframe.repaint();
-      try
+      switch (helper.getState())
       {
-        Thread.sleep(500);
-      } catch (Exception ex)
-      {
-      }
-
-      if (transientDialog && ((System.currentTimeMillis() / 1000)
-              - startTime) > SHOW_FOR_SECS)
-      {
-        visible = false;
-      }
-
-      if (visible && refreshText())
-      {
-        iframe.repaint();
-      }
-      if (!transientDialog)
-      {
-        return;
+      case STATE_INIT:
+        initSplashScreenWindow();
+        helper.setState(STATE_LOOP);
+        continue;
+      case STATE_LOOP:
+        if (!isVisible())
+        {
+          helper.setState(STATE_DONE);
+          continue;
+        }
+        if (refreshText())
+        {
+          iframe.repaint();
+        }
+        if (transientDialog)
+          helper.delayedState(SHOW_FOR_SECS * 1000, STATE_DONE);
+        return true;
+      default:
+      case STATE_DONE:
+        setVisible(false);
+        closeSplash();
+        Desktop.getInstance().startDialogQueue();
+        return true;
       }
     }
 
-    closeSplash();
-    Desktop.instance.startDialogQueue();
-  }
-
-  /**
-   * DOCUMENT ME!
-   */
-  public void closeSplash()
-  {
-    try
-    {
-
-      iframe.setClosed(true);
-    } catch (Exception ex)
-    {
-    }
   }
 
-  public class SplashImage extends JPanel
+  private class SplashImage extends JPanel
   {
     Image image;
 
index 6ebedb7..0c83625 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.AlignViewportI;
+import jalview.api.AlignViewControllerGuiI;
+import jalview.api.FeatureSettingsControllerI;
+import jalview.api.SplitContainerI;
+import jalview.controller.FeatureSettingsControllerGuiI;
+import jalview.datamodel.AlignmentI;
+import jalview.jbgui.GAlignFrame;
+import jalview.jbgui.GSplitFrame;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.viewmodel.AlignmentViewport;
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
@@ -49,17 +61,6 @@ import javax.swing.event.ChangeListener;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
-import jalview.api.AlignViewControllerGuiI;
-import jalview.api.FeatureSettingsControllerI;
-import jalview.api.SplitContainerI;
-import jalview.controller.FeatureSettingsControllerGuiI;
-import jalview.datamodel.AlignmentI;
-import jalview.jbgui.GAlignFrame;
-import jalview.jbgui.GSplitFrame;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.viewmodel.AlignmentViewport;
 
 /**
  * An internal frame on the desktop that hosts a horizontally split view of
@@ -152,7 +153,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     // allow about 65 pixels for Desktop decorators on Windows
 
     int newHeight = Math.min(height,
-            Desktop.instance.getHeight() - DESKTOP_DECORATORS_HEIGHT);
+            Desktop.getInstance().getHeight() - DESKTOP_DECORATORS_HEIGHT);
     if (newHeight != height)
     {
       int oldDividerLocation = getDividerLocation();
@@ -170,7 +171,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     // TODO if CommandListener is only ever 1:1 for complementary views,
     // may change broadcast pattern to direct messaging (more efficient)
     final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.addCommandListener(((AlignFrame) getTopFrame()).getViewport());
     ssm.addCommandListener(((AlignFrame) getBottomFrame()).getViewport());
   }
@@ -230,8 +231,8 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     topFrame.alignPanel.adjustAnnotationHeight();
     bottomFrame.alignPanel.adjustAnnotationHeight();
 
-    final AlignViewport topViewport = topFrame.viewport;
-    final AlignViewport bottomViewport = bottomFrame.viewport;
+    final AlignViewportI topViewport = topFrame.viewport;
+    final AlignViewportI bottomViewport = bottomFrame.viewport;
     final AlignmentI topAlignment = topViewport.getAlignment();
     final AlignmentI bottomAlignment = bottomViewport.getAlignment();
     boolean topAnnotations = topViewport.isShowAnnotation();
@@ -259,8 +260,8 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * calculate the maximum ratio that leaves at least the height 
      * of two sequences (after rounding) visible in the bottom panel
      */
-    int bottomSequencesHeight = bottomFrame.alignPanel
-            .getSeqPanel().seqCanvas.getHeight();
+    int bottomSequencesHeight = bottomFrame.alignPanel.getSeqPanel().seqCanvas
+            .getHeight();
     int bottomPanelMinHeight = bottomPanelHeight
             - Math.max(0, bottomSequencesHeight - 3 * bottomCharHeight);
     double maxRatio = (totalHeight - bottomPanelMinHeight) / totalHeight;
@@ -427,9 +428,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-W / Cmd-W - close view or window
      */
     KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     action = new AbstractAction()
     {
       @Override
@@ -450,9 +449,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-T / Cmd-T open new view
      */
     KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     AbstractAction action = new AbstractAction()
     {
       @Override
@@ -575,7 +572,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     adjustLayout();
 
     final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.addCommandListener(newTopPanel.av);
     ssm.addCommandListener(newBottomPanel.av);
   }
@@ -702,7 +699,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
    */
   protected void expandViews_actionPerformed()
   {
-    Desktop.instance.explodeViews(this);
+    Desktop.getInstance().explodeViews(this);
   }
 
   /**
@@ -711,7 +708,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
    */
   protected void gatherViews_actionPerformed()
   {
-    Desktop.instance.gatherViews(this);
+    Desktop.getInstance().gatherViews(this);
   }
 
   /**
@@ -808,9 +805,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-F / Cmd-F open Finder dialog, 'focused' on the right alignment
      */
     KeyStroke key_cmdF = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     AbstractAction action = new AbstractAction()
     {
       @Override
@@ -857,8 +852,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
-   * holds the frame for feature settings, so Protein and DNA tabs can be
-   * managed
+   * holds the frame for feature settings, so Protein and DNA tabs can be managed
    */
   JInternalFrame featureSettingsUI;
 
@@ -1111,4 +1105,4 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   {
     return featureSettingsUI != null && !featureSettingsUI.isClosed();
   }
-}
\ No newline at end of file
+}
index 07eec2b..4fb9e22 100644 (file)
@@ -35,6 +35,7 @@ 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;
@@ -79,13 +80,12 @@ import jalview.ws.sifts.SiftsSettings;
 public class StructureChooser extends GStructureChooser
         implements IProgressIndicator
 {
-  private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
+  protected static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
   /**
    * warn user if need to fetch more than this many uniprot records at once
    */
   private static final int THRESHOLD_WARN_UNIPROT_FETCH_NEEDED = 20;
-
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
@@ -116,7 +116,7 @@ public class StructureChooser extends GStructureChooser
 
   List<SequenceI> seqsWithoutSourceDBRef = null;
 
-  private static StructureViewer lastTargetedView = null;
+  protected static StructureViewer lastTargetedView = null;
 
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
@@ -277,7 +277,7 @@ public class StructureChooser extends GStructureChooser
         previousWantedFields = null;
         lastSelected = (FilterOption) cmb_filterOption.getSelectedItem();
         cmb_filterOption.setSelectedItem(null);
-        cachedPDBExists = false; // reset to initial
+        cachedPDBExists=false; // reset to initial
         initialStructureDiscovery();
         if (!isStructuresDiscovered())
         {
@@ -303,7 +303,7 @@ public class StructureChooser extends GStructureChooser
 
     final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
     {
-
+      
       @Override
       public void finished()
       {
@@ -311,12 +311,12 @@ public class StructureChooser extends GStructureChooser
         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()
+    final Runnable discoverCanonicalDBrefs = new Runnable() 
     {
       @Override
       public void run()
@@ -334,7 +334,6 @@ public class StructureChooser extends GStructureChooser
                   { new jalview.ws.dbsources.Uniprot() }, null, false);
           dbRefFetcher.addListener(afterDbRefFetch);
           // ideally this would also gracefully run with callbacks
-
           dbRefFetcher.fetchDBRefs(true);
         }
         else
@@ -361,13 +360,11 @@ public class StructureChooser extends GStructureChooser
     Console.debug("Using Uniprot fetch threshold of " + threshold);
     if (ignoreGui || seqsWithoutSourceDBRef.size() < threshold)
     {
-      Executors.defaultThreadFactory().newThread(discoverCanonicalDBrefs)
-              .start();
+      Executors.defaultThreadFactory().newThread(discoverCanonicalDBrefs).start();
       return;
     }
     // need cancel and no to result in the discoverPDB action - mocked is
     // 'cancel' TODO: mock should be OK
-
     StructureChooser thisSC = this;
     JvOptionPane.newOptionDialog(thisSC.getFrame())
             .setResponseHandler(JvOptionPane.OK_OPTION,
@@ -378,7 +375,8 @@ public class StructureChooser extends GStructureChooser
                     MessageManager.formatMessage(
                             "label.fetch_references_for_3dbeacons",
                             seqsWithoutSourceDBRef.size()),
-                    MessageManager.getString("label.3dbeacons"),
+                    MessageManager
+                            .getString("label.3dbeacons"),
                     JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
                     null, new Object[]
                     { MessageManager.getString("action.ok"),
@@ -393,7 +391,7 @@ public class StructureChooser extends GStructureChooser
    */
   private void discoverStructureViews()
   {
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
       targetView.removeAllItems();
       if (lastTargetedView != null && !lastTargetedView.isVisible())
@@ -401,7 +399,7 @@ public class StructureChooser extends GStructureChooser
         lastTargetedView = null;
       }
       int linkedViewsAt = 0;
-      for (StructureViewerBase view : Desktop.instance
+      for (StructureViewerBase view : Desktop.getInstance()
               .getStructureViewers(null, null))
       {
         StructureViewer viewHandler = (lastTargetedView != null
@@ -752,7 +750,7 @@ public class StructureChooser extends GStructureChooser
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
-
+    
     if (lastSelected == selectedFilterOpt)
     {
       // don't need to do anything, probably
@@ -767,7 +765,7 @@ public class StructureChooser extends GStructureChooser
     String filterTitle = mainFrame.getTitle();
     mainFrame.setTitle(frameTitle);
     chk_invertFilter.setVisible(false);
-
+    
     if (selectedFilterOpt.getView() == VIEWS_FILTER)
     {
       mainFrame.setTitle(filterTitle);
@@ -858,11 +856,9 @@ public class StructureChooser extends GStructureChooser
     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);
@@ -891,7 +887,6 @@ public class StructureChooser extends GStructureChooser
     // event not handled by us
     return false;
   }
-
   /**
    * Validates inputs from the Manual PDB entry panel
    */
@@ -963,9 +958,7 @@ public class StructureChooser extends GStructureChooser
   {
     validateSelections();
   }
-
-  private FilterOption lastSelected = null;
-
+  private FilterOption lastSelected=null;
   /**
    * Handles the state change event for the 'filter' combo-box and 'invert'
    * check-box
@@ -1054,7 +1047,8 @@ public class StructureChooser extends GStructureChooser
   {
 
     final StructureSelectionManager ssm = ap.getStructureSelectionManager();
-
+    final StructureViewer theViewer = getTargetedStructureViewer(ssm);
+    boolean superimpose = chk_superpose.isSelected(); 
     final int preferredHeight = pnl_filter.getHeight();
 
     Runnable viewStruc = new Runnable()
@@ -1078,8 +1072,8 @@ public class StructureChooser extends GStructureChooser
 
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
-                  selectedSeqs);
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
+                  selectedSeqs, superimpose, theViewer, progressBar);
         }
         else if (currentView == VIEWS_LOCAL_PDB)
         {
@@ -1103,8 +1097,8 @@ public class StructureChooser extends GStructureChooser
           }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
-                  selectedSeqs);
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
+                  selectedSeqs, superimpose, theViewer, progressBar);
         }
         else if (currentView == VIEWS_ENTER_ID)
         {
@@ -1122,8 +1116,7 @@ public class StructureChooser extends GStructureChooser
             if (pdbIdStr.split(":").length > 1)
             {
               pdbEntry.setId(pdbIdStr.split(":")[0]);
-              pdbEntry.setChainCode(
-                      pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
+              pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
             }
             else
             {
@@ -1134,9 +1127,10 @@ public class StructureChooser extends GStructureChooser
           }
 
           PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
-                  new SequenceI[]
-                  { selectedSequence });
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
+                   new SequenceI[]
+                  { selectedSequence }, superimpose, theViewer,
+                  progressBar);
         }
         else if (currentView == VIEWS_FROM_FILE)
         {
@@ -1146,14 +1140,12 @@ public class StructureChooser extends GStructureChooser
           {
             selectedSequence = userSelectedSeq;
           }
-          PDBEntry fileEntry = new AssociatePdbFileWithSeq()
-                  .associatePdbWithSeq(selectedPdbFileName,
-                          DataSourceType.FILE, selectedSequence, true,
-                          Desktop.instance);
-
-          sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry },
-                  ap, new SequenceI[]
-                  { selectedSequence });
+          PDBEntry fileEntry =  AssociatePdbFileWithSeq.associatePdbWithSeq(selectedPdbFileName,
+                          DataSourceType.FILE, selectedSequence, true);
+
+          sViewer = StructureViewer.launchStructureViewer(ap,  new PDBEntry[] { fileEntry },
+                 new SequenceI[] { selectedSequence }, superimpose, theViewer,
+                  progressBar);
         }
         SwingUtilities.invokeLater(new Runnable()
         {
@@ -1200,98 +1192,6 @@ public class StructureChooser extends GStructureChooser
   }
 
   /**
-   * Adds PDB structures to a new or existing structure viewer
-   * 
-   * @param ssm
-   * @param pdbEntriesToView
-   * @param alignPanel
-   * @param sequences
-   * @return
-   */
-  private StructureViewer launchStructureViewer(
-          StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
-          final AlignmentPanel alignPanel, SequenceI[] sequences)
-  {
-    long progressId = sequences.hashCode();
-    setProgressBar(MessageManager
-            .getString("status.launching_3d_structure_viewer"), progressId);
-    final StructureViewer theViewer = getTargetedStructureViewer(ssm);
-    boolean superimpose = chk_superpose.isSelected();
-    theViewer.setSuperpose(superimpose);
-
-    /*
-     * remember user's choice of superimpose or not
-     */
-    Cache.setProperty(AUTOSUPERIMPOSE,
-            Boolean.valueOf(superimpose).toString());
-
-    setProgressBar(null, progressId);
-    if (SiftsSettings.isMapWithSifts())
-    {
-      List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
-      int p = 0;
-      // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
-      // real PDB ID. For moment, we can also safely do this if there is already
-      // a known mapping between the PDBEntry and the sequence.
-      for (SequenceI seq : sequences)
-      {
-        PDBEntry pdbe = pdbEntriesToView[p++];
-        if (pdbe != null && pdbe.getFile() != null)
-        {
-          StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
-          if (smm != null && smm.length > 0)
-          {
-            for (StructureMapping sm : smm)
-            {
-              if (sm.getSequence() == seq)
-              {
-                continue;
-              }
-            }
-          }
-        }
-        if (seq.getPrimaryDBRefs().isEmpty())
-        {
-          seqsWithoutSourceDBRef.add(seq);
-          continue;
-        }
-      }
-      if (!seqsWithoutSourceDBRef.isEmpty())
-      {
-        int y = seqsWithoutSourceDBRef.size();
-        setProgressBar(MessageManager.formatMessage(
-                "status.fetching_dbrefs_for_sequences_without_valid_refs",
-                y), progressId);
-        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
-                .toArray(new SequenceI[y]);
-        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
-        dbRefFetcher.fetchDBRefs(true);
-
-        setProgressBar("Fetch complete.", progressId); // todo i18n
-      }
-    }
-    if (pdbEntriesToView.length > 1)
-    {
-      setProgressBar(
-              MessageManager.getString(
-                      "status.fetching_3d_structures_for_selected_entries"),
-              progressId);
-      theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
-    }
-    else
-    {
-      setProgressBar(MessageManager.formatMessage(
-              "status.fetching_3d_structures_for",
-              pdbEntriesToView[0].getId()), progressId);
-      theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
-    }
-    setProgressBar(null, progressId);
-    // remember the last viewer we used...
-    lastTargetedView = theViewer;
-    return theViewer;
-  }
-
-  /**
    * Populates the combo-box used in associating manually fetched structures to
    * a unique sequence when more than one sequence selection is made.
    */
@@ -1526,6 +1426,12 @@ public class StructureChooser extends GStructureChooser
   {
     progressBar.setProgressBar(message, id);
   }
+  
+  @Override
+  public void removeProgressBar(long id)
+  {
+    progressBar.removeProgressBar(id);
+  }
 
   @Override
   public void registerHandler(long id, IProgressIndicatorHandler handler)
@@ -1562,7 +1468,6 @@ public class StructureChooser extends GStructureChooser
             && mainFrame.isVisible()
             && cmb_filterOption.getSelectedItem() != null;
   }
-
   /**
    * 
    * @return true if the 3D-Beacons query button will/has been displayed
index 5effa1a..cc4a033 100644 (file)
@@ -26,14 +26,21 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
+import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.seqfetcher.DbSourceProxy;
+import jalview.ws.sifts.SiftsSettings;
+
+
 
 /**
  * A proxy for handling structure viewers, that orchestrates adding selected
@@ -45,6 +52,16 @@ import jalview.structure.StructureSelectionManager;
  */
 public class StructureViewer
 {
+  
+  static
+  {
+    Platform.loadStaticResource("core/core_jvjmol.z.js",
+            "org.jmol.viewer.Viewer");
+  }
+
+
+  
+  
   private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
 
   StructureSelectionManager ssm;
@@ -339,6 +356,9 @@ public class StructureViewer
    * Creates a new panel controlling a structure viewer
    * 
    * @param type
+   * @param pdbf
+   * @param id
+   * @param sq
    * @param alignPanel
    * @param viewerData
    * @param sessionFile
@@ -350,6 +370,7 @@ public class StructureViewer
           String sessionFile, String vid)
   {
     JalviewStructureDisplayI viewer = null;
+
     switch (type)
     {
     case JMOL:
@@ -410,4 +431,119 @@ public class StructureViewer
     superposeAdded = alignAddedStructures;
   }
 
+  /**
+   * Launch a minimal implementation of a StructureViewer.
+   * 
+   * @param alignPanel
+   * @param pdb
+   * @param seqs
+   * @return
+   */
+  public static StructureViewer launchStructureViewer(
+          AlignmentPanel alignPanel, PDBEntry pdb, SequenceI[] seqs)
+  {
+    return launchStructureViewer(alignPanel, new PDBEntry[] { pdb }, seqs,
+            false, null, null);
+  }
+
+  /**
+   * Adds PDB structures to a new or existing structure viewer
+   * 
+   * @param ssm
+   * @param pdbEntriesToView
+   * @param alignPanel
+   * @param sequences
+   * @return
+   */
+  protected static StructureViewer launchStructureViewer(
+          final AlignmentPanel ap, final PDBEntry[] pdbEntriesToView,
+          SequenceI[] sequences, boolean superimpose,
+          StructureViewer theViewer, IProgressIndicator pb)
+  {
+    final StructureSelectionManager ssm = ap.getStructureSelectionManager();
+    if (theViewer == null)
+      theViewer = new StructureViewer(ssm);
+    long progressId = sequences.hashCode();
+    if (pb != null)
+      pb.setProgressBar(MessageManager.getString(
+              "status.launching_3d_structure_viewer"), progressId);
+    theViewer.setSuperpose(superimpose);
+  
+    /*
+     * remember user's choice of superimpose or not
+     */
+    Cache.setProperty(StructureChooser.AUTOSUPERIMPOSE,
+            Boolean.valueOf(superimpose).toString());
+  
+    if (pb != null)
+      pb.setProgressBar(null, progressId);
+    if (SiftsSettings.isMapWithSifts())
+    {
+      List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
+      int p = 0;
+      // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
+      // real PDB ID. For moment, we can also safely do this if there is already
+      // a known mapping between the PDBEntry and the sequence.
+      for (SequenceI seq : sequences)
+      {
+        PDBEntry pdbe = pdbEntriesToView[p++];
+        if (pdbe != null && pdbe.getFile() != null)
+        {
+          StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
+          if (smm != null && smm.length > 0)
+          {
+            for (StructureMapping sm : smm)
+            {
+              if (sm.getSequence() == seq)
+              {
+                continue;
+              }
+            }
+          }
+        }
+        if (seq.getPrimaryDBRefs().isEmpty())
+        {
+          seqsWithoutSourceDBRef.add(seq);
+          continue;
+        }
+      }
+      if (!seqsWithoutSourceDBRef.isEmpty())
+      {
+        int y = seqsWithoutSourceDBRef.size();
+        if (pb != null)
+          pb.setProgressBar(MessageManager.formatMessage(
+                  "status.fetching_dbrefs_for_sequences_without_valid_refs",
+                  y), progressId);
+        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                .toArray(new SequenceI[y]);
+        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
+        dbRefFetcher.fetchDBRefs(true);
+  
+        if (pb != null)
+          pb.setProgressBar("Fetch complete.", progressId); // todo i18n
+      }
+    }
+    if (pdbEntriesToView.length > 1)
+    {
+      if (pb != null)
+        pb.setProgressBar(MessageManager.getString(
+                "status.fetching_3d_structures_for_selected_entries"),
+                progressId);
+      theViewer.viewStructures(pdbEntriesToView, sequences, ap);
+    }
+    else
+    {
+      if (pb != null)
+        pb.setProgressBar(MessageManager.formatMessage(
+                "status.fetching_3d_structures_for",
+                pdbEntriesToView[0].getId()), progressId);
+      theViewer.viewStructures(pdbEntriesToView[0], sequences, ap);
+    }
+    if (pb != null)
+      pb.setProgressBar(null, progressId);
+    // remember the last viewer we used...
+    StructureChooser.lastTargetedView = theViewer;
+    return theViewer;
+  }
+
 }
index ec5579c..090bf5c 100644 (file)
@@ -429,7 +429,7 @@ public abstract class StructureViewerBase extends GStructureViewer
    */
   protected List<StructureViewerBase> getViewersFor(AlignmentPanel alp)
   {
-    return Desktop.instance.getStructureViewers(alp, this.getClass());
+    return Desktop.getInstance().getStructureViewers(alp, this.getClass());
   }
 
   @Override
index 6f143db..619bdd7 100755 (executable)
@@ -756,7 +756,9 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
      */
     if (e.isPopupTrigger())
     {
-      chooseSubtreeColour();
+      if (highlightNode != null) {
+        chooseSubtreeColour();
+      }
       e.consume(); // prevent mouseClicked happening
     }
   }
index d735402..ec4ef7b 100755 (executable)
@@ -190,11 +190,12 @@ public class TreePanel extends GTreePanel
   {
     final PropertyChangeListener listener = new PropertyChangeListener()
     {
+      @SuppressWarnings("unchecked")
       @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
-        if (evt.getPropertyName().equals("alignment"))
-        {
+        switch (evt.getPropertyName()) {
+        case AlignmentViewport.PROPERTY_ALIGNMENT:
           if (tree == null)
           {
             System.out.println("tree is null");
@@ -207,12 +208,14 @@ public class TreePanel extends GTreePanel
           {
             System.out.println(
                     "new alignment sequences vector value is null");
+            return;
           }
 
           tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue());
           treeCanvas.nameHash.clear(); // reset the mapping between canvas
           // rectangles and leafnodes
           repaint();
+          break;
         }
       }
     };
index 1836e33..687d189 100755 (executable)
@@ -151,8 +151,8 @@ public class UserDefinedColours extends GUserDefinedColours
     frame = new JInternalFrame();
     frame.setContentPane(this);
     Desktop.addInternalFrame(frame,
-            MessageManager.getString("label.user_defined_colours"),
-            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
+            MessageManager.getString("label.user_defined_colours"), Desktop.FRAME_MAKE_VISIBLE,
+            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   /**
@@ -452,7 +452,7 @@ public class UserDefinedColours extends GUserDefinedColours
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
@@ -507,7 +507,7 @@ public class UserDefinedColours extends GUserDefinedColours
         String[] options = new String[] { title,
             MessageManager.getString("label.dont_save_changes"), };
         final String question = JvSwingUtils.wrapTooltip(true, message);
-        int response = JvOptionPane.showOptionDialog(Desktop.desktop,
+        int response = JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
                 question, title, JvOptionPane.DEFAULT_OPTION,
                 JvOptionPane.PLAIN_MESSAGE, null, options, options[0]);
 
@@ -561,7 +561,7 @@ public class UserDefinedColours extends GUserDefinedColours
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
@@ -745,7 +745,7 @@ public class UserDefinedColours extends GUserDefinedColours
     String name = schemeName.getText().trim();
     if (name.length() < 1)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.user_colour_scheme_must_have_name"),
               MessageManager.getString("label.no_name_colour_scheme"),
@@ -760,7 +760,7 @@ public class UserDefinedColours extends GUserDefinedColours
        * @j2sIgnore
        */
       {
-        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
                 MessageManager.formatMessage(
                         "label.colour_scheme_exists_overwrite", new Object[]
                         { name, name }),
index 385eb57..b75218e 100644 (file)
@@ -20,9 +20,6 @@
  */
 package jalview.gui;
 
-import jalview.bin.Cache;
-import jalview.bin.Console;
-import jalview.util.MessageManager;
 
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
@@ -30,6 +27,10 @@ import java.net.URL;
 
 import javax.swing.JOptionPane;
 
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.util.MessageManager;
+
 public class UserQuestionnaireCheck implements Runnable
 {
   /**
@@ -142,7 +143,7 @@ public class UserQuestionnaireCheck implements Runnable
         String qurl = url + (url.indexOf('?') > -1 ? "&" : "?") + "qid="
                 + qid + "&rid=" + rid;
         Console.info("Prompting user for questionnaire at " + qurl);
-        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.jalview_new_questionnaire"),
                 MessageManager.getString("label.jalview_user_survey"),
                 JvOptionPane.YES_NO_OPTION, JvOptionPane.QUESTION_MESSAGE);
index 8a8ba8d..1ad7caf 100644 (file)
@@ -174,7 +174,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
           }
         } catch (InvalidSessionDocumentException e)
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+           JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
 
                   MessageManager.getString(
                           "label.vamsas_doc_couldnt_be_opened_as_new_session"),
@@ -453,7 +453,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
             baseProvEntry(), alRedoState);
     // wander through frames
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
 
     if (frames == null)
     {
@@ -656,8 +656,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                 {
                   Console.debug(
                           "Asking user if the vamsas session should be stored.");
-                  int reply = JvOptionPane.showInternalConfirmDialog(
-                          Desktop.desktop,
+                  int reply = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
                           "The current VAMSAS session has unsaved data - do you want to save it ?",
                           "VAMSAS Session Shutdown",
                           JvOptionPane.YES_NO_OPTION,
@@ -666,7 +665,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   if (reply == JvOptionPane.YES_OPTION)
                   {
                     Console.debug("Prompting for vamsas store filename.");
-                    Desktop.instance.vamsasSave_actionPerformed(null);
+                    Desktop.getInstance().vamsasSave_actionPerformed(null);
                     Console.debug("Finished attempt at storing document.");
                   }
                   Console.debug(
@@ -760,7 +759,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
       {
         final IPickManager pm = vclient.getPickManager();
         final StructureSelectionManager ssm = StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance);
+           .getStructureSelectionManager(Desktop.getInstance());
         final VamsasApplication me = this;
         pm.registerMessageHandler(new IMessageHandler()
         {
index ee1b473..4c572a2 100644 (file)
  */
 package jalview.gui;
 
-import java.util.Locale;
+import jalview.jbgui.GWebserviceInfo;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.ChannelProperties;
+import jalview.ws.WSClientI;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -33,6 +37,7 @@ import java.awt.MediaTracker;
 import java.awt.RenderingHints;
 import java.awt.event.ActionEvent;
 import java.awt.image.BufferedImage;
+import java.util.Locale;
 import java.util.Vector;
 
 import javax.swing.JComponent;
@@ -49,11 +54,6 @@ import javax.swing.event.InternalFrameEvent;
 import javax.swing.text.html.HTMLEditorKit;
 import javax.swing.text.html.StyleSheet;
 
-import jalview.jbgui.GWebserviceInfo;
-import jalview.util.ChannelProperties;
-import jalview.util.MessageManager;
-import jalview.ws.WSClientI;
-
 /**
  * Base class for web service client thread and gui TODO: create StAX parser to
  * extract html body content reliably when preparing html formatted job statuses
@@ -262,6 +262,7 @@ public class WebserviceInfo extends GWebserviceInfo
   public WebserviceInfo(String title, String info, int width, int height,
           boolean makeVisible)
   {
+    // no references
     init(title, info, width, height, makeVisible);
   }
 
@@ -325,7 +326,7 @@ public class WebserviceInfo extends GWebserviceInfo
   {
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    Desktop.addInternalFrame(frame, title, makeVisible, width, height);
+    Desktop.addInternalFrame(frame, title, makeVisible, width, height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
     frame.setClosable(false);
 
     progressBar = new ProgressBar(statusPanel, statusBar);
@@ -351,22 +352,27 @@ public class WebserviceInfo extends GWebserviceInfo
     titlePanel.add(titleText, BorderLayout.CENTER);
     setStatus(currentStatus);
 
-    Thread thread = new Thread(ap);
-    thread.start();
-    final WebserviceInfo thisinfo = this;
-    frame.addInternalFrameListener(new InternalFrameAdapter()
+    if (!Platform.isJS())
     {
-      @Override
-      public void internalFrameClosed(InternalFrameEvent evt)
-      {
-        // System.out.println("Shutting down webservice client");
-        WSClientI service = thisinfo.getthisService();
-        if (service != null && service.isCancellable())
-        {
-          service.cancelJob();
-        }
-      }
-    });
+      // No animation for the moment//
+      Thread thread = new Thread(ap);
+      thread.start();
+    }
+    final WebserviceInfo thisinfo = this;
+    frame.addInternalFrameListener(
+            new InternalFrameAdapter()
+            {
+              @Override
+              public void internalFrameClosed(InternalFrameEvent evt)
+              {
+                // System.out.println("Shutting down webservice client");
+                WSClientI service = thisinfo.getthisService();
+                if (service != null && service.isCancellable())
+                {
+                  service.cancelJob();
+                }
+              }
+            });
     frame.validate();
 
   }
@@ -746,7 +752,7 @@ public class WebserviceInfo extends GWebserviceInfo
       @Override
       public void run()
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop, message,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), message,
                 title, JvOptionPane.WARNING_MESSAGE);
 
       }
@@ -928,6 +934,12 @@ public class WebserviceInfo extends GWebserviceInfo
   {
     progressBar.setProgressBar(message, id);
   }
+  
+  @Override
+  public void removeProgressBar(long id)
+  {
+    progressBar.removeProgressBar(id);
+  }
 
   @Override
   public void registerHandler(final long id,
index e1613da..80c21b8 100644 (file)
  */
 package jalview.gui;
 
+import jalview.bin.Console;
+import jalview.gui.OptsAndParamsPage.OptionBox;
+import jalview.gui.OptsAndParamsPage.ParamBox;
+import jalview.util.MessageManager;
+import jalview.ws.api.UIinfo;
+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;
@@ -35,13 +45,11 @@ import java.awt.event.HierarchyBoundsListener;
 import java.awt.event.HierarchyEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowListener;
-import java.net.URL;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
 
 import javax.swing.JButton;
 import javax.swing.JComboBox;
@@ -50,31 +58,12 @@ import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.JSplitPane;
 import javax.swing.JTextArea;
+import javax.swing.WindowConstants;
 import javax.swing.border.TitledBorder;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
-import compbio.metadata.Argument;
-import compbio.metadata.Option;
-import compbio.metadata.Parameter;
-import compbio.metadata.Preset;
-import compbio.metadata.PresetManager;
-import compbio.metadata.RunnerConfig;
-import jalview.bin.Console;
-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;
 
 /**
@@ -95,36 +84,38 @@ import net.miginfocom.swing.MigLayout;
 public class WsJobParameters extends JPanel implements ItemListener,
         ActionListener, DocumentListener, OptsParametersContainerI
 {
-  URL linkImageURL = getClass().getResource("/images/link.gif");
+  private static final int PREFERRED_WIDTH = 540;
+
+  private static final int DEFAULT_HEIGHT = 640;
+
+  // the default parameter set shown to the user
+  private static final String SVC_DEF = "Defaults";
+
+  private int maxOptWidth = 200;
 
-  private static final String SVC_DEF = "Defaults"; // this is the null
-                                                    // parameter set as shown to
-                                                    // user
+  // URL linkImageURL = getClass().getResource("/images/link.gif");
 
+  // TODO ABSRACT FROM JABAWS CLASSES
+
+  // completion stage representing whether start was clicked
+  private final CompletableFuture<Boolean> completionStage = new CompletableFuture<>();
+  
   /**
    * manager for options and parameters.
    */
-  OptsAndParamsPage opanp = new OptsAndParamsPage(this);
+  OptsAndParamsPage opanp;
 
-  /**
+  /*
    * panel containing job options
    */
-  JPanel jobOptions = new JPanel();
+  JPanel optionsPanel = new JPanel();
 
-  /**
+  /*
    * panel containing job parameters
    */
-  JPanel paramList = new JPanel();
-
-  JPanel SetNamePanel = new JPanel();
+  JPanel paramsPanel = new JPanel();
 
-  JPanel setDetails = new JPanel();
-
-  JSplitPane settingsPanel = new JSplitPane();
-
-  JPanel jobPanel = new JPanel();
-
-  JScrollPane jobOptionsPane = new JScrollPane();
+  JPanel setNamePanel = new JPanel();
 
   JButton createpref = new JButton();
 
@@ -134,93 +125,103 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
   JButton updatepref = new JButton();
 
-  JButton startjob = new JButton();
-
-  JButton canceljob = new JButton();
-
-  JComboBox setName = new JComboBox();
+  JComboBox<String> setName = new JComboBox<>();
 
   JTextArea setDescr = new JTextArea();
 
   JScrollPane paramPane = new JScrollPane();
-
-  // ScrollablePanel optsAndparams = new ScrollablePanel();
-  JPanel optsAndparams = new JPanel();
-
-  RunnerConfig serviceOptions;
+  
+  JButton startjob = JvSwingUtils.makeButton(
+          MessageManager.getString("action.start_job"),
+          MessageManager.getString("label.start_job_current_settings"),
+          this::startjob_actionPerformed);
+  JButton canceljob = JvSwingUtils.makeButton(
+          MessageManager.getString("action.cancel_job"),
+          MessageManager.getString("label.cancel_job_close_dialog"),
+          this::canceljob_actionPerformed);
 
   ParamDatastoreI paramStore;
 
-  private int MAX_OPTWIDTH = 200;
+  // set true when 'Start Job' is clicked
+  boolean startJob = false;
 
-  WsJobParameters(Jws2Instance service)
-  {
-    this(service, null);
-  }
+  JFrame frame = null;
 
-  public WsJobParameters(Jws2Instance service, WsParamSetI preset)
-  {
-    this(null, service, preset, null);
-  }
+  UIinfo service;
 
-  /**
-   * 
-   * @param desktop
-   *          - if null, create new JFrame outside of desktop
-   * @param service
-   * @param preset
+  /*
+   * list of service presets in the gui
+   */
+  Hashtable<String, String> servicePresets = null;
+
+  /*
+   * set if dialog is being set - so handlers will avoid spurious events
    */
-  public WsJobParameters(JFrame parent, Jws2Instance service,
-          WsParamSetI preset, List<Argument> jobArgset)
+  boolean settingDialog = false;
+
+  private Hashtable<Object, Object> modifiedElements = new Hashtable<>();
+
+  String lastParmSet = null;
+
+  public WsJobParameters(ParamDatastoreI store, WsParamSetI preset,
+          List<ArgumentI> args)
   {
-    this(parent, null, service, preset, jobArgset);
+    super();
+
+    // parameters dialog in 'compact' format (help as tooltips)
+    opanp = new OptsAndParamsPage(this, true);
+    jbInit();
+    this.paramStore = store;
+    this.service = null;
+    init(preset, args);
+    validate();
   }
 
   /**
+   * Constructor given a set of parameters and presets, a service to be invoked,
+   * and a list of (Jabaws client) arguments
    * 
-   * @param parent
    * @param paramStorei
    * @param service
    * @param preset
    * @param jobArgset
    */
-  public WsJobParameters(JFrame parent, ParamDatastoreI paramStorei,
-          Jws2Instance service, WsParamSetI preset,
-          List<Argument> jobArgset)
+  public WsJobParameters(ParamDatastoreI paramStorei, UIinfo service,
+          WsParamSetI preset, List<ArgumentI> jobArgset)
   {
     super();
+    // parameters dialog in 'expanded' format (help text boxes)
+    opanp = new OptsAndParamsPage(this, false);
     jbInit();
     this.paramStore = paramStorei;
-    if (paramStore == null)
+    if (paramStore == null && service != null)
     {
       paramStore = service.getParamStore();
     }
     this.service = service;
-    // argSetModified(false);
-    // populate parameter table
-    initForService(service, preset, jobArgset);
-    // display in new JFrame attached to parent.
+    initForService(preset, jobArgset);
     validate();
   }
 
-  int response = -1;
-
-  JDialog frame = null;
 
   /**
-   * shows a modal dialog containing the parameters.
+   * Shows a modal dialog containing the parameters and Start or Cancel options.
+   * Answers true if the job is started, false if cancelled.
    * 
    * @return
    */
-  public boolean showRunDialog()
+  public CompletionStage<Boolean> showRunDialog()
   {
 
-    frame = new JDialog(Desktop.instance, true);
-
-    frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
-            new String[]
-            { service.getActionText() }));
-    Rectangle deskr = Desktop.instance.getBounds();
+    // Should JFrame hahve a parent of getDesktop ?
+    frame = new JFrame();
+    frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+    if (service != null)
+    {
+      frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
+              new String[] { service.getActionText() }));
+    }
+    Rectangle deskr = Desktop.getInstance().getBounds();
     Dimension pref = this.getPreferredSize();
     frame.setBounds(
             new Rectangle((int) (deskr.getCenterX() - pref.width / 2),
@@ -240,13 +241,10 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
       }
     });
+    
     frame.setVisible(true);
 
-    if (response > 0)
-    {
-      return true;
-    }
-    return false;
+    return completionStage;
   }
 
   private void jbInit()
@@ -270,75 +268,23 @@ public class WsJobParameters extends JPanel implements ItemListener,
     updatepref = JvSwingUtils.makeButton(
             MessageManager.getString("action.update"),
             MessageManager.getString("label.update_user_parameter_set"),
-            new ActionListener()
-            {
-
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                update_actionPerformed(e);
-              }
-            });
+            this::update_actionPerformed);
     deletepref = JvSwingUtils.makeButton(
             MessageManager.getString("action.delete"),
             MessageManager.getString("label.delete_user_parameter_set"),
-            new ActionListener()
-            {
-
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                delete_actionPerformed(e);
-              }
-            });
+            this::delete_actionPerformed);
     createpref = JvSwingUtils.makeButton(
             MessageManager.getString("action.create"),
             MessageManager.getString("label.create_user_parameter_set"),
-            new ActionListener()
-            {
-
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                create_actionPerformed(e);
-              }
-            });
+            this::create_actionPerformed);
     revertpref = JvSwingUtils.makeButton(
             MessageManager.getString("action.revert"),
             MessageManager
                     .getString("label.revert_changes_user_parameter_set"),
-            new ActionListener()
-            {
+            this::revert_actionPerformed);
 
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                revert_actionPerformed(e);
-              }
-            });
-    startjob = JvSwingUtils.makeButton(
-            MessageManager.getString("action.start_job"),
-            MessageManager.getString("label.start_job_current_settings"),
-            new ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                startjob_actionPerformed(e);
-              }
-            });
-    canceljob = JvSwingUtils.makeButton(
-            MessageManager.getString("action.cancel_job"),
-            MessageManager.getString("label.cancel_job_close_dialog"),
-            new ActionListener()
-            {
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                canceljob_actionPerformed(e);
-              }
-            });
 
+    JPanel setDetails = new JPanel();
     setDetails.setBorder(
             new TitledBorder(MessageManager.getString("label.details")));
     setDetails.setLayout(new BorderLayout());
@@ -357,7 +303,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
     setName.getEditor().addActionListener(this);
     JPanel setNameInfo = new JPanel(new FlowLayout(FlowLayout.LEFT));
     GridBagLayout gbl = new GridBagLayout();
-    SetNamePanel.setLayout(gbl);
+    setNamePanel.setLayout(gbl);
 
     JLabel setNameLabel = new JLabel(
             MessageManager.getString("label.current_parameter_set_name"));
@@ -372,9 +318,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
     revertpref.setVisible(false);
     createpref.setVisible(false);
     JPanel setsavebuts = new JPanel();
-    setsavebuts.setLayout(new FlowLayout(FlowLayout.LEFT)); // GridLayout(1,2));
-    ((FlowLayout) setsavebuts.getLayout()).setHgap(10);
-    ((FlowLayout) setsavebuts.getLayout()).setVgap(0);
+    setsavebuts.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); // GridLayout(1,2));
     JPanel spacer = new JPanel();
     spacer.setPreferredSize(new Dimension(2, 30));
     setsavebuts.add(spacer);
@@ -385,11 +329,11 @@ public class WsJobParameters extends JPanel implements ItemListener,
     // setsavebuts.setSize(new Dimension(150, 30));
     JPanel buttonArea = new JPanel(new GridLayout(1, 1));
     buttonArea.add(setsavebuts);
-    SetNamePanel.add(setNameInfo);
+    setNamePanel.add(setNameInfo);
     GridBagConstraints gbc = new GridBagConstraints();
     gbc.gridheight = 2;
     gbl.setConstraints(setNameInfo, gbc);
-    SetNamePanel.add(buttonArea);
+    setNamePanel.add(buttonArea);
     gbc = new GridBagConstraints();
     gbc.gridx = 0;
     gbc.gridy = 2;
@@ -399,36 +343,32 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
     // paramPane.setPreferredSize(new Dimension(360, 400));
     // paramPane.setPreferredSize(null);
-    jobOptions.setBorder(
+    optionsPanel.setBorder(
             new TitledBorder(MessageManager.getString("label.options")));
-    jobOptions.setOpaque(true);
-    paramList.setBorder(
+    optionsPanel.setOpaque(true);
+    paramsPanel.setBorder(
             new TitledBorder(MessageManager.getString("label.parameters")));
-    paramList.setOpaque(true);
-    JPanel bjo = new JPanel(new BorderLayout()),
-            bjp = new JPanel(new BorderLayout());
-    bjo.add(jobOptions, BorderLayout.CENTER);
-    bjp.add(paramList, BorderLayout.CENTER);
-    bjp.setOpaque(true);
-    bjo.setOpaque(true);
+    paramsPanel.setOpaque(true);
     // optsAndparams.setScrollableWidth(ScrollableSizeHint.FIT);
     // optsAndparams.setScrollableHeight(ScrollableSizeHint.NONE);
     // optsAndparams.setLayout(new BorderLayout());
+    JPanel optsAndparams = new JPanel();
     optsAndparams.setLayout(new BorderLayout());
-    optsAndparams.add(jobOptions, BorderLayout.NORTH);
-    optsAndparams.add(paramList, BorderLayout.CENTER);
+    optsAndparams.add(optionsPanel, BorderLayout.NORTH);
+    optsAndparams.add(paramsPanel, BorderLayout.CENTER);
     JPanel jp = new JPanel(new BorderLayout());
     jp.add(optsAndparams, BorderLayout.CENTER);
     paramPane.getViewport().setView(jp);
     paramPane.setBorder(null);
     setLayout(new BorderLayout());
+    JPanel jobPanel = new JPanel();
     jobPanel.setPreferredSize(null);
     jobPanel.setLayout(new BorderLayout());
     jobPanel.add(setDetails, BorderLayout.NORTH);
     jobPanel.add(paramPane, BorderLayout.CENTER);
     // jobPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
 
-    add(SetNamePanel, BorderLayout.NORTH);
+    add(setNamePanel, BorderLayout.NORTH);
     add(jobPanel, BorderLayout.CENTER);
 
     JPanel dialogpanel = new JPanel();
@@ -436,8 +376,8 @@ public class WsJobParameters extends JPanel implements ItemListener,
     dialogpanel.add(canceljob);
     // JAL-1580: setMaximumSize() doesn't work, so just size for the worst case:
     // check for null is for JUnit usage
-    final int windowHeight = Desktop.instance == null ? 540
-            : Desktop.instance.getHeight();
+    final int windowHeight = Desktop.getInstance() == null ? DEFAULT_HEIGHT
+            : Desktop.getInstance().getHeight();
     setPreferredSize(new Dimension(540, windowHeight));
     add(dialogpanel, BorderLayout.SOUTH);
     validate();
@@ -499,53 +439,40 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
   protected void canceljob_actionPerformed(ActionEvent e)
   {
-    response = 0;
+    startJob = false;
     if (frame != null)
     {
       frame.setVisible(false);
     }
+    completionStage.complete(false);
   }
 
   protected void startjob_actionPerformed(ActionEvent e)
   {
-    response = 1;
+    startJob = true;
     if (frame != null)
     {
       frame.setVisible(false);
     }
+    completionStage.complete(true);
   }
 
-  Jws2Instance service;
+  void initForService(WsParamSetI paramSet, List<ArgumentI> jobArgset)
+  {
+    settingDialog = true;
 
-  /**
-   * list of service presets in the gui
-   */
-  Hashtable servicePresets = null;
+    init(paramSet, jobArgset);
 
-  /**
-   * set if dialog is being set - so handlers will avoid spurious events
-   */
-  boolean settingDialog = false;
+  }
 
-  void initForService(Jws2Instance service, WsParamSetI jabap,
-          List<Argument> jabajobArgset)
+  void init(WsParamSetI p, List<ArgumentI> jobArgset)
   {
-    WsParamSetI p = null;
-    List<ArgumentI> jobArgset = null;
-    settingDialog = true;
-    { // instantiate the abstract proxy for Jaba objects
-      jobArgset = jabajobArgset == null ? null
-              : JabaParamStore.getJwsArgsfromJaba(jabajobArgset);
-      p = jabap; // (jabap != null) ? paramStore.getPreset(jabap.getName()) :
-                 // null;
-    }
-
-    Hashtable exnames = new Hashtable();
+    Hashtable<String, String> exnames = new Hashtable<>();
     for (int i = 0, iSize = setName.getItemCount(); i < iSize; i++)
     {
       exnames.put(setName.getItemAt(i), setName.getItemAt(i));
     }
-    servicePresets = new Hashtable();
+    servicePresets = new Hashtable<>();
     // Add the default entry - if not present already.
     if (!exnames.contains(SVC_DEF))
     {
@@ -553,7 +480,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
       exnames.put(SVC_DEF, SVC_DEF);
       servicePresets.put(SVC_DEF, SVC_DEF);
     }
-    String curname = (p == null ? "" : p.getName());
+    // String curname = (p == null ? "" : p.getName());
     for (WsParamSetI pr : paramStore.getPresets())
     {
       if (!pr.isModifiable())
@@ -596,7 +523,6 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
   }
 
-  @SuppressWarnings("unchecked")
   private void updateTable(WsParamSetI p, List<ArgumentI> jobArgset)
   {
     boolean setDefaultParams = false;
@@ -633,9 +559,9 @@ public class WsJobParameters extends JPanel implements ItemListener,
             OptionI opt = (OptionI) myarg;
             OptionBox ob = opanp.addOption(opt);
             ob.resetToDefault(setDefaultParams);
-            if (MAX_OPTWIDTH < ob.getPreferredSize().width)
+            if (maxOptWidth < ob.getPreferredSize().width)
             {
-              MAX_OPTWIDTH = ob.getPreferredSize().width;
+              maxOptWidth = ob.getPreferredSize().width;
             }
             ob.validate();
             cw += ob.getPreferredSize().width + 5;
@@ -704,7 +630,6 @@ public class WsJobParameters extends JPanel implements ItemListener,
     return modifiedElements.size() > 0;
   }
 
-  private Hashtable modifiedElements = new Hashtable();
 
   /**
    * reset gui and modification state settings
@@ -770,7 +695,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
     if (b && modifiedElements.size() > 0)
     {
       makeSetNameValid(!isUserPreset);
-      SetNamePanel.revalidate();
+      setNamePanel.revalidate();
     }
     updateButtonDisplay();
   }
@@ -817,7 +742,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
     // sync the gui with the preset database
     for (int i = 0, iS = setName.getItemCount(); i < iS; i++)
     {
-      String snm = (String) setName.getItemAt(i);
+      String snm = setName.getItemAt(i);
       if (snm.equals(nm))
       {
         makeupdate = true;
@@ -837,96 +762,88 @@ public class WsJobParameters extends JPanel implements ItemListener,
     settingDialog = stn;
   }
 
+  /**
+   * Rebuilds the Options and Parameters panels
+   */
   @Override
   public void refreshParamLayout()
   {
-    // optsAndparams.setPreferredSize(null);
-    FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
-    int sep = fl.getVgap();
-    boolean fh = true;
-    int os = 0,
-            s = jobOptions.getBorder().getBorderInsets(jobOptions).bottom
-                    + jobOptions.getBorder().getBorderInsets(jobOptions).top
-                    + 2 * sep;
-    /**
-     * final height for viewport
-     */
-    int finalh = s;
-    int panewidth = paramPane.getViewport().getSize().width - 120
-            - jobOptions.getBorder().getBorderInsets(jobOptions).left
-            + jobOptions.getBorder().getBorderInsets(jobOptions).right;
-
-    int w = 2 * fl.getHgap()
-            + (MAX_OPTWIDTH > OptsAndParamsPage.PARAM_WIDTH ? MAX_OPTWIDTH
-                    : OptsAndParamsPage.PARAM_WIDTH);
-    int hgap = fl.getHgap(), cw = hgap;
+    final int rightMargin = 40;
+    final int availableWidth = paramPane.getViewport().getSize().width
+            - rightMargin
+            - optionsPanel.getBorder().getBorderInsets(optionsPanel).left
+            + optionsPanel.getBorder().getBorderInsets(optionsPanel).right;
 
     if (opanp.getOptSet().size() > 0)
     {
+      int hgap = 5;
+      int currentWidth = hgap;
 
-      jobOptions.setLayout(new MigLayout("", "", ""));
-      jobOptions.removeAll();
+      /*
+       * layout constraint 'nogrid' prevents vertical column alignment,
+       * allowing controls to flow without extra space inserted to align
+       */
+      optionsPanel.setLayout(new MigLayout("nogrid", "", ""));
+      optionsPanel.removeAll();
+      JPanel lastAdded = null;
 
+      /*
+       * add each control in turn; if adding would overflow the right margin,
+       * remove and re-add the previous parameter with "wrap" (after) 
+       * in order to start a new row
+       */
       for (OptionBox pbox : opanp.getOptSet().values())
       {
         pbox.validate();
-        cw += pbox.getSize().width + hgap;
-        if (cw + 120 > panewidth)
+        int boxWidth = pbox.getSize().width;
+        currentWidth += boxWidth + hgap;
+        boolean wrapAfterLast = currentWidth > availableWidth
+                && lastAdded != null;
+        // System.out.println(String.format(
+        // "%s width=%d, paneWidth=%d, currentWidth=%d, wrapAfterLast=%s",
+        // pbox.toString(), boxWidth, panewidth, currentWidth,
+        // wrapAfterLast));
+        if (wrapAfterLast)
         {
-          jobOptions.add(pbox, "wrap");
-          // System.out.println("Wrap on "+pbox.option.getName());
-          cw = hgap + pbox.getSize().width;
-          fh = true;
-        }
-        else
-        {
-          jobOptions.add(pbox);
-        }
-        if (fh)
-        {
-          finalh += pbox.getSize().height + fl.getVgap();
-          fh = false;
+          optionsPanel.remove(lastAdded);
+          optionsPanel.add(lastAdded, "wrap");
+          currentWidth = hgap + boxWidth;
         }
+        optionsPanel.add(pbox);
+        lastAdded = pbox;
       }
-      jobOptions.revalidate();
+      optionsPanel.revalidate();
     }
     else
     {
-      jobOptions.setVisible(false);
+      optionsPanel.setVisible(false);
     }
 
-    // Now layout the parameters assuming they occupy one column - to calculate
-    // total height of options+parameters
-    fl = new FlowLayout(FlowLayout.LEFT);
-    // helpful hint from
-    // http://stackoverflow.com/questions/2743177/top-alignment-for-flowlayout
-    fl.setAlignOnBaseline(true);
     if (opanp.getParamSet().size() > 0)
     {
-      paramList.removeAll();
-      paramList.setLayout(new MigLayout("", "", ""));
-      fh = true;
+      paramsPanel.removeAll();
+      paramsPanel.setLayout(new MigLayout("", "", ""));
+      int hgap = 5;
+      int currentWidth = hgap;
+
+      JPanel lastAdded = null;
       for (ParamBox pbox : opanp.getParamSet().values())
       {
         pbox.validate();
-        cw += pbox.getSize().width + hgap;
-        if (cw + 160 > panewidth)
-        {
-          paramList.add(pbox, "wrap");
-          cw = pbox.getSize().width + hgap;
-          fh = true;
-        }
-        else
+        int boxWidth = pbox.getSize().width;
+        currentWidth += boxWidth + hgap;
+        boolean wrapAfterLast = currentWidth > availableWidth
+                && lastAdded != null;
+        if (wrapAfterLast)
         {
-          paramList.add(pbox);
+          paramsPanel.remove(lastAdded);
+          paramsPanel.add(lastAdded, "wrap");
+          currentWidth = pbox.getSize().width + hgap;
         }
-        if (fh)
-        {
-          finalh += pbox.getSize().height + fl.getVgap();
-          fh = false;
-        }
-
+        paramsPanel.add(pbox);
+        lastAdded = pbox;
       }
+
       /*
        * s = 2 * sep; for (ParamBox pbox : opanp.getParamSet().values()) {
        * pbox.validate(); s += sep +
@@ -938,11 +855,11 @@ public class WsJobParameters extends JPanel implements ItemListener,
        * .getBorder().getBorderInsets(paramList).bottom+paramList
        * .getBorder().getBorderInsets(paramList).top;
        */
-      paramList.revalidate();
+      paramsPanel.revalidate();
     }
     else
     {
-      paramList.setVisible(false);
+      paramsPanel.setVisible(false);
     }
     // TODO: waste some time trying to eliminate any unnecessary .validate calls
     // here
@@ -954,242 +871,6 @@ public class WsJobParameters extends JPanel implements ItemListener,
     revalidate();
   }
 
-  /**
-   * testing method - grab a service and parameter set and show the window
-   * 
-   * @param args
-   * @j2sIgnore
-   */
-  public static void main(String[] args)
-  {
-    jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
-            .getDiscoverer();
-    int p = 0;
-    if (args.length > 0)
-    {
-      Vector<String> services = new Vector<>();
-      services.addElement(args[p++]);
-      Jws2Discoverer.getDiscoverer().setServiceUrls(services);
-    }
-    try
-    {
-      disc.run();
-    } catch (Exception e)
-    {
-      System.err.println("Aborting. Problem discovering services.");
-      e.printStackTrace();
-      return;
-    }
-    Jws2Instance lastserv = null;
-    for (Jws2Instance service : disc.getServices())
-    {
-      lastserv = service;
-      if (p >= args.length || service.serviceType.equalsIgnoreCase(args[p]))
-      {
-        if (lastserv != null)
-        {
-          List<Preset> prl = null;
-          Preset pr = null;
-          if (++p < args.length)
-          {
-            PresetManager prman = lastserv.getPresets();
-            if (prman != null)
-            {
-              pr = prman.getPresetByName(args[p]);
-              if (pr == null)
-              {
-                // just grab the last preset.
-                prl = prman.getPresets();
-              }
-            }
-          }
-          else
-          {
-            PresetManager prman = lastserv.getPresets();
-            if (prman != null)
-            {
-              prl = prman.getPresets();
-            }
-          }
-          Iterator<Preset> en = (prl == null) ? null : prl.iterator();
-          while (en != null && en.hasNext())
-          {
-            if (en != null)
-            {
-              if (!en.hasNext())
-              {
-                en = prl.iterator();
-              }
-              pr = en.next();
-            }
-            {
-              System.out.println("Testing opts dupes for "
-                      + lastserv.getUri() + " : " + lastserv.getActionText()
-                      + ":" + pr.getName());
-              List<Option> rg = lastserv.getRunnerConfig().getOptions();
-              for (Option o : rg)
-              {
-                try
-                {
-                  Option cpy = jalview.ws.jws2.ParameterUtils.copyOption(o);
-                } catch (Exception e)
-                {
-                  System.err.println("Failed to copy " + o.getName());
-                  e.printStackTrace();
-                } catch (Error e)
-                {
-                  System.err.println("Failed to copy " + o.getName());
-                  e.printStackTrace();
-                }
-              }
-            }
-            {
-              System.out.println("Testing param dupes:");
-              List<Parameter> rg = lastserv.getRunnerConfig()
-                      .getParameters();
-              for (Parameter o : rg)
-              {
-                try
-                {
-                  Parameter cpy = jalview.ws.jws2.ParameterUtils
-                          .copyParameter(o);
-                } catch (Exception e)
-                {
-                  System.err.println("Failed to copy " + o.getName());
-                  e.printStackTrace();
-                } catch (Error e)
-                {
-                  System.err.println("Failed to copy " + o.getName());
-                  e.printStackTrace();
-                }
-              }
-            }
-            {
-              System.out.println("Testing param write:");
-              List<String> writeparam = null, readparam = null;
-              try
-              {
-                writeparam = jalview.ws.jws2.ParameterUtils
-                        .writeParameterSet(
-                                pr.getArguments(lastserv.getRunnerConfig()),
-                                " ");
-                System.out.println("Testing param read :");
-                List<Option> pset = jalview.ws.jws2.ParameterUtils
-                        .processParameters(writeparam,
-                                lastserv.getRunnerConfig(), " ");
-                readparam = jalview.ws.jws2.ParameterUtils
-                        .writeParameterSet(pset, " ");
-                Iterator<String> o = pr.getOptions().iterator(),
-                        s = writeparam.iterator(), t = readparam.iterator();
-                boolean failed = false;
-                while (s.hasNext() && t.hasNext())
-                {
-                  String on = o.next(), sn = s.next(), st = t.next();
-                  if (!sn.equals(st))
-                  {
-                    System.out.println(
-                            "Original was " + on + " Phase 1 wrote " + sn
-                                    + "\tPhase 2 wrote " + st);
-                    failed = true;
-                  }
-                }
-                if (failed)
-                {
-                  System.out.println(
-                          "Original parameters:\n" + pr.getOptions());
-                  System.out.println(
-                          "Wrote parameters in first set:\n" + writeparam);
-                  System.out.println(
-                          "Wrote parameters in second set:\n" + readparam);
-
-                }
-              } catch (Exception e)
-              {
-                e.printStackTrace();
-              }
-            }
-            WsJobParameters pgui = new WsJobParameters(lastserv,
-                    new JabaPreset(lastserv, pr));
-            JFrame jf = new JFrame(MessageManager
-                    .formatMessage("label.ws_parameters_for", new String[]
-                    { lastserv.getActionText() }));
-            JPanel cont = new JPanel(new BorderLayout());
-            pgui.validate();
-            cont.setPreferredSize(pgui.getPreferredSize());
-            cont.add(pgui, BorderLayout.CENTER);
-            jf.setLayout(new BorderLayout());
-            jf.add(cont, BorderLayout.CENTER);
-            jf.validate();
-            final Thread thr = Thread.currentThread();
-            jf.addWindowListener(new WindowListener()
-            {
-
-              @Override
-              public void windowActivated(WindowEvent e)
-              {
-                // TODO Auto-generated method stub
-
-              }
-
-              @Override
-              public void windowClosed(WindowEvent e)
-              {
-              }
-
-              @Override
-              public void windowClosing(WindowEvent e)
-              {
-                thr.interrupt();
-
-              }
-
-              @Override
-              public void windowDeactivated(WindowEvent e)
-              {
-                // TODO Auto-generated method stub
-
-              }
-
-              @Override
-              public void windowDeiconified(WindowEvent e)
-              {
-                // TODO Auto-generated method stub
-
-              }
-
-              @Override
-              public void windowIconified(WindowEvent e)
-              {
-                // TODO Auto-generated method stub
-
-              }
-
-              @Override
-              public void windowOpened(WindowEvent e)
-              {
-                // TODO Auto-generated method stub
-
-              }
-
-            });
-            jf.setVisible(true);
-            boolean inter = false;
-            while (!inter)
-            {
-              try
-              {
-                Thread.sleep(10000);
-              } catch (Exception e)
-              {
-                inter = true;
-              }
-            }
-            jf.dispose();
-          }
-        }
-      }
-    }
-  }
 
   public boolean isServiceDefaults()
   {
@@ -1202,7 +883,6 @@ public class WsJobParameters extends JPanel implements ItemListener,
     return opanp.getCurrentSettings();
   }
 
-  String lastParmSet = null;
 
   /*
    * Hashtable<String, Object[]> editedParams = new Hashtable<String,
@@ -1243,10 +923,10 @@ public class WsJobParameters extends JPanel implements ItemListener,
     int n = 0;
     // remove any set names in the drop down menu that aren't either a reserved
     // setting, or a user defined or service preset.
-    Vector items = new Vector();
+    Vector<String> items = new Vector<>();
     while (n < setName.getItemCount())
     {
-      String item = (String) setName.getItemAt(n);
+      String item = setName.getItemAt(n);
       if (!item.equals(SVC_DEF) && !paramStore.presetExists(item))
       {
         setName.removeItemAt(n);
@@ -1314,7 +994,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
     initArgSetModified();
     syncSetNamesWithStore();
     setName.setSelectedItem(lastParmSet);
-    SetNamePanel.validate();
+    setNamePanel.validate();
     validate();
     settingDialog = false;
   }
@@ -1325,9 +1005,13 @@ public class WsJobParameters extends JPanel implements ItemListener,
    */
   protected void updateWebServiceMenus()
   {
+    if (Desktop.getInstance() == null)
+    {
+      return;
+    }
     for (AlignFrame alignFrame : Desktop.getAlignFrames())
     {
-      alignFrame.BuildWebServiceMenu();
+      alignFrame.buildWebServicesMenu();
     }
   }
 
@@ -1336,7 +1020,8 @@ public class WsJobParameters extends JPanel implements ItemListener,
   @Override
   public void itemStateChanged(ItemEvent e)
   {
-    if (e.getSource() == setName && e.getStateChange() == e.SELECTED)
+    if (e.getSource() == setName
+            && e.getStateChange() == ItemEvent.SELECTED)
     {
       final String setname = (String) setName.getSelectedItem();
       if (Console.isDebugEnabled())
@@ -1399,12 +1084,6 @@ public class WsJobParameters extends JPanel implements ItemListener,
 
   }
 
-  private void _renameExistingPreset(String oldName, String curSetName2)
-  {
-    paramStore.updatePreset(oldName, curSetName2, setDescr.getText(),
-            getJobParams());
-  }
-
   /**
    * store current settings as given name. You should then reset gui.
    * 
index 3c9f65c..6e37cf7 100644 (file)
@@ -57,13 +57,14 @@ import javax.xml.stream.XMLStreamReader;
  */
 public class WsParamSetManager implements ParamManager
 {
+  private static final String WS_PARAM_FILES = "WS_PARAM_FILES";
   Hashtable<String, ParamDatastoreI> paramparsers = new Hashtable<>();
 
   @Override
   public WsParamSetI[] getParameterSet(String name, String serviceUrl,
           boolean modifiable, boolean unmodifiable)
   {
-    String files = Cache.getProperty("WS_PARAM_FILES");
+    String files = Cache.getProperty(WS_PARAM_FILES);
     if (files == null)
     {
       return null;
@@ -148,8 +149,8 @@ public class WsParamSetManager implements ParamManager
         if (parser != null)
         {
           WsParamSetI pset = parser.parseServiceParameterFile(
-                  wspset.getName(), wspset.getDescription(), urlArray,
-                  wspset.getParameters());
+                  wspset.getName(), wspset.getDescription(),
+                  urlArray, wspset.getParameters());
           if (pset != null)
           {
             pset.setSourceFile(filename);
@@ -204,7 +205,7 @@ public class WsParamSetManager implements ParamManager
       chooser.setDialogTitle(MessageManager
               .getString("label.choose_filename_for_param_file"));
       chooser.setToolTipText(MessageManager.getString("action.save"));
-      int value = chooser.showSaveDialog(Desktop.instance);
+      int value = chooser.showSaveDialog(Desktop.getInstance());
       if (value == JalviewFileChooser.APPROVE_OPTION)
       {
         outfile = chooser.getSelectedFile();
@@ -228,7 +229,7 @@ public class WsParamSetManager implements ParamManager
         }
         paramFiles = paramFiles.concat(filename);
       }
-      Cache.setProperty("WS_PARAM_FILES", paramFiles);
+      Cache.setProperty(WS_PARAM_FILES, paramFiles);
 
       WebServiceParameterSet paramxml = new WebServiceParameterSet();
 
@@ -262,7 +263,7 @@ public class WsParamSetManager implements ParamManager
 
   /*
    * 
-   * JalviewFileChooser chooser = new JalviewFileChooser(Cache
+   * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
    * .getProperty("LAST_DIRECTORY"), new String[] { "jc" }, new String[] {
    * "Jalview User Colours" }, "Jalview User Colours"); chooser.setFileView(new
    * jalview.io.JalviewFileView());
@@ -272,8 +273,8 @@ public class WsParamSetManager implements ParamManager
    * int value = chooser.showOpenDialog(this);
    * 
    * if (value == JalviewFileChooser.APPROVE_OPTION) { File choice =
-   * chooser.getSelectedFile(); Cache.setProperty("LAST_DIRECTORY",
-   * choice.getParent()); String defaultColours = Cache.getDefault(
+   * chooser.getSelectedFile(); jalview.bin.Cache.setProperty("LAST_DIRECTORY",
+   * choice.getParent()); String defaultColours = jalview.bin.Cache.getDefault(
    * "USER_DEFINED_COLOURS", choice.getPath()); if
    * (defaultColours.indexOf(choice.getPath()) == -1) { defaultColours =
    * defaultColours.concat("|") .concat(choice.getPath()); } (non-Javadoc)
@@ -290,7 +291,7 @@ public class WsParamSetManager implements ParamManager
     {
       return;
     }
-    String paramFiles = Cache.getDefault("WS_PARAM_FILES", "");
+    String paramFiles = Cache.getDefault(WS_PARAM_FILES, "");
     if (paramFiles.indexOf(filename) > -1)
     {
       String nparamFiles = new String();
@@ -303,7 +304,7 @@ public class WsParamSetManager implements ParamManager
           nparamFiles = nparamFiles.concat("|").concat(fl);
         }
       }
-      Cache.setProperty("WS_PARAM_FILES", nparamFiles);
+      Cache.setProperty(WS_PARAM_FILES, nparamFiles);
     }
 
     try
@@ -311,7 +312,7 @@ public class WsParamSetManager implements ParamManager
       File pfile = new File(filename);
       if (pfile.exists() && pfile.canWrite())
       {
-        if (JvOptionPane.showConfirmDialog(Desktop.instance,
+        if (JvOptionPane.showConfirmDialog(Desktop.getInstance(),
                 "Delete the preset's file, too ?", "Delete User Preset ?",
                 JvOptionPane.OK_CANCEL_OPTION) == JvOptionPane.OK_OPTION)
         {
index e37f77c..bdec5bd 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -27,6 +28,7 @@ import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
 
@@ -36,10 +38,10 @@ import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableCellRenderer;
-
 import jalview.bin.Cache;
 import jalview.jbgui.GWsPreferences;
 import jalview.util.MessageManager;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.rest.RestServiceDescription;
 
@@ -64,7 +66,7 @@ public class WsPreferences extends GWsPreferences
   private void initFromPreferences()
   {
 
-    wsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls();
+    wsUrls = Jws2Discoverer.getInstance().getServiceUrls();
     if (!wsUrls.isEmpty())
     {
       oldUrls = new Vector<String>(wsUrls);
@@ -121,7 +123,7 @@ public class WsPreferences extends GWsPreferences
     int r = 0;
     for (String url : wsUrls)
     {
-      int status = Jws2Discoverer.getDiscoverer().getServerStatusFor(url);
+      int status = Jws2Discoverer.getInstance().getServerStatusFor(url);
       tdat[r][1] = Integer.valueOf(status);
       tdat[r++][0] = url;
     }
@@ -155,21 +157,22 @@ public class WsPreferences extends GWsPreferences
       String t = new String("");
       switch (((Integer) status).intValue())
       {
-      case 1:
+      case WSDiscovererI.STATUS_OK:
         // cb.setSelected(true);
         // cb.setBackground(
         c = Color.green;
         break;
-      case 0:
+      case WSDiscovererI.STATUS_NO_SERVICES:
         // cb.setSelected(true);
         // cb.setBackground(
         c = Color.lightGray;
         break;
-      case -1:
+      case WSDiscovererI.STATUS_INVALID:
         // cb.setSelected(false);
         // cb.setBackground(
         c = Color.red;
         break;
+      case WSDiscovererI.STATUS_UNKNOWN:
       default:
         // cb.setSelected(false);
         // cb.setBackground(
@@ -240,7 +243,7 @@ public class WsPreferences extends GWsPreferences
 
   private void updateServiceList()
   {
-    Jws2Discoverer.getDiscoverer().setServiceUrls(wsUrls);
+    Jws2Discoverer.getInstance().setServiceUrls(wsUrls);
   }
 
   private void updateRsbsServiceList()
@@ -453,7 +456,7 @@ public class WsPreferences extends GWsPreferences
     boolean valid = false;
     int resp = JvOptionPane.CANCEL_OPTION;
     while (!valid && (resp = JvOptionPane.showInternalConfirmDialog(
-            Desktop.desktop, panel, title,
+            Desktop.getDesktopPane(), panel, title,
             JvOptionPane.OK_CANCEL_OPTION)) == JvOptionPane.OK_OPTION)
     {
       try
@@ -471,26 +474,26 @@ public class WsPreferences extends GWsPreferences
       } catch (Exception e)
       {
         valid = false;
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.invalid_url"));
       }
     }
     if (valid && resp == JvOptionPane.OK_OPTION)
     {
-      int validate = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+      int validate = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
               MessageManager.getString("info.validate_jabaws_server"),
               MessageManager.getString("label.test_server"),
               JvOptionPane.YES_NO_OPTION);
 
       if (validate == JvOptionPane.OK_OPTION)
       {
-        if (Jws2Discoverer.testServiceUrl(foo))
+        if (Jws2Discoverer.getInstance().testServiceUrl(foo))
         {
           return foo.toString();
         }
         else
         {
-          int opt = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
+          int opt = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
                   "The Server  '" + foo.toString()
                           + "' failed validation,\ndo you want to add it anyway? ",
                   "Server Validation Failed", JvOptionPane.YES_NO_OPTION,
@@ -501,7 +504,7 @@ public class WsPreferences extends GWsPreferences
           }
           else
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.getString(
                             "warn.server_didnt_pass_validation"));
           }
@@ -594,7 +597,7 @@ public class WsPreferences extends GWsPreferences
           if (lastrefresh != update)
           {
             lastrefresh = update;
-            Desktop.instance.startServiceDiscovery(true); // wait around for all
+            Desktop.getInstance().startServiceDiscovery(true); // wait around for all
                                                           // threads to complete
             updateList();
 
@@ -615,15 +618,15 @@ public class WsPreferences extends GWsPreferences
         public void run()
         {
           long ct = System.currentTimeMillis();
-          Desktop.instance.setProgressBar(MessageManager
+          Desktop.getInstance().setProgressBar(MessageManager
                   .getString("status.refreshing_web_service_menus"), ct);
           if (lastrefresh != update)
           {
             lastrefresh = update;
-            Desktop.instance.startServiceDiscovery(true);
+            Desktop.getInstance().startServiceDiscovery(true);
             updateList();
           }
-          Desktop.instance.setProgressBar(null, ct);
+          Desktop.getInstance().setProgressBar(null, ct);
         }
 
       }).start();
@@ -633,9 +636,7 @@ public class WsPreferences extends GWsPreferences
   /**
    * state counters for ensuring that updates only happen if config has changed.
    */
-  protected long update = 0;
-
-  private long lastrefresh = 0;
+  private long update = 0, lastrefresh = 0;
 
   /*
    * (non-Javadoc)
@@ -647,8 +648,8 @@ public class WsPreferences extends GWsPreferences
   @Override
   protected void resetWs_actionPerformed(ActionEvent e)
   {
-    Jws2Discoverer.getDiscoverer().setServiceUrls(null);
-    List<String> nwsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls();
+    Jws2Discoverer.getInstance().setServiceUrls(null);
+    List<String> nwsUrls = Jws2Discoverer.getInstance().getServiceUrls();
     if (!wsUrls.equals(nwsUrls))
     {
       update++;
diff --git a/src/jalview/hmmer/HMMAlign.java b/src/jalview/hmmer/HMMAlign.java
new file mode 100644 (file)
index 0000000..dd85c74
--- /dev/null
@@ -0,0 +1,340 @@
+package jalview.hmmer;
+
+import jalview.analysis.AlignmentSorter;
+import jalview.analysis.SeqsetUtils.SequenceInfo;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.gui.SplitFrame;
+import jalview.io.DataSourceType;
+import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+import jalview.ws.params.ArgumentI;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JInternalFrame;
+
+public class HMMAlign extends HmmerCommand
+{
+  static final String HMMALIGN = "hmmalign";
+
+  private final AlignmentI dataset;
+
+  /**
+   * Constructor for the HMMAlignThread
+   * 
+   * @param af
+   * @param args
+   */
+  public HMMAlign(AlignFrame af, List<ArgumentI> args)
+  {
+    super(af, args);
+    if (alignment.getDataset() != null)
+    {
+      dataset = alignment.getDataset();
+    }
+    else
+    {
+      dataset = null;
+    }
+  }
+
+  /**
+   * Runs the HMMAlignThread: the data on the alignment or group is exported,
+   * then the command is executed in the command line and then the data is
+   * imported and displayed in a new frame (if true). The command is executed
+   * for each segment of the alignment. Call this method directly to execute
+   * synchronously, or via start() in a new Thread for asynchronously.
+   */
+  @Override
+  public void run()
+  {
+    HiddenMarkovModel hmm = getHmmProfile();
+
+    long msgId = System.currentTimeMillis();
+    af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
+            msgId);
+
+    // ensure alignments are the same length
+    alignment.padGaps();
+
+    AlignmentView msa = af.gatherSequencesForAlignment();
+    SequenceI[][] subAlignments = msa.getVisibleContigs(alignment.getGapCharacter());
+
+    List<AlignmentOrder> allOrders = new ArrayList<>();
+
+    SequenceI[][] allResults = new SequenceI[subAlignments.length][];
+    int job = 0;
+    for (SequenceI[] seqs : subAlignments)
+    {
+      Map<String, SequenceInfo> sequencesHash = stashSequences(seqs);
+      try
+      {
+        File modelFile = FileUtils.createTempFile("hmm", ".hmm");
+        File alignmentFile = FileUtils.createTempFile("output", ".sto");
+        File resultFile = FileUtils.createTempFile("input", ".sto");
+
+        exportStockholm(seqs, alignmentFile.getAbsoluteFile(), null);
+        exportHmm(hmm, modelFile.getAbsoluteFile());
+
+        boolean ran = runCommand(modelFile, alignmentFile, resultFile);
+        if (!ran)
+        {
+          JvOptionPane.showInternalMessageDialog(af, MessageManager
+                  .formatMessage("warn.command_failed", "hmmalign"));
+          return;
+        }
+
+        SequenceI[] result = importData(resultFile, allOrders);
+        recoverSequences(sequencesHash, result);
+        allResults[job] = result;
+        modelFile.delete();
+        alignmentFile.delete();
+        resultFile.delete();
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+      }
+      job++;
+    }
+
+    String title = "hmmalign to " + hmm.getConsensusSequence().getName();
+    displayResults(allResults, allOrders, msa, title);
+
+    af.setProgressBar("", msgId);
+  }
+
+  /**
+   * Executes the hmmalign command and returns true if successful, false if an
+   * error is detected
+   * 
+   * @param modelFile
+   *          the HMM to align to
+   * @param alignmentFile
+   *          the sequences to align
+   * @param resultFile
+   *          the file to hold the results of alignment
+   * @return
+   * @throws IOException
+   */
+  private boolean runCommand(File modelFile, File alignmentFile,
+          File resultFile) throws IOException
+  {
+    String command = getCommandPath(HMMALIGN);
+    if (command == null)
+    {
+      return false;
+    }
+    List<String> args = new ArrayList<>();
+    args.add(command);
+
+    if (params != null)
+    {
+      for (ArgumentI arg : params)
+      {
+        String name = arg.getName();
+        if (MessageManager.getString("label.trim_termini").equals(name))
+        {
+          args.add(ARG_TRIM);
+        }
+      }
+    }
+    args.add("-o");
+    args.add(getFilePath(resultFile, true));
+    args.add(getFilePath(modelFile, true));
+    args.add(getFilePath(alignmentFile, true));
+    
+    return runCommand(args);
+  }
+
+  /**
+   * Imports the data from the file holding the output of hmmalign
+   * 
+   * @param resultFile
+   * @param allOrders
+   *          a list of alignment orders to add to
+   * 
+   * @return
+   * @throws IOException
+   */
+  private SequenceI[] importData(File resultFile,
+          List<AlignmentOrder> allOrders) throws IOException
+  {
+    StockholmFile file = new StockholmFile(getFilePath(resultFile, false),
+            DataSourceType.FILE);
+    SequenceI[] result = file.getSeqsAsArray();
+    AlignmentOrder msaorder = new AlignmentOrder(result);
+    AlignmentSorter.recoverOrder(result);
+    allOrders.add(msaorder);
+
+    return result;
+  }
+
+  /**
+   * Displays the results of all 'jobs' in a new frame
+   * 
+   * @param allResults
+   * 
+   * @param allOrders
+   * @param msa
+   * @param title
+   */
+  private void displayResults(SequenceI[][] allResults,
+          List<AlignmentOrder> allOrders, AlignmentView msa, String title)
+  {
+    AlignmentOrder[] arrOrders = allOrders
+            .toArray(new AlignmentOrder[allOrders.size()]);
+    Object[] newview = msa.getUpdatedView(allResults, arrOrders,
+            alignment.getGapCharacter());
+    SequenceI[] seqs = (SequenceI[]) newview[0];
+    HiddenColumns hidden = (HiddenColumns) newview[1];
+    Alignment al = new Alignment(seqs);
+    al.setProperty("Alignment Program", "hmmalign");
+    if (dataset != null)
+    {
+      al.setDataset(dataset);
+    }
+
+    displayInNewFrame(al, allOrders, hidden, title);
+  }
+
+  /**
+   * Displays the results in a new frame
+   * 
+   * @param al
+   *          The alignment containing the results
+   * @param alorders
+   *          The order of the sequences in the alignment on which the jobs were
+   *          run
+   * @param hidden
+   *          Hidden columns in the previous alignment
+   * @param title
+   */
+  private void displayInNewFrame(AlignmentI al,
+          List<AlignmentOrder> alorders, HiddenColumns hidden, String title)
+  {
+    AlignFrame alignFrame = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    alignFrame.setTitle(title);
+
+    FeatureRendererSettings featureSettings = af.getFeatureRenderer()
+            .getSettings();
+    // initialise with same renderer settings as in parent alignframe.
+    alignFrame.getFeatureRenderer().transferSettings(featureSettings);
+
+    addSortByMenuItems(alignFrame, alorders);
+
+    // TODO: refactor retrieve and show as new splitFrame as Desktop method
+
+    /*
+     * If alignment was requested from one half of a SplitFrame, show in a
+     * SplitFrame with the other pane similarly aligned.
+     */
+    AlignFrame requestedBy = this.af;
+    if (requestedBy != null && requestedBy.getSplitViewContainer() != null
+            && requestedBy.getSplitViewContainer()
+                    .getComplement(requestedBy) != null)
+    {
+      AlignmentI complement = requestedBy.getSplitViewContainer()
+              .getComplement(requestedBy);
+      String complementTitle = requestedBy.getSplitViewContainer()
+              .getComplementTitle(requestedBy);
+      // becomes null if the alignment window was closed before the alignment
+      // job finished.
+      AlignmentI copyComplement = new Alignment(complement);
+      // todo should this be done by copy constructor?
+      copyComplement.setGapCharacter(complement.getGapCharacter());
+      // share the same dataset (and the mappings it holds)
+      copyComplement.setDataset(complement.getDataset());
+      copyComplement.alignAs(al);
+      if (copyComplement.getHeight() > 0)
+      {
+        alignFrame.setTitle(this.af.getTitle());
+        AlignFrame af2 = new AlignFrame(copyComplement,
+                AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+        af2.setTitle(complementTitle);
+        String linkedTitle = MessageManager
+                .getString("label.linked_view_title");
+        JInternalFrame splitFrame = new SplitFrame(
+                al.isNucleotide() ? alignFrame : af2, al.isNucleotide() ? af2 : alignFrame);
+        Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
+        return;
+      }
+    }
+
+    /*
+     * Not from SplitFrame, or failed to created a complementary alignment
+     */
+    Desktop.addInternalFrame(alignFrame, alignFrame.getTitle(), AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+  }
+
+  /**
+   * Adds sort order options to the AlignFrame menus
+   * 
+   * @param alignFrame
+   * @param alorders
+   */
+  protected void addSortByMenuItems(AlignFrame alignFrame,
+          List<AlignmentOrder> alorders)
+  {
+    // update orders
+    if (alorders.size() == 1)
+    {
+      alignFrame.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
+    }
+    else
+    {
+      // construct a non-redundant ordering set
+      List<String> names = new ArrayList<>();
+      for (int i = 0, l = alorders.size(); i < l; i++)
+      {
+        String orderName = " Region " + i;
+        int j = i + 1;
+
+        while (j < l)
+        {
+          if (alorders.get(i).equals(alorders.get(j)))
+          {
+            alorders.remove(j);
+            l--;
+            orderName += "," + j;
+          }
+          else
+          {
+            j++;
+          }
+        }
+
+        if (i == 0 && j == 1)
+        {
+          names.add("");
+        }
+        else
+        {
+          names.add(orderName);
+        }
+      }
+      for (int i = 0, l = alorders.size(); i < l; i++)
+      {
+        alignFrame.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering",
+                alorders.get(i));
+      }
+    }
+  }
+}
diff --git a/src/jalview/hmmer/HMMBuild.java b/src/jalview/hmmer/HMMBuild.java
new file mode 100644 (file)
index 0000000..cb087fa
--- /dev/null
@@ -0,0 +1,373 @@
+package jalview.hmmer;
+
+import jalview.analysis.SeqsetUtils.SequenceInfo;
+import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.HMMFile;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class that runs the hmmbuild command as a separate process.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class HMMBuild extends HmmerCommand
+{
+  static final String ARG_AMINO = "--amino";
+
+  static final String ARG_DNA = "--dna";
+
+  static final String ARG_RNA = "--rna";
+
+  /**
+   * Constructor
+   * 
+   * @param alignFrame
+   * @param args
+   */
+  public HMMBuild(AlignFrame alignFrame, List<ArgumentI> args)
+  {
+    super(alignFrame, args);
+  }
+
+  /**
+   * Builds a HMM from an alignment (and/or groups), then imports and adds it to
+   * the alignment (and/or groups). Call this method directly to execute
+   * synchronously, or via start() in a new Thread for asynchronously.
+   */
+  @Override
+  public void run()
+  {
+    if (params == null || params.isEmpty())
+    {
+      Console.error("No parameters to HMMBuild!|");
+      return;
+    }
+
+    long msgID = System.currentTimeMillis();
+    af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
+            msgID);
+
+    AlignViewportI viewport = af.getViewport();
+    try
+    {
+      /*
+       * run hmmbuild for alignment and/or groups as selected
+       */
+      List<AnnotatedCollectionI> runBuildFor = parseParameters(viewport);
+
+      for (AnnotatedCollectionI grp : runBuildFor)
+      {
+        runHMMBuild(grp);
+      }
+    } finally
+    {
+      af.setProgressBar("", msgID);
+      viewport.alignmentChanged(af.alignPanel);
+      af.buildColourMenu(); // to enable HMMER colour schemes
+    }
+  }
+
+  /**
+   * Scans the parameters to determine whether to run hmmmbuild for the whole
+   * alignment or specified subgroup(s) or both
+   * 
+   * @param viewport
+   * @return
+   */
+  protected List<AnnotatedCollectionI> parseParameters(
+          AlignViewportI viewport)
+  {
+    List<AnnotatedCollectionI> runBuildFor = new ArrayList<>();
+    boolean foundArg = false;
+
+    for (ArgumentI arg : params)
+    {
+      String name = arg.getName();
+      if (MessageManager.getString("label.hmmbuild_for").equals(name))
+      {
+        foundArg = true;
+        String value = arg.getValue();
+
+        if (MessageManager.getString("label.alignment").equals(value))
+        {
+          runBuildFor.add(viewport.getAlignmentView(false)
+                  .getVisibleAlignment('-'));
+        }
+        else if (MessageManager.getString("label.groups_and_alignment")
+                .equals(value))
+        {
+          runBuildFor.add(viewport.getAlignmentView(false)
+                  .getVisibleAlignment('-'));
+          runBuildFor.addAll(viewport.getAlignment().getGroups());
+        }
+        else if (MessageManager.getString("label.groups").equals(value))
+        {
+          runBuildFor.addAll(viewport.getAlignment().getGroups());
+        }
+        else if (MessageManager.getString("label.selected_group")
+                .equals(value))
+        {
+          runBuildFor.add(viewport.getSelectionGroup());
+        }
+      }
+      else if (MessageManager.getString("label.use_reference")
+              .equals(name))
+      {
+        // todo disable this option if no RF annotation on alignment
+        if (!af.getViewport().hasReferenceAnnotation())
+        {
+          JvOptionPane.showInternalMessageDialog(af, MessageManager
+                  .getString("warn.no_reference_annotation"));
+          // return;
+        }
+      }
+    }
+
+    /*
+     * default is to build for the whole alignment
+     */
+    if (!foundArg)
+    {
+      runBuildFor.add(alignment);
+    }
+
+    return runBuildFor;
+  }
+
+  /**
+   * Runs hmmbuild on the given sequences (alignment or group)
+   * 
+   * @param grp
+   */
+  private void runHMMBuild(AnnotatedCollectionI ac)
+  {
+    File hmmFile = null;
+    File alignmentFile = null;
+    try
+    {
+      hmmFile = FileUtils.createTempFile("hmm", ".hmm");
+      alignmentFile = FileUtils.createTempFile("output", ".sto");
+
+      if (ac instanceof Alignment)
+      {
+        AlignmentI al = (Alignment) ac;
+        // todo pad gaps in an unaligned SequenceGroup as well?
+        if (!al.isAligned())
+        {
+          al.padGaps();
+        }
+      }
+
+      deleteHmmSequences(ac);
+
+      List<SequenceI> copy = new ArrayList<>();
+      if (ac instanceof Alignment)
+      {
+        copy.addAll(ac.getSequences());
+      }
+      else
+      {
+        SequenceI[] sel = ((SequenceGroup) ac)
+                                               .getSelectionAsNewSequences((AlignmentI) ac.getContext());
+        for (SequenceI seq : sel)
+        {
+          if (seq != null)
+          {
+            copy.add(seq);
+          }
+        }
+      }
+      // TODO rather than copy alignment data we should anonymize in situ -
+      // export/File import could use anonymization hash to reinstate references
+      // at import level ?
+
+      SequenceI[] copyArray = copy.toArray(new SequenceI[copy.size()]);
+      Map<String, SequenceInfo> sequencesHash = stashSequences(copyArray);
+
+      exportStockholm(copyArray, alignmentFile, ac);
+
+      recoverSequences(sequencesHash, copy.toArray(new SequenceI[] {}));
+
+      boolean ran = runCommand(alignmentFile, hmmFile, ac);
+      if (!ran)
+      {
+        JvOptionPane.showInternalMessageDialog(af, MessageManager
+                .formatMessage("warn.command_failed", "hmmbuild"));
+        return;
+      }
+      importData(hmmFile, ac);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    } finally
+    {
+      if (hmmFile != null)
+      {
+        hmmFile.delete();
+      }
+      if (alignmentFile != null)
+      {
+        alignmentFile.delete();
+      }
+    }
+  }
+
+  /**
+   * Constructs and executes the hmmbuild command as a separate process
+   * 
+   * @param sequencesFile
+   *          the alignment from which the HMM is built
+   * @param hmmFile
+   *          the output file to which the HMM is written
+   * @param group
+   *          alignment or group for which the hmm is generated
+   * 
+   * @return
+   * @throws IOException
+   */
+  private boolean runCommand(File sequencesFile, File hmmFile,
+          AnnotatedCollectionI group) throws IOException
+  {
+    String cmd = getCommandPath(HMMBUILD);
+    if (cmd == null)
+    {
+      return false; // executable not found
+    }
+    List<String> args = new ArrayList<>();
+    args.add(cmd);
+
+    /*
+     * HMM name (will be given to consensus sequence) is
+     * - as specified by an input parameter if set
+     * - else group name with _HMM appended (if for a group)
+     * - else align frame title with _HMM appended (if title is not too long)
+     * - else "Alignment_HMM" 
+     */
+    String name = "";
+
+    if (params != null)
+    {
+      for (ArgumentI arg : params)
+      {
+        String argName = arg.getName();
+        switch (argName)
+        {
+        case "HMM Name":
+          name = arg.getValue().trim();
+          break;
+        case "Use Reference Annotation":
+          args.add("--hand");
+          break;
+        }
+      }
+    }
+
+    if (group instanceof SequenceGroup)
+    {
+      name = ((SequenceGroup) group).getName() + "_HMM";
+    }
+
+    if ("".equals(name))
+    {
+      if (af != null && af.getTitle().length() < 15)
+      {
+        name = af.getTitle();
+      }
+      else
+      {
+        name = "Alignment_HMM";
+      }
+    }
+
+    args.add("-n");
+    args.add(name.replace(' ', '_'));
+    if (!alignment.isNucleotide())
+    {
+      args.add(ARG_AMINO); // TODO check for rna
+    }
+    else
+    {
+      args.add(ARG_DNA);
+    }
+
+    args.add(getFilePath(hmmFile, true));
+    args.add(getFilePath(sequencesFile, true));
+
+    return runCommand(args);
+  }
+
+  /**
+   * Imports the .hmm file produced by hmmbuild, and inserts the HMM consensus
+   * sequence (with attached HMM profile) as the first sequence in the alignment
+   * or group for which it was generated
+   * 
+   * @param hmmFile
+   * @param ac
+   *          (optional) the group for which the hmm was generated
+   * @throws IOException
+   */
+  private void importData(File hmmFile, AnnotatedCollectionI ac)
+          throws IOException
+  {
+    if (hmmFile.length() == 0L)
+    {
+      Console.error("Error: hmmbuild produced empty hmm file");
+      return;
+    }
+
+    HMMFile file = new HMMFile(
+            new FileParse(hmmFile.getAbsolutePath(), DataSourceType.FILE));
+    SequenceI hmmSeq = file.getHMM().getConsensusSequence();
+
+
+
+    ResidueCount counts = new ResidueCount(alignment.getSequences());
+    hmmSeq.getHMM().setBackgroundFrequencies(counts);
+
+    if (hmmSeq == null)
+    {
+      // hmmbuild failure not detected earlier
+      return;
+    }
+
+    if (ac instanceof SequenceGroup)
+    {
+      SequenceGroup grp = (SequenceGroup) ac;
+      char gapChar = alignment.getGapCharacter();
+      hmmSeq.insertCharAt(0, ac.getStartRes(), gapChar);
+      hmmSeq.insertCharAt(ac.getEndRes() + 1,
+              alignment.getWidth() - ac.getEndRes() - 1, gapChar);
+      SequenceI topSeq = grp.getSequencesInOrder(alignment)[0];
+      int topIndex = alignment.findIndex(topSeq);
+      alignment.insertSequenceAt(topIndex, hmmSeq);
+      ac.setSeqrep(hmmSeq);
+      grp.addSequence(hmmSeq, false);
+    }
+    else
+    {
+      alignment.insertSequenceAt(0, hmmSeq);
+    }
+  }
+}
diff --git a/src/jalview/hmmer/HMMERParamStore.java b/src/jalview/hmmer/HMMERParamStore.java
new file mode 100644 (file)
index 0000000..df9ab55
--- /dev/null
@@ -0,0 +1,481 @@
+package jalview.hmmer;
+
+import jalview.bin.Cache;
+import jalview.datamodel.SequenceI;
+import jalview.gui.Preferences;
+import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.WsParamSetI;
+import jalview.ws.params.simple.BooleanOption;
+import jalview.ws.params.simple.DoubleParameter;
+import jalview.ws.params.simple.FileParameter;
+import jalview.ws.params.simple.IntegerParameter;
+import jalview.ws.params.simple.LogarithmicParameter;
+import jalview.ws.params.simple.Option;
+import jalview.ws.params.simple.RadioChoiceParameter;
+import jalview.ws.params.simple.StringParameter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+
+public final class HMMERParamStore implements ParamDatastoreI
+{
+  private static final String HMMBUILD = "hmmbuild";
+
+  private static final String HMMALIGN = "hmmalign";
+
+  private static final String HMMSEARCH = "hmmsearch";
+
+  private static final String JACKHMMER = "jackhmmer";
+
+  private String name;
+
+  private List<WsParamSetI> presets = new ArrayList<>();
+
+  private AlignmentViewport viewport;
+
+  private HMMERParamStore(String nam, AlignmentViewport av)
+  {
+    this.viewport = av;
+    this.name = nam;
+  }
+
+  public static HMMERParamStore forBuild(AlignmentViewport viewport)
+  {
+    return new HMMERParamStore(HMMBUILD, viewport);
+  }
+
+  public static HMMERParamStore forAlign(AlignmentViewport viewport)
+  {
+    return new HMMERParamStore(HMMALIGN, viewport);
+  }
+
+  public static HMMERParamStore forSearch(AlignmentViewport viewport)
+  {
+    return new HMMERParamStore(HMMSEARCH, viewport);
+  }
+
+  public static HMMERParamStore forJackhmmer(AlignmentViewport viewport)
+  {
+    return new HMMERParamStore(JACKHMMER, viewport);
+  }
+
+  @Override
+  public List<WsParamSetI> getPresets()
+  {
+    return presets;
+  }
+
+  @Override
+  public WsParamSetI getPreset(String nam)
+  {
+    return null;
+  }
+
+  @Override
+  public List<ArgumentI> getServiceParameters()
+  {
+    List<ArgumentI> args = new ArrayList<>();
+    switch (name)
+    {
+    case HMMSEARCH:
+      getHMMSearchParams(args);
+      break;
+    case HMMALIGN:
+      getHMMAlignParams(args);
+      break;
+    case HMMBUILD:
+      getHMMBuildParams(args);
+      break;
+    case JACKHMMER:
+      getJackhmmerParams(args);
+    default:
+    }
+
+    return args;
+  }
+
+  /**
+   * Answers default parameters for hmmsearch, taking into account any
+   * configured as user preferences
+   * 
+   * @param args
+   */
+  private void getHMMSearchParams(List<ArgumentI> args)
+  {
+    /*
+     * 'Options'
+     */
+    args.add(new BooleanOption(
+            MessageManager.getString(HMMSearch.AUTO_ALIGN_SEQS_KEY),
+            MessageManager.getString("label.auto_align_seqs_desc"), false,
+            false, false, null));
+    args.add(new BooleanOption(
+            MessageManager.getString(HMMSearch.USE_ACCESSIONS_KEY),
+            MessageManager.getString("label.use_accessions_desc"), false,
+            false, true, null));
+    args.add(new BooleanOption(
+            MessageManager.getString(HMMSearch.TRIM_TERMINI_KEY),
+            MessageManager.getString("label.trim_termini_desc"), false,
+            false, true, null));
+    args.add(new BooleanOption(
+            MessageManager.getString(HMMSearch.RETURN_N_NEW_SEQ),
+            MessageManager.getString("label.check_for_new_sequences_desc"),
+            false, false, false, null));
+
+    /*
+     * 'Parameters'
+     */
+    addChoiceOfHmm(args);
+
+    // addChoiceOfDatabase(args);
+
+    String thisAlignment = MessageManager
+            .getString(HMMSearch.THIS_ALIGNMENT_KEY);
+    String database = MessageManager.getString("label.database");
+    args.add(new FileParameter(database, "", false, "", ""));
+    args.add(new IntegerParameter(
+            MessageManager.getString(HMMSearch.NUMBER_OF_RESULTS_KEY),
+            MessageManager.getString("label.number_of_results_desc"), true,
+            100, 0, 100000));
+    args.add(new RadioChoiceParameter(
+            MessageManager.getString(HMMSearch.REPORTING_CUTOFF_KEY), null,
+            Arrays.asList(MessageManager.getString(HMMSearch.CUTOFF_EVALUE),
+                    MessageManager.getString(HMMSearch.CUTOFF_SCORE)),
+            MessageManager.getString(HMMSearch.CUTOFF_EVALUE)));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.REPORTING_SEQ_EVALUE_KEY),
+            MessageManager.getString("label.reporting_seq_e_value_desc"),
+            false, 1D,
+            1E-100, 10D));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.REPORTING_DOM_EVALUE_KEY),
+            MessageManager.getString("label.reporting_dom_e_value_desc"),
+            false, 1D,
+            1E-100, 10D));
+    args.add(
+            new DoubleParameter(
+                    MessageManager
+                            .getString(HMMSearch.REPORTING_SEQ_SCORE_KEY),
+                    MessageManager.getString(
+                            "label.reporting_seq_score_desc"),
+                    false,
+                    0d, 0d, 1000d));
+    args.add(
+            new DoubleParameter(
+                    MessageManager
+                            .getString(HMMSearch.REPORTING_DOM_SCORE_KEY),
+                    MessageManager.getString(
+                            "label.reporting_dom_score_desc"),
+                    false,
+                    0d, 0d, 1000d));
+    args.add(new RadioChoiceParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_THRESHOLD_KEY),
+            null,
+            Arrays.asList(MessageManager.getString(HMMSearch.CUTOFF_EVALUE),
+                    MessageManager.getString(HMMSearch.CUTOFF_SCORE)),
+            MessageManager.getString(HMMSearch.CUTOFF_EVALUE)));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_SEQ_EVALUE_KEY),
+            MessageManager.getString("label.inclusion_seq_e_value_desc"),
+            false, 1D,
+            1E-100, 10D));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_DOM_EVALUE_KEY),
+            MessageManager.getString("label.inclusion_dom_e_value_desc"),
+            false, 1D,
+            1E-100, 10D));
+    args.add(new DoubleParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_SEQ_SCORE_KEY),
+            MessageManager.getString("label.inclusion_seq_score_desc"),
+            false, 0d, 0d,
+            1000d));
+    args.add(new DoubleParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_DOM_SCORE_KEY),
+            MessageManager.getString("label.inclusion_dom_score_desc"),
+            false, 0d, 0d,
+            1000d));
+  }
+
+  /**
+   * Answers default parameters for jackhmmer, taking into account any configured
+   * as user preferences
+   * 
+   * @param args
+   */
+  private void getJackhmmerParams(List<ArgumentI> args)
+  {
+
+    /*
+     * 'Parameters'
+     */
+    addChoiceOfSequence(args);
+
+    // addChoiceOfDatabase(args);
+
+    String database = MessageManager.getString("label.database");
+    args.add(new FileParameter(database, "", false, "", ""));
+    args.add(new IntegerParameter(
+            MessageManager.getString(HMMSearch.NUMBER_OF_ITERATIONS),
+            MessageManager.getString("label.number_of_iterations_desc"),
+            true, 5, 1, 20));
+    args.add(new RadioChoiceParameter(
+            MessageManager.getString(JackHMMER.REPORTING_CUTOFF_KEY), null,
+            Arrays.asList(MessageManager.getString(JackHMMER.CUTOFF_NONE),
+                    MessageManager.getString(JackHMMER.CUTOFF_EVALUE),
+                    MessageManager.getString(JackHMMER.CUTOFF_SCORE)),
+            MessageManager.getString(JackHMMER.CUTOFF_EVALUE)));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(JackHMMER.REPORTING_SEQ_EVALUE_KEY),
+            MessageManager.getString("label.reporting_seq_e_value_desc"),
+            false, 1D,
+            1E-38, 10D));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(JackHMMER.REPORTING_DOM_EVALUE_KEY),
+            MessageManager.getString(
+                    "label.reporting_dom_e_value_desc"),
+            false, 1D,
+            1E-38, 10D));
+    args.add(new DoubleParameter(
+            MessageManager.getString(JackHMMER.REPORTING_SEQ_SCORE_KEY),
+            MessageManager.getString("label.reporting_seq_score_desc"),
+            false, 0d, 0d,
+            1000d));
+    args.add(new DoubleParameter(
+            MessageManager.getString(JackHMMER.REPORTING_DOM_SCORE_KEY),
+            MessageManager.getString("label.reporting_dom_score_desc"),
+            false, 0d, 0d,
+            1000d));
+    args.add(new RadioChoiceParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_THRESHOLD_KEY),
+            null,
+            Arrays.asList(MessageManager.getString(HMMSearch.CUTOFF_EVALUE),
+                    MessageManager.getString(HMMSearch.CUTOFF_SCORE)),
+            MessageManager.getString(HmmerCommand.CUTOFF_EVALUE)));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_SEQ_EVALUE_KEY),
+            MessageManager.getString("label.inclusion_seq_e_value_desc"),
+            false, 1D, 1E-100, 10D));
+    args.add(new LogarithmicParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_DOM_EVALUE_KEY),
+            MessageManager.getString("label.inclusion_dom_e_value_desc"),
+            false, 1D, 1E-100, 10D));
+    args.add(new DoubleParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_SEQ_SCORE_KEY),
+            MessageManager.getString("label.inclusion_seq_score_desc"),
+            false, 0d, 0d, 1000d));
+    args.add(new DoubleParameter(
+            MessageManager.getString(HMMSearch.INCLUSION_DOM_SCORE_KEY),
+            MessageManager.getString("label.inclusion_dom_score_desc"),
+            false, 0d, 0d, 1000d));
+  }
+
+  /**
+   * Constructs a choice parameter for database to search; always includes 'this
+   * alignment', and also includes any databases held under user preferences key
+   * "HMMSEARCH_DBS" as a comma-delimited list
+   * 
+   * @param args
+   */
+  protected void addChoiceOfDatabase(List<ArgumentI> args)
+  {
+    String names = Cache.getProperty(Preferences.HMMSEARCH_DBS);
+    if (names == null || names.isEmpty())
+    {
+      return;
+    }
+
+    List<String> filePaths = new ArrayList<>();
+    List<String> fileNames = new ArrayList<>();
+
+    String thisAlignment = MessageManager.getString(HMMSearch.THIS_ALIGNMENT_KEY);
+    filePaths.add(thisAlignment);
+    fileNames.add(thisAlignment);
+
+    Scanner nameScanner = new Scanner(names);
+    nameScanner.useDelimiter(Preferences.COMMA);
+
+    while (nameScanner.hasNext())
+    {
+      String next = nameScanner.next();
+      if ("null".equals(next))
+      {
+        Cache.setProperty(Preferences.HMMSEARCH_DBS, "");
+      }
+      else
+      {
+        filePaths.add(next);
+        int pos = next.lastIndexOf(File.separator);
+        String fileName = next.substring(pos + 1);
+        fileNames.add(fileName);
+      }
+    }
+    nameScanner.close();
+    ArgumentI databasesOption = new StringParameter(
+            MessageManager.getString(HMMSearch.DATABASE_KEY),
+            MessageManager.getString("label.database_for_hmmsearch"), true,
+            thisAlignment,
+            thisAlignment,
+            filePaths, fileNames);
+    args.add(databasesOption);
+  }
+
+  /**
+   * Answers default parameters for hmmalign, taking into account any configured
+   * as user preferences
+   * 
+   * @param args
+   */
+  private void getHMMAlignParams(List<ArgumentI> args)
+  {
+    addChoiceOfHmm(args);
+
+    boolean def = Cache.getDefault(Preferences.HMMALIGN_TRIM_TERMINI,
+            false);
+    args.add(new BooleanOption(
+            MessageManager.getString("label.trim_termini"),
+            MessageManager.getString("label.trim_termini_desc"),
+            false, false, def, null));
+  }
+
+  /**
+   * Adds an argument representing the choice of HMM sequences (profiles)
+   * against which to perform align or search, provided at least one is found
+   * 
+   * @param args
+   */
+  protected void addChoiceOfHmm(List<ArgumentI> args)
+  {
+    List<SequenceI> hmms = viewport.getAlignment().getHmmSequences();
+    if (!hmms.isEmpty())
+    {
+      List<String> options = new ArrayList<>();
+      for (SequenceI hmmSeq : hmms)
+      {
+        options.add(hmmSeq.getName());
+      }
+      String defseq = options.get(0);
+      ArgumentI arg = new StringParameter(
+              MessageManager.getString("label.use_hmm"), null, true, defseq,
+              defseq, options, null);
+      args.add(arg);
+    }
+  }
+
+  /**
+   * Adds an argument representing the choice of sequence against which to perform
+   * jackhmmer
+   * 
+   * @param args
+   */
+  protected void addChoiceOfSequence(List<ArgumentI> args)
+  {
+    List<SequenceI> sequences = viewport.getAlignment().getSequences();
+
+    List<String> options = new ArrayList<>();
+
+    for (SequenceI seq : sequences)
+    {
+      options.add(seq.getName());
+    }
+
+    String defseq = options.get(0);
+    ArgumentI arg = new StringParameter(
+            MessageManager.getString("label.use_sequence"), null, true,
+            defseq,
+            defseq, options, null);
+    args.add(arg);
+  }
+
+  /**
+   * Answers default parameters for hmmbuild, taking into account any configured
+   * as user preferences
+   * 
+   * @param args
+   */
+  private void getHMMBuildParams(List<ArgumentI> args)
+  {
+    /*
+     * name to give the computed alignment HMM consensus sequence
+     * (Jalview constructs group HMM consensus sequence names)
+     */
+    String defValue = "Alignment_HMM";
+    StringParameter nameParam = new StringParameter(MessageManager.getString("label.hmm_name"),
+            MessageManager.getString("label.hmm_name_desc"), true, defValue,
+            defValue);
+    args.add(nameParam);
+
+    /*
+     * only enable Use Reference Annotation if RF is present
+     */
+    if (viewport.hasReferenceAnnotation())
+    {
+      args.add(new BooleanOption(
+              MessageManager.getString("label.use_reference"),
+              MessageManager.getString("label.use_reference_desc"), true,
+              true, true, null));
+    }
+
+    /*
+     * choice of whether to compute HMM for alignment and/or group(s)
+     * - only if there are any groups
+     */
+    if (!viewport.getAlignment().getGroups().isEmpty())
+    {
+      List<String> options = new ArrayList<>();
+      options.add(MessageManager.getString("label.alignment"));
+      options.add(MessageManager.getString("label.groups_and_alignment"));
+      options.add(MessageManager.getString("label.groups"));
+      options.add(MessageManager.getString("label.selected_group"));
+      args.add(new Option(MessageManager.getString("label.hmmbuild_for"),
+              MessageManager.getString("label.hmmbuild_for_desc"), true,
+              MessageManager.getString("label.alignment"),
+              MessageManager.getString("label.alignment"), options, null));
+    }
+  }
+
+  @Override
+  public boolean presetExists(String forName)
+  {
+    return false;
+  }
+
+  @Override
+  public void deletePreset(String forName)
+  {
+  }
+
+  @Override
+  public void storePreset(String presetName, String text,
+          List<ArgumentI> jobParams)
+  {
+  }
+
+  @Override
+  public void updatePreset(String oldName, String presetName, String text,
+          List<ArgumentI> jobParams)
+  {
+  }
+
+  @Override
+  public WsParamSetI parseServiceParameterFile(String forName,
+          String description, String[] serviceURL, String parameters)
+          throws IOException
+  {
+    return null;
+  }
+
+  @Override
+  public String generateServiceParameterFile(WsParamSetI pset)
+          throws IOException
+  {
+    return null;
+  }
+
+}
diff --git a/src/jalview/hmmer/HMMERPreset.java b/src/jalview/hmmer/HMMERPreset.java
new file mode 100644 (file)
index 0000000..2712259
--- /dev/null
@@ -0,0 +1,57 @@
+package jalview.hmmer;
+
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+
+public class HMMERPreset implements WsParamSetI
+{
+
+  @Override
+  public String getName()
+  {
+    return null;
+  }
+
+  @Override
+  public String getDescription()
+  {
+    return null;
+  }
+
+  @Override
+  public String[] getApplicableUrls()
+  {
+    return null;
+  }
+
+  @Override
+  public String getSourceFile()
+  {
+    return null;
+  }
+
+  @Override
+  public void setSourceFile(String newfile)
+  {
+  }
+
+  @Override
+  public boolean isModifiable()
+  {
+    return false;
+  }
+
+  @Override
+  public List<ArgumentI> getArguments()
+  {
+    return null;
+  }
+
+  @Override
+  public void setArguments(List<ArgumentI> args)
+  {
+  }
+
+}
diff --git a/src/jalview/hmmer/HMMSearch.java b/src/jalview/hmmer/HMMSearch.java
new file mode 100644 (file)
index 0000000..767a56e
--- /dev/null
@@ -0,0 +1,319 @@
+package jalview.hmmer;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.simple.BooleanOption;
+import jalview.ws.params.simple.Option;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+public class HMMSearch extends Search
+{
+
+  boolean realign = false;
+
+  boolean trim = false;
+
+  boolean returnNoOfNewSeqs = false;
+
+  int seqsToReturn = Integer.MAX_VALUE;
+
+
+  /**
+   * Constructor for the HMMSearchThread
+   * 
+   * @param af
+   */
+  public HMMSearch(AlignFrame af, List<ArgumentI> args)
+  {
+    super(af, args);
+  }
+
+  /**
+   * Runs the HMMSearchThread: the data on the alignment or group is exported,
+   * then the command is executed in the command line and then the data is
+   * imported and displayed in a new frame. Call this method directly to execute
+   * synchronously, or via start() in a new Thread for asynchronously.
+   */
+  @Override
+  public void run()
+  {
+    HiddenMarkovModel hmm = getHmmProfile();
+    if (hmm == null)
+    {
+      // shouldn't happen if we got this far
+      Console.error("Error: no hmm for hmmsearch");
+      return;
+    }
+
+    SequenceI hmmSeq = hmm.getConsensusSequence();
+    long msgId = System.currentTimeMillis();
+    af.setProgressBar(MessageManager.getString("status.running_search"),
+            msgId);
+
+    try
+    {
+      File hmmFile = FileUtils.createTempFile("hmm", ".hmm");
+      File hitsAlignmentFile = FileUtils.createTempFile("hitAlignment",
+              ".sto");
+      File searchOutputFile = FileUtils.createTempFile("searchOutput",
+              ".sto");
+
+      exportHmm(hmm, hmmFile.getAbsoluteFile());
+
+      boolean ran = runCommand(searchOutputFile, hitsAlignmentFile, hmmFile);
+      if (!ran)
+      {
+        JvOptionPane.showInternalMessageDialog(af, MessageManager
+                .formatMessage("warn.command_failed", "hmmsearch"));
+        return;
+      }
+
+      importData(hmmSeq, hitsAlignmentFile, hmmFile, searchOutputFile);
+      // TODO make realignment of search results a step at this level
+      // and make it conditional on this.realign
+    } catch (IOException | InterruptedException e)
+    {
+      e.printStackTrace();
+    }
+    finally
+    {
+      af.setProgressBar("", msgId);
+    }
+  }
+
+  /**
+   * Executes an hmmsearch with the given hmm as input. The database to be
+   * searched is a local file as specified by the 'Database' parameter, or the
+   * current alignment (written to file) if none is specified.
+   * 
+   * @param searchOutputFile
+   * @param hitsAlignmentFile
+   * @param hmmFile
+   * 
+   * @return
+   * @throws IOException
+   */
+  private boolean runCommand(File searchOutputFile, File hitsAlignmentFile,
+          File hmmFile) throws IOException
+  {
+    String command = getCommandPath(HMMSEARCH);
+    if (command == null)
+    {
+      return false;
+    }
+
+    List<String> args = new ArrayList<>();
+    args.add(command);
+    buildArguments(args, searchOutputFile, hitsAlignmentFile, hmmFile);
+
+    return runCommand(args);
+  }
+
+
+  /**
+   * Imports the data from the temporary file to which the output of hmmsearch
+   * was directed. The results are optionally realigned using hmmalign.
+   * 
+   * @param hmmSeq
+   */
+  private void importData(SequenceI hmmSeq, File inputAlignmentTemp,
+          File hmmTemp, File searchOutputFile)
+          throws IOException, InterruptedException
+  {
+    BufferedReader br = new BufferedReader(
+            new FileReader(inputAlignmentTemp));
+    try
+    {
+      if (br.readLine() == null)
+      {
+        JOptionPane.showMessageDialog(af,
+                MessageManager.getString("label.no_sequences_found"));
+        return;
+      }
+      StockholmFile file = new StockholmFile(new FileParse(
+              inputAlignmentTemp.getAbsolutePath(), DataSourceType.FILE));
+      seqs = file.getSeqsAsArray();
+
+      readDomainTable(searchOutputFile, false);
+
+      if (searchAlignment)
+      {
+        recoverSequences(sequencesHash, seqs);
+      }
+
+      // look for PP cons and ref seq in alignment only annotation
+      AlignmentAnnotation modelpos = null, ppcons = null;
+      for (AlignmentAnnotation aa : file.getAnnotations())
+      {
+        if (aa.sequenceRef == null)
+        {
+          if (aa.label.equals("Reference Positions")) // RF feature type in
+                                                      // stockholm parser
+          {
+            modelpos = aa;
+          }
+          if (aa.label.equals("Posterior Probability"))
+          {
+            ppcons = aa;
+          }
+        }
+      }
+
+
+      int seqCount = Math.min(seqs.length, seqsToReturn);
+      SequenceI[] hmmAndSeqs = new SequenceI[seqCount + 1];
+      hmmSeq = hmmSeq.deriveSequence(); // otherwise all bad things happen
+      hmmAndSeqs[0] = hmmSeq;
+      System.arraycopy(seqs, 0, hmmAndSeqs, 1, seqCount);
+      if (modelpos != null)
+      {
+        // TODO need - get ungapped sequence method
+        hmmSeq.setSequence(
+                hmmSeq.getDatasetSequence().getSequenceAsString());
+        Annotation[] refpos = modelpos.annotations;
+        // insert gaps to match with refseq positions
+        int gc = 0, lcol = 0;
+        for (int c = 0; c < refpos.length; c++)
+        {
+          if (refpos[c] != null && ("x".equals(refpos[c].displayCharacter)))
+          {
+            if (gc > 0)
+            {
+              hmmSeq.insertCharAt(lcol + 1, gc, '-');
+            }
+            gc = 0;
+            lcol = c;
+          }
+          else
+          {
+            gc++;
+          }
+        }
+      }
+
+      if (realign)
+      {
+        realignResults(hmmAndSeqs);
+      }
+      else
+      {
+        AlignmentI al = new Alignment(hmmAndSeqs);
+        if (ppcons != null)
+        {
+          al.addAnnotation(ppcons);
+        }
+        if (modelpos != null)
+        {
+          al.addAnnotation(modelpos);
+        }
+        AlignFrame alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+                AlignFrame.DEFAULT_HEIGHT);
+        String ttl = "hmmSearch of " + databaseName + " using "
+                + hmmSeq.getName();
+        Desktop.addInternalFrame(alignFrame, ttl, AlignFrame.DEFAULT_WIDTH,
+                AlignFrame.DEFAULT_HEIGHT);
+
+        if (returnNoOfNewSeqs)
+        {
+          int nNew = checkForNewSequences();
+          JvOptionPane.showMessageDialog(af.alignPanel, nNew + " "
+                  + MessageManager.getString("label.new_returned"));
+        }
+
+      }
+
+
+      hmmTemp.delete();
+      inputAlignmentTemp.delete();
+      searchOutputFile.delete();
+    } finally
+    {
+      if (br != null)
+      {
+        br.close();
+      }
+    }
+  }
+
+  private int checkForNewSequences()
+  {
+    int nNew = seqs.length;
+
+    for (SequenceI resultSeq : seqs)
+    {
+      for (SequenceI aliSeq : alignment.getSequencesArray())
+      {
+        if (resultSeq.getName().equals(aliSeq.getName()))
+        {
+          nNew--;
+          break;
+        }
+      }
+    }
+
+    return nNew;
+
+  }
+
+  /**
+   * Realigns the given sequences using hmmalign, to the HMM profile sequence
+   * which is the first in the array, and opens the results in a new frame
+   * 
+   * @param hmmAndSeqs
+   */
+  protected void realignResults(SequenceI[] hmmAndSeqs)
+  {
+    /*
+     * and align the search results to the HMM profile
+     */
+    AlignmentI al = new Alignment(hmmAndSeqs);
+    AlignFrame frame = new AlignFrame(al, 1, 1);
+    List<ArgumentI> alignArgs = new ArrayList<>();
+    String alignTo = hmmAndSeqs[0].getName();
+    List<String> options = Collections.singletonList(alignTo);
+    Option option = new Option(MessageManager.getString("label.use_hmm"),
+            "", true, alignTo, alignTo, options, null);
+    alignArgs.add(option);
+    if (trim)
+    {
+      alignArgs.add(new BooleanOption(
+              MessageManager.getString(TRIM_TERMINI_KEY),
+              MessageManager.getString("label.trim_termini_desc"), true,
+              true, true, null));
+    }
+    HmmerCommand hmmalign = new HMMAlign(frame, alignArgs);
+    hmmalign.run();
+
+    if (returnNoOfNewSeqs)
+    {
+      int nNew = checkForNewSequences();
+      JvOptionPane.showMessageDialog(frame.alignPanel,
+              nNew + " " + MessageManager.getString("label.new_returned"));
+    }
+  }
+
+}
diff --git a/src/jalview/hmmer/HmmerCommand.java b/src/jalview/hmmer/HmmerCommand.java
new file mode 100644 (file)
index 0000000..9db0ae1
--- /dev/null
@@ -0,0 +1,539 @@
+package jalview.hmmer;
+
+import jalview.analysis.SeqsetUtils;
+import jalview.analysis.SeqsetUtils.SequenceInfo;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+import jalview.gui.Preferences;
+import jalview.io.FastaFile;
+import jalview.io.HMMFile;
+import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.params.ArgumentI;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for hmmbuild, hmmalign and hmmsearch
+ * 
+ * @author TZVanaalten
+ *
+ */
+public abstract class HmmerCommand implements Runnable
+{
+  public static final String HMMBUILD = "hmmbuild";
+
+  protected final AlignFrame af;
+
+  protected final AlignmentI alignment;
+
+  protected final List<ArgumentI> params;
+
+  /*
+   * constants for i18n lookup of passed parameter names
+   */
+  static final String DATABASE_KEY = "label.database";
+
+  static final String THIS_ALIGNMENT_KEY = "label.this_alignment";
+
+  static final String USE_ACCESSIONS_KEY = "label.use_accessions";
+
+  static final String AUTO_ALIGN_SEQS_KEY = "label.auto_align_seqs";
+
+  static final String NUMBER_OF_RESULTS_KEY = "label.number_of_results";
+
+  static final String NUMBER_OF_ITERATIONS = "label.number_of_iterations";
+
+  static final String TRIM_TERMINI_KEY = "label.trim_termini";
+
+  static final String RETURN_N_NEW_SEQ = "label.check_for_new_sequences";
+
+  static final String REPORTING_CUTOFF_KEY = "label.reporting_cutoff";
+
+  static final String CUTOFF_NONE = "label.default";
+
+  static final String CUTOFF_SCORE = "label.score";
+
+  static final String CUTOFF_EVALUE = "label.evalue";
+
+  static final String REPORTING_SEQ_EVALUE_KEY = "label.reporting_seq_evalue";
+
+  static final String REPORTING_DOM_EVALUE_KEY = "label.reporting_dom_evalue";
+
+  static final String REPORTING_SEQ_SCORE_KEY = "label.reporting_seq_score";
+
+  static final String REPORTING_DOM_SCORE_KEY = "label.reporting_dom_score";
+
+  static final String INCLUSION_SEQ_EVALUE_KEY = "label.inclusion_seq_evalue";
+
+  static final String INCLUSION_DOM_EVALUE_KEY = "label.inclusion_dom_evalue";
+
+  static final String INCLUSION_SEQ_SCORE_KEY = "label.inclusion_seq_score";
+
+  static final String INCLUSION_DOM_SCORE_KEY = "label.inclusion_dom_score";
+
+  static final String ARG_TRIM = "--trim";
+
+  static final String INCLUSION_THRESHOLD_KEY = "label.inclusion_threshold";
+
+  /**
+   * Constructor
+   * 
+   * @param alignFrame
+   * @param args
+   */
+  public HmmerCommand(AlignFrame alignFrame, List<ArgumentI> args)
+  {
+    af = alignFrame;
+    alignment = af.getViewport().getAlignment();
+    params = args;
+  }
+
+  /**
+   * Answers true if preference HMMER_PATH is set, and its value is the path to
+   * a directory that contains an executable <code>hmmbuild</code> or
+   * <code>hmmbuild.exe</code>, else false
+   * 
+   * @return
+   */
+  public static boolean isHmmerAvailable()
+  {
+    File exec = FileUtils.getExecutable(HMMBUILD,
+            Cache.getProperty(Preferences.HMMER_PATH));
+    return exec != null;
+  }
+
+  /**
+   * Uniquifies the sequences when exporting and stores their details in a
+   * hashtable
+   * 
+   * @param seqs
+   */
+  protected Map<String, SequenceInfo> stashSequences(SequenceI[] seqs)
+  {
+    return SeqsetUtils.uniquify(seqs, true);
+  }
+
+  /**
+   * Restores the sequence data lost by uniquifying
+   * 
+   * @param sequencesHash
+   * @param seqs
+   */
+  protected void recoverSequences(Map<String, SequenceInfo> sequencesHash, SequenceI[] seqs)
+  {
+    SeqsetUtils.deuniquify(sequencesHash, seqs);
+  }
+
+  /**
+   * Runs a command as a separate process and waits for it to complete. Answers
+   * true if the process return status is zero, else false.
+   * 
+   * @param commands
+   *          the executable command and any arguments to it
+   * @throws IOException
+   */
+  public boolean runCommand(List<String> commands)
+          throws IOException
+  {
+    List<String> args = Platform.isWindowsAndNotJS() ? wrapWithCygwin(commands)
+            : commands;
+
+    try
+    {
+      ProcessBuilder pb = new ProcessBuilder(args);
+      pb.redirectErrorStream(true); // merge syserr to sysout
+      if (Platform.isWindowsAndNotJS())
+      {
+        String path = pb.environment().get("Path");
+        path = jalview.bin.Cache.getProperty("CYGWIN_PATH") + ";" + path;
+        pb.environment().put("Path", path);
+      }
+      final Process p = pb.start();
+      new Thread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          BufferedReader input = new BufferedReader(
+                  new InputStreamReader(p.getInputStream()));
+          try
+          {
+            String line = input.readLine();
+            while (line != null)
+            {
+              System.out.println(line);
+              line = input.readLine();
+            }
+          } catch (IOException e)
+          {
+            e.printStackTrace();
+          }
+        }
+      }).start();
+
+      p.waitFor();
+      int exitValue = p.exitValue();
+      if (exitValue != 0)
+      {
+        Console.error("Command failed, return code = " + exitValue);
+        Console.error("Command/args were: " + args.toString());
+      }
+      return exitValue == 0; // 0 is success, by convention
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      return false;
+    }
+  }
+
+  /**
+   * Converts the given command to a Cygwin "bash" command wrapper. The hmmer
+   * command and any arguments to it are converted into a single parameter to the
+   * bash command.
+   * 
+   * @param commands
+   */
+  protected List<String> wrapWithCygwin(List<String> commands)
+  {
+    File bash = FileUtils.getExecutable("bash",
+            Cache.getProperty(Preferences.CYGWIN_PATH));
+    if (bash == null)
+    {
+      Console.error("Cygwin shell not found");
+      return commands;
+    }
+
+    List<String> wrapped = new ArrayList<>();
+    // wrapped.add("C:\Users\tva\run");
+    wrapped.add(bash.getAbsolutePath());
+    wrapped.add("-c");
+
+    /*
+     * combine hmmbuild/search/align and arguments to a single string
+     */
+    StringBuilder sb = new StringBuilder();
+    for (String cmd : commands)
+    {
+      sb.append(" ").append(cmd);
+    }
+    wrapped.add(sb.toString());
+
+    return wrapped;
+  }
+
+  /**
+   * Exports an alignment, and reference (RF) annotation if present, to the
+   * specified file, in Stockholm format, removing all HMM sequences
+   * 
+   * @param seqs
+   * @param toFile
+   * @param annotated
+   * @throws IOException
+   */
+  public void exportStockholm(SequenceI[] seqs, File toFile,
+          AnnotatedCollectionI annotated)
+          throws IOException
+  {
+    if (seqs == null)
+    {
+      return;
+    }
+    AlignmentI newAl = new Alignment(seqs);
+
+    if (!newAl.isAligned())
+    {
+      newAl.padGaps();
+    }
+
+    if (toFile != null && annotated != null)
+    {
+      AlignmentAnnotation[] annots = annotated.getAlignmentAnnotation();
+      if (annots != null)
+      {
+        for (AlignmentAnnotation annot : annots)
+        {
+          if (annot.label.contains("Reference") || "RF".equals(annot.label))
+          {
+            AlignmentAnnotation newRF;
+            if (annot.annotations.length > newAl.getWidth())
+            {
+              Annotation[] rfAnnots = new Annotation[newAl.getWidth()];
+              System.arraycopy(annot.annotations, 0, rfAnnots, 0,
+                      rfAnnots.length);
+              newRF = new AlignmentAnnotation("RF", "Reference Positions",
+                      rfAnnots);
+            }
+            else
+            {
+              newRF = new AlignmentAnnotation(annot);
+            }
+            newAl.addAnnotation(newRF);
+          }
+        }
+      }
+    }
+
+    for (SequenceI seq : newAl.getSequencesArray())
+    {
+      if (seq.getAnnotation() != null)
+      {
+        for (AlignmentAnnotation ann : seq.getAnnotation())
+        {
+          seq.removeAlignmentAnnotation(ann);
+        }
+      }
+    }
+
+    StockholmFile file = new StockholmFile(newAl);
+    String output = file.print(seqs, false);
+    PrintWriter writer = new PrintWriter(toFile);
+    writer.println(output);
+    writer.close();
+  }
+
+  /**
+   * Answers the full path to the given hmmer executable, or null if file cannot
+   * be found or is not executable
+   * 
+   * @param cmd
+   *          command short name e.g. hmmalign
+   * @return
+   * @throws IOException
+   */
+  protected String getCommandPath(String cmd)
+          throws IOException
+  {
+    String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
+    // ensure any symlink to the directory is resolved:
+    binariesFolder = Paths.get(binariesFolder).toRealPath().toString();
+    File file = FileUtils.getExecutable(cmd, binariesFolder);
+    if (file == null && af != null)
+    {
+      JvOptionPane.showInternalMessageDialog(af, MessageManager
+              .formatMessage("label.executable_not_found", cmd));
+    }
+
+    return file == null ? null : getFilePath(file, true);
+  }
+
+  /**
+   * Exports an HMM to the specified file
+   * 
+   * @param hmm
+   * @param hmmFile
+   * @throws IOException
+   */
+  public void exportHmm(HiddenMarkovModel hmm, File hmmFile)
+          throws IOException
+  {
+    if (hmm != null)
+    {
+      HMMFile file = new HMMFile(hmm);
+      PrintWriter writer = new PrintWriter(hmmFile);
+      writer.print(file.print());
+      writer.close();
+    }
+  }
+
+  // TODO is needed?
+  /**
+   * Exports a sequence to the specified file
+   * 
+   * @param hmm
+   * @param hmmFile
+   * @throws IOException
+   */
+  public void exportSequence(SequenceI seq, File seqFile) throws IOException
+  {
+    if (seq != null)
+    {
+      FastaFile file = new FastaFile();
+      PrintWriter writer = new PrintWriter(seqFile);
+      writer.print(file.print(new SequenceI[] { seq }, false));
+      writer.close();
+    }
+  }
+
+  /**
+   * Answers the HMM profile for the profile sequence the user selected (default
+   * is just the first HMM sequence in the alignment)
+   * 
+   * @return
+   */
+  protected HiddenMarkovModel getHmmProfile()
+  {
+    String alignToParamName = MessageManager.getString("label.use_hmm");
+    for (ArgumentI arg : params)
+    {
+      String name = arg.getName();
+      if (name.equals(alignToParamName))
+      {
+        String seqName = arg.getValue();
+        SequenceI hmmSeq = alignment.findName(seqName);
+        if (hmmSeq.hasHMMProfile())
+        {
+          return hmmSeq.getHMM();
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Answers the query sequence the user selected (default is just the first
+   * sequence in the alignment)
+   * 
+   * @return
+   */
+  protected SequenceI getSequence()
+  {
+    String alignToParamName = MessageManager
+            .getString("label.use_sequence");
+    for (ArgumentI arg : params)
+    {
+      String name = arg.getName();
+      if (name.equals(alignToParamName))
+      {
+        String seqName = arg.getValue();
+        SequenceI seq = alignment.findName(seqName);
+        return seq;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Answers an absolute path to the given file, in a format suitable for
+   * processing by a hmmer command. On a Windows platform, the native Windows file
+   * path is converted to Cygwin format, by replacing '\'with '/' and drive letter
+   * X with /cygdrive/x.
+   * 
+   * @param resultFile
+   * @param isInCygwin
+   *                     True if file is to be read/written from within the Cygwin
+   *                     shell. Should be false for any imports.
+   * @return
+   */
+  protected String getFilePath(File resultFile, boolean isInCygwin)
+  {
+    String path = resultFile.getAbsolutePath();
+    if (Platform.isWindowsAndNotJS() && isInCygwin)
+    {
+      // the first backslash escapes '\' for the regular expression argument
+      path = path.replaceAll("\\" + File.separator, "/");
+      int colon = path.indexOf(':');
+      if (colon > 0)
+      {
+        String drive = path.substring(0, colon);
+        path = path.replaceAll(drive + ":", "/cygdrive/" + drive);
+      }
+    }
+
+    return path;
+  }
+
+  /**
+   * A helper method that deletes any HMM consensus sequence from the given
+   * collection, and from the parent alignment if <code>ac</code> is a subgroup
+   * 
+   * @param ac
+   */
+  void deleteHmmSequences(AnnotatedCollectionI ac)
+  {
+    List<SequenceI> hmmSeqs = ac.getHmmSequences();
+    for (SequenceI hmmSeq : hmmSeqs)
+    {
+      if (ac instanceof SequenceGroup)
+      {
+        ((SequenceGroup) ac).deleteSequence(hmmSeq, false);
+        AnnotatedCollectionI context = ac.getContext();
+        if (context != null && context instanceof AlignmentI)
+        {
+          ((AlignmentI) context).deleteSequence(hmmSeq);
+        }
+      }
+      else
+      {
+        ((AlignmentI) ac).deleteSequence(hmmSeq);
+      }
+    }
+  }
+
+  /**
+   * Sets the names of any duplicates within the given sequences to include their
+   * respective lengths. Deletes any duplicates that have the same name after this
+   * step
+   * 
+   * @param seqs
+   */
+  void renameDuplicates(AlignmentI al)
+  {
+
+    SequenceI[] seqs = al.getSequencesArray();
+    List<Boolean> wasRenamed = new ArrayList<>();
+
+    for (SequenceI seq : seqs)
+    {
+      wasRenamed.add(false);
+    }
+
+    for (int i = 0; i < seqs.length; i++)
+    {
+      for (int j = 0; j < seqs.length; j++)
+      {
+        if (seqs[i].getName().equals(seqs[j].getName()) && i != j
+                && !wasRenamed.get(j))
+        {
+
+          wasRenamed.set(i, true);
+          String range = "/" + seqs[j].getStart() + "-" + seqs[j].getEnd();
+          // setting sequence name to include range - to differentiate between
+          // sequences of the same name. Currently have to include the range twice
+          // because the range is removed (once) when setting the name
+          // TODO come up with a better way of doing this
+          seqs[j].setName(seqs[j].getName() + range + range);
+        }
+
+      }
+      if (wasRenamed.get(i))
+      {
+        String range = "/" + seqs[i].getStart() + "-" + seqs[i].getEnd();
+        seqs[i].setName(seqs[i].getName() + range + range);
+      }
+    }
+
+    for (int i = 0; i < seqs.length; i++)
+    {
+      for (int j = 0; j < seqs.length; j++)
+      {
+        if (seqs[i].getName().equals(seqs[j].getName()) && i != j)
+        {
+          al.deleteSequence(j);
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/jalview/hmmer/JackHMMER.java b/src/jalview/hmmer/JackHMMER.java
new file mode 100644 (file)
index 0000000..00314f0
--- /dev/null
@@ -0,0 +1,180 @@
+package jalview.hmmer;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+public class JackHMMER extends Search
+{
+
+  SequenceI seq = null;
+
+  /**
+   * Constructor for the JackhmmerThread
+   * 
+   * @param af
+   */
+  public JackHMMER(AlignFrame af, List<ArgumentI> args)
+  {
+    super(af, args);
+  }
+
+  /**
+   * Runs the JackhmmerThread: the data on the alignment or group is exported,
+   * then the command is executed in the command line and then the data is
+   * imported and displayed in a new frame. Call this method directly to execute
+   * synchronously, or via start() in a new Thread for asynchronously.
+   */
+  @Override
+  public void run()
+  {
+    seq = getSequence();
+    if (seq == null)
+    {
+      // shouldn't happen if we got this far
+      Console.error("Error: no sequence for jackhmmer");
+      return;
+    }
+
+    long msgId = System.currentTimeMillis();
+    af.setProgressBar(MessageManager.getString("status.running_search"),
+            msgId);
+
+    try
+    {
+      File seqFile = FileUtils.createTempFile("seq", ".sto");
+      File hitsAlignmentFile = FileUtils.createTempFile("hitAlignment",
+              ".sto");
+      File searchOutputFile = FileUtils.createTempFile("searchOutput",
+              ".txt");
+
+      exportStockholm(new SequenceI[] { seq }, seqFile.getAbsoluteFile(),
+              null);
+
+      boolean ran = runCommand(searchOutputFile, hitsAlignmentFile,
+              seqFile);
+      if (!ran)
+      {
+        JvOptionPane.showInternalMessageDialog(af, MessageManager
+                .formatMessage("warn.command_failed", "jackhmmer"));
+        return;
+      }
+
+      importData(hitsAlignmentFile, seqFile, searchOutputFile);
+      // TODO make realignment of search results a step at this level
+      // and make it conditional on this.realign
+    } catch (IOException | InterruptedException e)
+    {
+      e.printStackTrace();
+    } finally
+    {
+      af.setProgressBar("", msgId);
+    }
+  }
+
+  /**
+   * Executes an jackhmmer search with the given sequence as input. The database
+   * to be searched is a local file as specified by the 'Database' parameter, or
+   * the current alignment (written to file) if none is specified.
+   * 
+   * @param searchOutputFile
+   * @param hitsAlignmentFile
+   * @param seqFile
+   * 
+   * @return
+   * @throws IOException
+   */
+  private boolean runCommand(File searchOutputFile, File hitsAlignmentFile,
+          File seqFile) throws IOException
+  {
+    String command = getCommandPath(JACKHMMER);
+    if (command == null)
+    {
+      return false;
+    }
+
+    List<String> args = new ArrayList<>();
+    args.add(command);
+    buildArguments(args, searchOutputFile, hitsAlignmentFile, seqFile);
+
+    return runCommand(args);
+  }
+
+  /**
+   * Imports the data from the temporary file to which the output of jackhmmer was
+   * directed.
+   */
+  private void importData(File inputAlignmentTemp, File seqTemp,
+          File searchOutputFile) throws IOException, InterruptedException
+  {
+    BufferedReader br = new BufferedReader(
+            new FileReader(inputAlignmentTemp));
+    try
+    {
+      if (br.readLine() == null)
+      {
+        JOptionPane.showMessageDialog(af,
+                MessageManager.getString("label.no_sequences_found"));
+        return;
+      }
+      StockholmFile file = new StockholmFile(new FileParse(
+              inputAlignmentTemp.getAbsolutePath(), DataSourceType.FILE));
+      seqs = file.getSeqsAsArray();
+
+      readDomainTable(searchOutputFile, true);
+
+      if (searchAlignment)
+      {
+        recoverSequences(sequencesHash, seqs);
+      }
+
+
+
+      int seqCount = seqs.length;
+
+
+      AlignmentI al = new Alignment(seqs);
+
+      AlignFrame alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+      String ttl = "jackhmmer search of " + databaseName + " using "
+              + seqs[0].getName();
+      Desktop.addInternalFrame(alignFrame, ttl, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+
+      seqTemp.delete();
+      inputAlignmentTemp.delete();
+      searchOutputFile.delete();
+    } finally
+    {
+      if (br != null)
+      {
+        br.close();
+      }
+    }
+  }
+
+
+
+
+}
diff --git a/src/jalview/hmmer/Search.java b/src/jalview/hmmer/Search.java
new file mode 100644 (file)
index 0000000..6c3a71d
--- /dev/null
@@ -0,0 +1,396 @@
+package jalview.hmmer;
+
+import jalview.analysis.SeqsetUtils.SequenceInfo;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.util.FileUtils;
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+public abstract class Search extends HmmerCommand
+{
+
+  static final String JACKHMMER = "jackhmmer";
+
+  static final String HMMSEARCH = "hmmsearch";
+
+  boolean realign = false;
+
+  boolean trim = false;
+
+  SequenceI[] seqs;
+
+  String databaseName;
+
+  boolean searchAlignment = true;
+
+  Map<String, SequenceInfo> sequencesHash;
+
+  public Search(AlignFrame alignFrame, List<ArgumentI> args)
+  {
+    super(alignFrame, args);
+  }
+
+  @Override
+  public void run()
+  {
+  }
+
+  /*
+  void readOutputFile(File inputTableTemp) throws IOException
+  {
+    BufferedReader br = new BufferedReader(new FileReader(inputTableTemp));
+  
+  
+    String line = "";
+    while (!line.startsWith("//"))
+    {
+  
+      while (!line.startsWith(">> ") && !line.startsWith("//"))
+      {
+        line = br.readLine();
+      }
+  
+      if (line.startsWith("//"))
+      {
+        break;
+      }
+  
+      Scanner scanner = new Scanner(line);
+      String name = scanner.next();
+      name = scanner.next();
+  
+      br.readLine();
+      br.readLine();
+  
+      List<SequenceI> domains = new ArrayList<>();
+  
+      for (SequenceI seq : seqs)
+      {
+        if (seq.getName().contains(name))
+        {
+          domains.add(seq);
+        }
+      }
+  
+      if (domains.contains(getSequence()))
+      {
+        domains.remove(getSequence());
+      }
+  
+      if (domains.size() > 0)
+      {
+        readOutputTable(br, domains);
+      }
+  
+      line = br.readLine();
+    }
+  
+  }
+  
+  
+  /**
+   * Reads in the scores table output by jackhmmer and adds annotation to
+   * sequences for E-value and bit score
+   * 
+   * @param inputTableTemp
+   * @throws IOException
+   */
+  /*
+  void readOutputTable(BufferedReader br, List<SequenceI> seqs) throws IOException
+  {
+    String line = br.readLine();
+  
+    while (!"".equals(line) && line != null)
+    {
+      if ("  ------ inclusion threshold ------".equals(line))
+      {
+        line = br.readLine();
+        continue;
+      }
+  
+      Scanner scanner = new Scanner(line);
+      scanner.next();
+      scanner.next();
+      String score = scanner.next();
+  
+      scanner.next();
+  
+      String evalue = scanner.next();
+  
+      scanner.next();
+      scanner.next();
+      scanner.next();
+      scanner.next();
+  
+      int start = scanner.nextInt();
+      int end = scanner.nextInt();
+  
+      SequenceI seq = null;
+      for (SequenceI sequence : seqs)
+      {
+        if (sequence.getStart() >= start && sequence.getEnd() <= end)
+        {
+          seq = sequence;
+          break;
+        }
+      }
+  
+      if (seq != null)
+      {
+        addScoreAnnotations(evalue, score, seq);
+      }
+  
+      scanner.close();
+      line = br.readLine();
+    }
+  }
+  */
+
+  void readDomainTable(File inputTableTemp, boolean includesQuery)
+          throws IOException
+  {
+    BufferedReader br = new BufferedReader(new FileReader(inputTableTemp));
+
+    String line = br.readLine();
+    br.readLine();
+    br.readLine();
+    line = br.readLine();
+
+    int index;
+
+    if (includesQuery)
+    {
+      index = 1;
+    }
+    else
+    {
+      index = 0;
+    }
+    while (!line.startsWith("#"))
+    {
+      if (line.contains("inclusion threshold"))
+      {
+        line = br.readLine();
+        continue;
+      }
+
+      Scanner scanner = new Scanner(line);
+      String name = scanner.next();
+
+      for (int i = 0; i < 10; i++)
+      {
+        scanner.next();
+      }
+
+      String evalue = scanner.next();
+      scanner.next();
+      String score = scanner.next();
+
+      addScoreAnnotations(evalue, score, seqs[index]);
+      index++;
+
+      scanner.close();
+      line = br.readLine();
+    }
+    br.close();
+  }
+
+
+
+
+  void addScoreAnnotations(String eValue, String bitScore, SequenceI seq)
+  {
+    String label = "Search Scores";
+    String description = "Full sequence bit score and E-Value";
+
+    try
+    {
+      AlignmentAnnotation annot = new AlignmentAnnotation(label,
+              description, null);
+
+      annot.label = label;
+      annot.description = description;
+
+      annot.setCalcId(JACKHMMER);
+
+      double dEValue = Double.parseDouble(eValue);
+      annot.setEValue(dEValue);
+
+      double dBitScore = Double.parseDouble(bitScore);
+      annot.setBitScore(dBitScore);
+
+      annot.setSequenceRef(seq);
+      seq.addAlignmentAnnotation(annot);
+
+    } catch (NumberFormatException e)
+    {
+      System.err.println("Error parsing " + label + " from " + eValue
+              + " & " + bitScore);
+    }
+  }
+
+  void buildArguments(List<String> args, File searchOutputFile,
+          File hitsAlignmentFile, File queryFile) throws IOException
+  {
+    args.add("--domtblout");
+    args.add(getFilePath(searchOutputFile, true));
+    args.add("-A");
+    args.add(getFilePath(hitsAlignmentFile, true));
+
+    File databaseFile = null;
+
+    boolean useEvalueCutoff = false;
+    boolean useScoreCutoff = false;
+    String seqReportingEvalueCutoff = null;
+    String domReportingEvalueCutoff = null;
+    String seqReportingScoreCutoff = null;
+    String domReportingScoreCutoff = null;
+    String seqInclusionEvalueCutoff = null;
+    String domInclusionEvalueCutoff = null;
+    String seqInclusionScoreCutoff = null;
+    String domInclusionScoreCutoff = null;
+    databaseName = "Alignment";
+
+    if (params != null)
+    {
+      for (ArgumentI arg : params)
+      {
+        String name = arg.getName();
+
+        if (MessageManager.getString(REPORTING_CUTOFF_KEY).equals(name))
+        {
+          if (MessageManager.getString(CUTOFF_EVALUE)
+                  .equals(arg.getValue()))
+          {
+            useEvalueCutoff = true;
+          }
+          else if (MessageManager.getString(CUTOFF_SCORE)
+                  .equals(arg.getValue()))
+          {
+            useScoreCutoff = true;
+          }
+        }
+        else if (MessageManager.getString(REPORTING_SEQ_EVALUE_KEY)
+                .equals(name))
+        {
+          seqReportingEvalueCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(REPORTING_SEQ_SCORE_KEY)
+                .equals(name))
+        {
+          seqReportingScoreCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(REPORTING_DOM_EVALUE_KEY)
+                .equals(name))
+        {
+          domReportingEvalueCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(REPORTING_DOM_SCORE_KEY)
+                .equals(name))
+        {
+          domReportingScoreCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(INCLUSION_SEQ_EVALUE_KEY)
+                .equals(name))
+        {
+          seqInclusionEvalueCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(INCLUSION_SEQ_SCORE_KEY)
+                .equals(name))
+        {
+          seqInclusionScoreCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(INCLUSION_DOM_EVALUE_KEY)
+                .equals(name))
+        {
+          domInclusionEvalueCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(INCLUSION_DOM_SCORE_KEY)
+                .equals(name))
+        {
+          domInclusionScoreCutoff = arg.getValue();
+        }
+        else if (MessageManager.getString(DATABASE_KEY).equals(name))
+        {
+          databaseFile = new File(arg.getValue());
+          if (!arg.getValue().isEmpty())
+          {
+            searchAlignment = false;
+          }
+        }
+        else if (MessageManager.getString(NUMBER_OF_ITERATIONS)
+                .equals(name))
+        {
+          if (!arg.getValue().isEmpty())
+          {
+            args.add("-N");
+            args.add(arg.getValue());
+          }
+        }
+      }
+    }
+
+    if (useEvalueCutoff)
+    {
+      args.add("-E");
+      args.add(seqReportingEvalueCutoff);
+      args.add("--domE");
+      args.add(domReportingEvalueCutoff);
+
+      args.add("--incE");
+      args.add(seqInclusionEvalueCutoff);
+      args.add("--incdomE");
+      args.add(domInclusionEvalueCutoff);
+    }
+    else if (useScoreCutoff)
+    {
+      args.add("-T");
+      args.add(seqReportingScoreCutoff);
+      args.add("--domT");
+      args.add(domReportingScoreCutoff);
+
+      args.add("--incT");
+      args.add(seqInclusionEvalueCutoff);
+      args.add("--incdomT");
+      args.add(domInclusionEvalueCutoff);
+    }
+
+    // if (!dbFound || MessageManager.getString(THIS_ALIGNMENT_KEY)
+    // .equals(dbPath))
+    if (searchAlignment)
+    {
+      /*
+       * no external database specified for search, so
+       * export current alignment as 'database' to search
+       */
+      databaseFile = FileUtils.createTempFile("database", ".sto");
+      AlignmentI al = af.getViewport().getAlignment();
+      AlignmentI copy = new Alignment(al);
+
+      deleteHmmSequences(copy);
+
+      if (searchAlignment)
+      {
+        sequencesHash = stashSequences(copy.getSequencesArray());
+      }
+
+      exportStockholm(copy.getSequencesArray(), databaseFile, null);
+    }
+
+    args.add(getFilePath(queryFile, true));
+    args.add(getFilePath(databaseFile, true));
+  }
+}
index a18d38d..7021fae 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.httpserver;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.rest.RestHandler;
 
 import java.net.BindException;
@@ -49,39 +51,8 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
  * @author gmcarstairs
  * @see http://eclipse.org/jetty/documentation/current/embedding-jetty.html
  */
-public class HttpServer
+public class HttpServer implements ApplicationSingletonI
 {
-  /*
-   * 'context root' - actually just prefixed to the path for each handler for
-   * now - see registerHandler
-   */
-  private static final String JALVIEW_PATH = "jalview";
-
-  /*
-   * Singleton instance of this server
-   */
-  private static HttpServer instance;
-
-  /*
-   * The Http server
-   */
-  private Server server;
-
-  /*
-   * Registered handlers for context paths
-   */
-  private HandlerCollection contextHandlers;
-
-  /*
-   * Lookup of ContextHandler by its wrapped handler
-   */
-  Map<Handler, ContextHandler> myHandlers = new HashMap<Handler, ContextHandler>();
-
-  /*
-   * The context root for the server
-   */
-  private URI contextRoot;
-
   /**
    * Returns the singleton instance of this class.
    * 
@@ -92,14 +63,10 @@ public class HttpServer
   {
     synchronized (HttpServer.class)
     {
-      if (instance == null)
-      {
-        instance = new HttpServer();
-      }
-      return instance;
+      return (HttpServer) ApplicationSingletonProvider.getInstance(HttpServer.class);
     }
   }
-
+  
   /**
    * Private constructor to enforce use of singleton
    * 
@@ -116,6 +83,34 @@ public class HttpServer
     registerHandler(RestHandler.getInstance());
   }
 
+
+  /*
+   * 'context root' - actually just prefixed to the path for each handler for
+   * now - see registerHandler
+   */
+  private static final String JALVIEW_PATH = "jalview";
+
+  /*
+   * The Http server
+   */
+  private Server server;
+
+  /*
+   * Registered handlers for context paths
+   */
+  private HandlerCollection contextHandlers;
+
+  /*
+   * Lookup of ContextHandler by its wrapped handler
+   */
+  Map<Handler, ContextHandler> myHandlers = new HashMap<Handler, ContextHandler>();
+
+  /*
+   * The context root for the server
+   */
+  private URI contextRoot;
+
+
   /**
    * Start the http server
    * 
index 3202ac9..3104772 100755 (executable)
@@ -341,9 +341,9 @@ public abstract class AlignFile extends FileParse
    */
   protected void initData()
   {
-    seqs = new Vector<SequenceI>();
-    annotations = new Vector<AlignmentAnnotation>();
-    seqGroups = new ArrayList<SequenceGroup>();
+    seqs = new Vector<>();
+    annotations = new Vector<>();
+    seqGroups = new ArrayList<>();
     parseCalled = false;
   }
 
@@ -356,7 +356,7 @@ public abstract class AlignFile extends FileParse
   @Override
   public void setSeqs(SequenceI[] s)
   {
-    seqs = new Vector<SequenceI>();
+    seqs = new Vector<>();
 
     for (int i = 0; i < s.length; i++)
     {
@@ -427,7 +427,7 @@ public abstract class AlignFile extends FileParse
   {
     if (newickStrings == null)
     {
-      newickStrings = new Vector<String[]>();
+      newickStrings = new Vector<>();
     }
     newickStrings.addElement(new String[] { treeName, newickString });
   }
@@ -451,4 +451,16 @@ public abstract class AlignFile extends FileParse
   {
     seqs.add(seq);
   }
+
+  /**
+   * Used only for hmmer statistics, so should probably be removed at some
+   * point. TODO remove this
+   * 
+   * @return
+   */
+  public Vector<AlignmentAnnotation> getAnnotations()
+  {
+    return annotations;
+  }
+
 }
index c3e148d..1c42896 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.io;
 
-import jalview.api.AlignExportSettingsI;
-import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
index 2049032..7497005 100644 (file)
@@ -22,8 +22,6 @@ package jalview.io;
 
 import jalview.api.AlignExportSettingsI;
 import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureSettingsModelI;
-import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
 public interface AlignmentFileWriterI
index 09859c9..89085c4 100755 (executable)
@@ -34,6 +34,7 @@ import java.util.Vector;
 
 import jalview.analysis.Conservation;
 import jalview.api.AlignViewportI;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -66,6 +67,11 @@ public class AnnotationFile
   private String lastread = "";
 
   /**
+   * default calcId string for new annotation
+   */
+  private String defaultCalcId = null;
+
+  /**
    * Constructor
    */
   public AnnotationFile()
@@ -665,6 +671,21 @@ public class AnnotationFile
     return readAnnotationFile(al, null, file, sourceType);
   }
 
+  /**
+   * read an annotation file onto the alignment, but apply the given calcId
+   * @param aln
+   * @param calcId
+   * @param file
+   * @param url
+   * @return
+   */
+  public boolean readAnnotationFileWithCalcId(Alignment aln, String calcId, String file,
+          DataSourceType url)
+  {
+    defaultCalcId=calcId;
+    return readAnnotationFile(aln, null, file, url);
+  }
+  
   public boolean readAnnotationFile(AlignmentI al, HiddenColumns hidden,
           Object file, DataSourceType sourceType)
   {
@@ -1005,7 +1026,7 @@ public class AnnotationFile
 
         annotation = new AlignmentAnnotation(label, description,
                 (index == 0) ? null : annotations, 0, 0, graphStyle);
-
+        annotation.setCalcId(defaultCalcId);
         annotation.score = score;
         if (!overrideAutoAnnot && autoAnnots
                 .containsKey(autoAnnotsKey(annotation, refSeq, groupRef)))
index 00fcb9c..bd1ca37 100755 (executable)
@@ -161,11 +161,7 @@ public class AppletFormatAdapter
   {
 
     this.selectedFile = selectedFile;
-    if (selectedFile != null)
-    {
-      this.inFile = selectedFile.getPath();
-    }
-    this.inFile = file;
+    inFile = (selectedFile == null ? file : selectedFile.getPath());
     try
     {
       if (fileFormat.isStructureFile())
diff --git a/src/jalview/io/BSMLFile.java b/src/jalview/io/BSMLFile.java
new file mode 100644 (file)
index 0000000..69e29f8
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * 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.io;
+
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
+import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
+import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;
+
+/**
+ * Preliminary reader for Bioinformatics Sequence Markup Language
+ * http://www.bsml.org
+ * 
+ * @author hansonr
+ *
+ */
+public class BSMLFile extends AlignFile
+{
+
+  public BSMLFile()
+  {
+    super();
+
+  }
+
+  public BSMLFile(String inFile, DataSourceType type) throws IOException
+  {
+    super(inFile, type);
+
+  }
+
+  public BSMLFile(FileParse source) throws IOException
+  {
+    super(source);
+
+  }
+
+  public BufferedReader CreateReader() throws FileNotFoundException
+  {
+    FileReader fr = null;
+    fr = new FileReader(inFile);
+
+    BufferedReader r = new BufferedReader(fr);
+    return r;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.AlignFile#parse()
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    try
+    {
+      _parse();
+    } catch (ExceptionPermissionDenied pdx)
+    {
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_couldnt_access_datasource", new String[]
+              { pdx.getMessage() });
+      throw new IOException(pdx);
+    } catch (ExceptionLoadingFailed lf)
+    {
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_couldnt_process_data", new String[]
+              { lf.getMessage() });
+      throw new IOException(lf);
+    } catch (ExceptionFileFormatOrSyntax iff)
+    {
+      errormessage = MessageManager
+              .formatMessage("exception.BSML_invalid_file", new String[]
+              { iff.getMessage() });
+      throw new IOException(iff);
+    } catch (Exception x)
+    {
+      error = true;
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_problem_parsing_data", new String[]
+              { x.getMessage() });
+      throw new IOException(errormessage, x);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public void _parse()
+          throws ExceptionPermissionDenied, ExceptionLoadingFailed,
+          ExceptionFileFormatOrSyntax, ParserConfigurationException,
+          SAXException, IOException
+  {
+
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+    dbf.setIgnoringElementContentWhitespace(true);
+    dbf.setIgnoringComments(true);
+    dbf.setValidating(true);
+    dbf.setCoalescing(true);
+    dbf.setNamespaceAware(true);
+      dbf.setFeature("http://xml.org/sax/features/namespaces", false);
+      dbf.setFeature("http://xml.org/sax/features/validation", false);
+      dbf.setFeature(
+              "http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
+              false);
+      dbf.setFeature(
+              "http://apache.org/xml/features/nonvalidating/load-external-dtd",
+              false);
+
+      DocumentBuilder db = dbf.newDocumentBuilder();
+
+      Map<String, SequenceI> htSeq = new Hashtable<>();
+      InputSource is = new InputSource(getReader());
+      Document d = db.parse(is);
+      NodeList sequences = d.getElementsByTagName("Sequence-data");
+      int n = sequences.getLength();
+      SequenceI[] sqs = new SequenceI[n];
+      for (int i = 0; i < n; i++)
+      {
+        Element e = (Element) sequences.item(i);
+        String s = e.getTextContent();
+        String id = e.getAttribute("seq-name");
+        SequenceI seq = sqs[i] = new Sequence(id, s, 1, s.length());
+        htSeq.put(id, seq);
+        // ?? sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength()));
+      }
+
+      sequences = d.getElementsByTagName("Sequence");
+      n = sequences.getLength();
+      for (int i = 0; i < n; i++)
+      {
+        Element e = (Element) sequences.item(i);
+        String mol = e.getAttribute("molecule"); // dna or rna
+        if (!"dna".equals(mol))
+        {
+          System.err.println("BSML molecule=rna not implemented");
+          continue;
+        }
+        String title = e.getAttribute("title");
+        SequenceI seq = htSeq.get(title);
+        if (seq == null)
+        {
+          continue;
+        }
+      NodeList features = e.getElementsByTagName("Feature");
+        int featureCount = features.getLength();
+        for (int f = 0; f < featureCount; f++)
+        {
+          Element feature = (Element) features.item(f);
+          // <Feature class="GENE" title="CPXV-GER_1980_EP4-211">
+          // <Interval-loc complement="0" endpos="217104" startpos="216643"/>
+          // <Resource id="GENE-ID:119705"/>
+          // </Feature>
+        Element iloc = (Element) feature
+                .getElementsByTagName("Interval-loc").item(0);
+          String complement = iloc.getAttribute("complement");
+          if (!"0".equals(complement))
+          {
+            // Jalview cannot handle complement genes (running backward on the
+            // complementary strand);
+            continue;
+          }
+          String fclass = feature.getAttribute("class");
+          if (!"GENE".equals(fclass))
+          {
+            // just processing GENE features for now;
+            continue;
+          }
+          String ftitle = feature.getAttribute("title");
+        int start = Integer.parseInt(iloc.getAttribute("startpos"));
+        int end = Integer.parseInt(iloc.getAttribute("endpos"));
+        SequenceFeature sf = new SequenceFeature("GENE", ftitle, start, end,
+                null);
+        seq.addSequenceFeature(sf);
+        }
+        setSeqs(sqs);
+      }
+
+  }
+
+  @Override
+  public String print(SequenceI[] s, boolean jvSuffix)
+  {
+    return "not yet implemented";
+  }
+
+}
index 2039d3c..828bd0d 100644 (file)
@@ -633,12 +633,12 @@ public class BackupFiles
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.rename") };
 
+      // TODO enable JvOptionPane to behave appropriately when batch/headless
       confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
-              : JvOptionPane.showOptionDialog(Desktop.desktop,
+              : JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
                       messageSB.toString(),
                       MessageManager.getString(
                               "label.backupfiles_confirm_delete"),
-                      // "Confirm delete"
                       JvOptionPane.YES_NO_OPTION,
                       JvOptionPane.WARNING_MESSAGE, null, options,
                       options[0]);
@@ -662,14 +662,11 @@ public class BackupFiles
           MessageManager.getString("label.keep") };
 
       confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
-              : JvOptionPane.showOptionDialog(Desktop.desktop,
-                      messageSB.toString(),
-                      MessageManager.getString(
-                              "label.backupfiles_confirm_delete"),
-                      // "Confirm delete"
-                      JvOptionPane.YES_NO_OPTION,
-                      JvOptionPane.WARNING_MESSAGE, null, options,
-                      options[0]);
+              : JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
+              messageSB.toString(),
+              MessageManager.getString("label.backupfiles_confirm_delete"),
+              JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
+              null, options, options[0]);
     }
 
     // return should be TRUE if file is to be deleted
@@ -703,7 +700,7 @@ public class BackupFiles
         }
 
         int confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION
-                : JvOptionPane.showConfirmDialog(Desktop.desktop,
+           : JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
                         messageSB.toString(),
                         MessageManager.getString(
                                 "label.backupfiles_confirm_delete"),
@@ -806,7 +803,7 @@ public class BackupFiles
               .append(MessageManager.getString("label.continue_operation"));
 
       int confirmButton = Platform.isHeadless() ? JvOptionPane.OK_OPTION
-              : JvOptionPane.showConfirmDialog(Desktop.desktop,
+              : JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
                       messageSB.toString(),
                       MessageManager.getString(
                               "label.backupfiles_confirm_save_file"),
index c88d8eb..82058ec 100644 (file)
@@ -26,6 +26,7 @@ import jalview.gui.OOMWarning;
 import jalview.json.binding.biojs.BioJSReleasePojo;
 import jalview.json.binding.biojs.BioJSRepositoryPojo;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
@@ -45,8 +46,7 @@ public class BioJsHTMLOutput extends HTMLOutput
 
   private static TreeMap<String, File> bioJsMSAVersions;
 
-  public static final String DEFAULT_DIR = System.getProperty("user.home")
-          + File.separatorChar + ".biojs_templates" + File.separatorChar;
+  public static final String DEFAULT_DIR = Platform.getUserPath(".biojs_templates/");
 
   public static final String BJS_TEMPLATES_LOCAL_DIRECTORY = Cache
           .getDefault("biojs_template_directory", DEFAULT_DIR);
diff --git a/src/jalview/io/CountReader.java b/src/jalview/io/CountReader.java
new file mode 100644 (file)
index 0000000..a691c53
--- /dev/null
@@ -0,0 +1,63 @@
+package jalview.io;
+
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.util.MessageManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.swing.JFileChooser;
+
+public class CountReader
+{
+  public static ResidueCount getBackgroundFrequencies(AlignmentPanel ap, SequenceI seq) throws MalformedURLException, IOException
+  {
+    JFileChooser bkgdFreqChooser = new JFileChooser();
+    
+    bkgdFreqChooser.showOpenDialog(ap);
+    
+    File file = bkgdFreqChooser.getSelectedFile();
+    if (file == null)
+    {
+      return null;
+    }
+    
+    IdentifyFile identifier = new IdentifyFile();
+    FileFormatI format = null;
+    try
+    {
+      format = identifier.identify(file.getPath(), DataSourceType.FILE);
+    } catch (Exception e)
+    {
+
+    }
+    
+    if (format == null)
+    {
+      if (!Jalview.isHeadlessMode())
+      {
+        JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
+                MessageManager.getString("label.couldnt_read_data") + " in "
+                        + file + "\n"
+                        + AppletFormatAdapter.getSupportedFormats(),
+                MessageManager.getString("label.couldnt_read_data"),
+                JvOptionPane.WARNING_MESSAGE);
+      }
+    }
+
+    FileParse parser = new FileParse(file.getPath(), DataSourceType.FILE);
+    AlignmentI al = new FormatAdapter().readFromFile(parser, format);
+    parser.close();
+    
+    ResidueCount counts = new ResidueCount(al.getSequences());
+    
+    return counts;
+  }
+}
index b701963..cfee8e9 100644 (file)
@@ -405,8 +405,38 @@ public enum FileFormat implements FileFormatI
     {
       return true;
     }
+  },
+  HMMER3("HMMER3", "hmm", true, true)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      return new HMMFile(source);
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return new HMMFile();
+    }
+  },   BSML("BSML", "bbb", true, false)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      return new BSMLFile(source);
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return null;
+    }
   };
 
+
   private boolean writable;
 
   private boolean readable;
index b7dd834..b0d5d17 100644 (file)
@@ -29,6 +29,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton registry of alignment file formats known to Jalview. On startup,
  * the 'built-in' formats are added (from the FileFormat enum). Additional
@@ -38,23 +41,11 @@ import java.util.Set;
  * @author gmcarstairs
  *
  */
-public class FileFormats
+public class FileFormats implements ApplicationSingletonI
 {
-  private static FileFormats instance = new FileFormats();
-
-  /*
-   * A lookup map of file formats by upper-cased name
-   */
-  private static Map<String, FileFormatI> formats;
-
-  /*
-   * Formats in this set are capable of being identified by IdentifyFile 
-   */
-  private static Set<FileFormatI> identifiable;
-
   public static FileFormats getInstance()
   {
-    return instance;
+    return (FileFormats) ApplicationSingletonProvider.getInstance(FileFormats.class);
   }
 
   /**
@@ -65,6 +56,17 @@ public class FileFormats
     reset();
   }
 
+  /*
+   * A lookup map of file formats by upper-cased name
+   */
+  private Map<String, FileFormatI> formats;
+
+  /*
+   * Formats in this set are capable of being identified by IdentifyFile 
+   */
+  private Set<FileFormatI> identifiable;
+
+
   /**
    * Reset to just the built-in file formats packaged with Jalview. These are
    * added (and will be shown in menus) in the order of their declaration in the
index 4016e71..834e004 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.io;
 
+import java.awt.Dimension;
 import java.io.File;
 import java.io.IOException;
 import java.util.StringTokenizer;
@@ -46,10 +47,14 @@ import jalview.project.Jalview2XML;
 import jalview.schemes.ColourSchemeI;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.utils.UrlDownloadClient;
 
+import java.util.ArrayList;
+import java.util.List;
 public class FileLoader implements Runnable
 {
+  private static final String TAB = "\t";
   String file;
 
   DataSourceType protocol;
@@ -213,56 +218,78 @@ public class FileLoader implements Runnable
     return alignFrame;
   }
 
-  public void updateRecentlyOpened()
+  public void LoadFileOntoAlignmentWaitTillLoaded(AlignViewport viewport,
+          String file, DataSourceType sourceType, FileFormatI format)
   {
     Vector<String> recent = new Vector<>();
     if (protocol == DataSourceType.PASTE)
+    this.viewport = viewport;
+    this.file = file;
+    this.protocol = sourceType;
+    this.format = format;
+    _LoadFileWaitTillLoaded();
+  }
+
+
+  /**
+   * Updates (or creates) the tab-separated list of recently opened files held
+   * under the given property name by inserting the filePath at the front of the
+   * list. Duplicates are removed, and the list is limited to 11 entries. The
+   * method returns the updated value of the property.
+   * 
+   * @param filePath
+   * @param sourceType
+   */
+  public static String updateRecentlyOpened(String filePath,
+          DataSourceType sourceType)
+  {
+    if (sourceType != DataSourceType.FILE
+            && sourceType != DataSourceType.URL)
     {
-      // do nothing if the file was pasted in as text... there is no filename to
-      // refer to it as.
-      return;
+      return null;
     }
-    if (file != null
-            && file.indexOf(System.getProperty("java.io.tmpdir")) > -1)
+    String propertyName = sourceType == DataSourceType.FILE ? "RECENT_FILE"
+            : "RECENT_URL";
+    String historyItems = Cache.getProperty(propertyName);
+    if (filePath != null
+            && filePath.indexOf(System.getProperty("java.io.tmpdir")) > -1)
     {
       // ignore files loaded from the system's temporary directory
-      return;
+      return null;
     }
-    String type = protocol == DataSourceType.FILE ? "RECENT_FILE"
-            : "RECENT_URL";
-
-    String historyItems = Cache.getProperty(type);
 
-    StringTokenizer st;
+    List<String> recent = new ArrayList<>();
 
     if (historyItems != null)
     {
-      st = new StringTokenizer(historyItems, "\t");
+      StringTokenizer st = new StringTokenizer(historyItems, TAB);
 
       while (st.hasMoreTokens())
       {
-        recent.addElement(st.nextToken().trim());
+        String trimmed = st.nextToken().trim();
+       recent.add(trimmed);
       }
     }
 
-    if (recent.contains(file))
+    /*
+     * if file was already in the list, it moves to the top
+     */
+    if (recent.contains(filePath))
     {
-      recent.remove(file);
+      recent.remove(filePath);
     }
 
-    StringBuffer newHistory = new StringBuffer(file);
+    StringBuilder newHistory = new StringBuilder(filePath);
     for (int i = 0; i < recent.size() && i < 10; i++)
     {
-      newHistory.append("\t");
-      newHistory.append(recent.elementAt(i));
+      newHistory.append(TAB);
+      newHistory.append(recent.get(i));
     }
 
-    Cache.setProperty(type, newHistory.toString());
+    String newProperty = newHistory.toString();
+    Cache.setProperty(propertyName, newProperty);
 
-    if (protocol == DataSourceType.FILE)
-    {
-      Cache.setProperty("DEFAULT_FILE_FORMAT", format.getName());
-    }
+    return newProperty;
   }
 
   @Override
@@ -274,9 +301,9 @@ public class FileLoader implements Runnable
     Runtime rt = Runtime.getRuntime();
     try
     {
-      if (Desktop.instance != null)
+      if (Desktop.getInstance() != null)
       {
-        Desktop.instance.startLoading(file);
+        Desktop.getInstance().startLoading(file);
       }
       if (format == null)
       {
@@ -299,12 +326,12 @@ public class FileLoader implements Runnable
 
       if (format == null)
       {
-        Desktop.instance.stopLoading();
+        Desktop.getInstance().stopLoading();
         System.err.println("The input file \"" + file
                 + "\" has null or unidentifiable data content!");
         if (!Jalview.isHeadlessMode())
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+          JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                   MessageManager.getString("label.couldnt_read_data")
                           + " in " + file + "\n"
                           + AppletFormatAdapter.getSupportedFormats(),
@@ -315,7 +342,7 @@ public class FileLoader implements Runnable
       }
       // TODO: cache any stream datasources as a temporary file (eg. PDBs
       // retrieved via URL)
-      if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
+      if (Desktop.getDesktopPane() != null && Desktop.getDesktopPane().isShowMemoryUsage())
       {
         System.gc();
         memused = (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // free
@@ -411,7 +438,7 @@ public class FileLoader implements Runnable
                 // register PDB entries with desktop's structure selection
                 // manager
                 StructureSelectionManager
-                        .getStructureSelectionManager(Desktop.instance)
+                        .getStructureSelectionManager(Desktop.getInstance())
                         .registerPDBEntry(pdbe);
               }
             }
@@ -421,9 +448,29 @@ public class FileLoader implements Runnable
                   .getFeatureColourScheme();
           if (viewport != null)
           {
+            // TODO: test if this needs to be done after addAlignment ? (like in 2.11.2)  
+            if (proxyColourScheme != null)
+            {
+              viewport.applyFeaturesStyle(proxyColourScheme);
+            }
             // append to existing alignment
             viewport.addAlignment(al, title);
-            viewport.applyFeaturesStyle(proxyColourScheme);
+            if (source instanceof HMMFile)
+            {
+              AlignmentI alignment = viewport.getAlignment();
+              SequenceI seq = alignment
+                      .getSequenceAt(alignment.getHeight() - 1);
+              if (seq.hasHMMProfile())
+              {
+                /* 
+                 * fudge: move HMM consensus sequence from last to first
+                 */
+                alignment.deleteSequence(alignment.getAbsoluteHeight() - 1);
+                alignment.insertSequenceAt(0, seq);
+              }
+              viewport.getAlignPanel().adjustAnnotationHeight();
+              viewport.updateSequenceIdColours();
+            }
           }
           else
           {
@@ -463,8 +510,7 @@ public class FileLoader implements Runnable
             // add metadata and update ui
             if (!(protocol == DataSourceType.PASTE))
             {
-              alignFrame.setFileName(file, format);
-              alignFrame.setFileObject(selectedFile); // BH 2018 SwingJS
+              alignFrame.setFile(file, selectedFile, protocol, format);
             }
             if (proxyColourScheme != null)
             {
@@ -482,8 +528,12 @@ public class FileLoader implements Runnable
               // status in Jalview 3
               // TODO: define 'virtual desktop' for benefit of headless scripts
               // that perform queries to find the 'current working alignment'
-              Desktop.addInternalFrame(alignFrame, title,
+              
+              Dimension dim = Platform.getDimIfEmbedded(alignFrame,
                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+              alignFrame.setSize(dim);
+              Desktop.addInternalFrame(alignFrame, title, dim.width,
+                      dim.height);
             }
 
             try
@@ -497,23 +547,23 @@ public class FileLoader implements Runnable
         }
         else
         {
-          if (Desktop.instance != null)
+          if (Desktop.getInstance() != null)
           {
-            Desktop.instance.stopLoading();
+            Desktop.getInstance().stopLoading();
           }
 
           final String errorMessage = MessageManager.getString(
                   "label.couldnt_load_file") + " " + title + "\n" + error;
           // TODO: refactor FileLoader to be independent of Desktop / Applet GUI
           // bits ?
-          if (raiseGUI && Desktop.desktop != null)
+          if (raiseGUI && Desktop.getDesktopPane() != null)
           {
             javax.swing.SwingUtilities.invokeLater(new Runnable()
             {
               @Override
               public void run()
               {
-                JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                         errorMessage,
                         MessageManager
                                 .getString("label.error_loading_file"),
@@ -528,7 +578,12 @@ public class FileLoader implements Runnable
         }
       }
 
-      updateRecentlyOpened();
+      updateRecentlyOpened(file, protocol);
+
+      if (protocol == DataSourceType.FILE && format != null)
+      {
+        Cache.setProperty("DEFAULT_FILE_FORMAT", format.getName());
+      }
 
     } catch (Exception er)
     {
@@ -541,7 +596,7 @@ public class FileLoader implements Runnable
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.formatMessage(
                             "label.problems_opening_file", new String[]
                             { file }),
@@ -563,7 +618,7 @@ public class FileLoader implements Runnable
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.formatMessage(
                             "warn.out_of_memory_loading_file", new String[]
                             { file }),
@@ -585,7 +640,7 @@ public class FileLoader implements Runnable
     // memory
     // after
     // load
-    if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
+    if (Desktop.getDesktopPane() != null && Desktop.getDesktopPane().isShowMemoryUsage())
     {
       if (alignFrame != null)
       {
@@ -607,9 +662,9 @@ public class FileLoader implements Runnable
       }
     }
     // remove the visual delay indicator
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Desktop.instance.stopLoading();
+      Desktop.getInstance().stopLoading();
     }
 
   }
diff --git a/src/jalview/io/HMMFile.java b/src/jalview/io/HMMFile.java
new file mode 100644 (file)
index 0000000..2fce4cc
--- /dev/null
@@ -0,0 +1,716 @@
+package jalview.io;
+
+import jalview.api.AlignExportSettingsI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.HMMNode;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+
+/**
+ * Adds capability to read in and write out HMMER3 files. .
+ * 
+ * 
+ * @author TZVanaalten
+ *
+ */
+public class HMMFile extends AlignFile
+        implements AlignmentFileReaderI, AlignmentFileWriterI
+{
+  private static final String TERMINATOR = "//";
+
+  /*
+   * keys to data in HMM file, used to store as properties of the HiddenMarkovModel
+   */
+  public static final String HMM = "HMM";
+
+  public static final String NAME = "NAME";
+
+  public static final String ACCESSION_NUMBER = "ACC";
+
+  public static final String DESCRIPTION = "DESC";
+
+  public static final String LENGTH = "LENG";
+
+  public static final String MAX_LENGTH = "MAXL";
+
+  public static final String ALPHABET = "ALPH";
+
+  public static final String DATE = "DATE";
+
+  public static final String COMMAND_LOG = "COM";
+
+  public static final String NUMBER_OF_SEQUENCES = "NSEQ";
+
+  public static final String EFF_NUMBER_OF_SEQUENCES = "EFFN";
+
+  public static final String CHECK_SUM = "CKSUM";
+
+  public static final String STATISTICS = "STATS";
+
+  public static final String COMPO = "COMPO";
+
+  public static final String GATHERING_THRESHOLD = "GA";
+
+  public static final String TRUSTED_CUTOFF = "TC";
+
+  public static final String NOISE_CUTOFF = "NC";
+
+  public static final String VITERBI = "VITERBI";
+
+  public static final String MSV = "MSV";
+
+  public static final String FORWARD = "FORWARD";
+
+  public static final String MAP = "MAP";
+
+  public static final String REFERENCE_ANNOTATION = "RF";
+
+  public static final String CONSENSUS_RESIDUE = "CONS";
+
+  public static final String CONSENSUS_STRUCTURE = "CS";
+
+  public static final String MASKED_VALUE = "MM";
+
+  private static final String ALPH_AMINO = "amino";
+
+  private static final String ALPH_DNA = "DNA";
+
+  private static final String ALPH_RNA = "RNA";
+
+  private static final String ALPHABET_AMINO = "ACDEFGHIKLMNPQRSTVWY";
+
+  private static final String ALPHABET_DNA = "ACGT";
+
+  private static final String ALPHABET_RNA = "ACGU";
+
+  private static final int NUMBER_OF_TRANSITIONS = 7;
+
+  private static final String SPACE = " ";
+
+  /*
+   * optional guide line added to an output HMMER file, purely for readability
+   */
+  private static final String TRANSITIONTYPELINE = "            m->m     m->i     m->d     i->m     i->i     d->m     d->d";
+
+  private static String NL = System.lineSeparator();
+
+  private HiddenMarkovModel hmm;
+
+  // number of symbols in the alphabet used in the hidden Markov model
+  private int numberOfSymbols;
+
+  /**
+   * Constructor that parses immediately
+   * 
+   * @param inFile
+   * @param type
+   * @throws IOException
+   */
+  public HMMFile(String inFile, DataSourceType type) throws IOException
+  {
+    super(inFile, type);
+  }
+
+  /**
+   * Constructor that parses immediately
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public HMMFile(FileParse source) throws IOException
+  {
+    super(source);
+  }
+
+  /**
+   * Default constructor
+   */
+  public HMMFile()
+  {
+  }
+
+  /**
+   * Constructor for HMMFile used for exporting
+   * 
+   * @param hmm
+   */
+  public HMMFile(HiddenMarkovModel markov)
+  {
+    hmm = markov;
+  }
+
+  /**
+   * Returns the HMM produced by parsing a HMMER3 file
+   * 
+   * @return
+   */
+  public HiddenMarkovModel getHMM()
+  {
+    return hmm;
+  }
+
+  /**
+   * Gets the name of the hidden Markov model
+   * 
+   * @return
+   */
+  public String getName()
+  {
+    return hmm.getName();
+  }
+
+  /**
+   * Reads the data from HMM file into the HMM model
+   */
+  @Override
+  public void parse()
+  {
+    try
+    {
+      hmm = new HiddenMarkovModel();
+      parseHeaderLines(dataIn);
+      parseModel(dataIn);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Reads the header properties from a HMMER3 file and saves them in the
+   * HiddeMarkovModel. This method exits after reading the next line after the
+   * HMM line.
+   * 
+   * @param input
+   * @throws IOException
+   */
+  void parseHeaderLines(BufferedReader input) throws IOException
+  {
+    boolean readingHeaders = true;
+    hmm.setFileHeader(input.readLine());
+    String line = input.readLine();
+    while (readingHeaders && line != null)
+    {
+      Scanner parser = new Scanner(line);
+      String next = parser.next();
+      if (ALPHABET.equals(next))
+      {
+        String alphabetType = parser.next();
+        hmm.setProperty(ALPHABET, alphabetType);
+        String alphabet = ALPH_DNA.equalsIgnoreCase(alphabetType)
+                ? ALPHABET_DNA
+                : (ALPH_RNA.equalsIgnoreCase(alphabetType) ? ALPHABET_RNA
+                        : ALPHABET_AMINO);
+        numberOfSymbols = hmm.setAlphabet(alphabet);
+      }
+      else if (HMM.equals(next))
+      {
+        readingHeaders = false;
+        String symbols = line.substring(line.indexOf(HMM) + HMM.length());
+        numberOfSymbols = hmm.setAlphabet(symbols);
+      }
+      else if (STATISTICS.equals(next))
+      {
+        parser.next();
+        String key;
+        String value;
+        key = parser.next();
+        value = parser.next() + SPACE + SPACE + parser.next();
+        hmm.setProperty(key, value);
+      }
+      else
+      {
+        String key = next;
+        String value = parser.next();
+        while (parser.hasNext())
+        {
+          value = value + SPACE + parser.next();
+        }
+        hmm.setProperty(key, value);
+      }
+      parser.close();
+      line = input.readLine();
+    }
+  }
+
+  /**
+   * Parses the model data from the HMMER3 file. The input buffer should be
+   * positioned at the (optional) COMPO line if there is one, else at the insert
+   * emissions line for the BEGIN node of the model.
+   * 
+   * @param input
+   * @throws IOException
+   */
+  void parseModel(BufferedReader input) throws IOException
+  {
+    /*
+     * specification says there must always be an HMM header (already read)
+     * and one more header (guide headings) which is skipped here
+     */
+    int nodeNo = 0;
+    String line = input.readLine();
+    List<HMMNode> nodes = new ArrayList<>();
+
+    while (line != null && !TERMINATOR.equals(line))
+    {
+      HMMNode node = new HMMNode();
+      nodes.add(node);
+      Scanner scanner = new Scanner(line);
+      String next = scanner.next();
+
+      /*
+       * expect COMPO (optional) for average match emissions
+       * or a node number followed by node's match emissions
+       */
+      if (COMPO.equals(next) || nodeNo > 0)
+      {
+        /*
+         * parse match emissions
+         */
+        double[] matches = parseDoubles(scanner, numberOfSymbols);
+        node.setMatchEmissions(matches);
+        if (!COMPO.equals(next))
+        {
+          int resNo = parseAnnotations(scanner, node);
+          if (resNo == 0)
+          {
+            /*
+             * no MAP annotation provided, just number off from 0 (begin node)
+             */
+            resNo = nodeNo;
+          }
+          node.setResidueNumber(resNo);
+        }
+        line = input.readLine();
+      }
+      scanner.close();
+
+      /*
+       * parse insert emissions
+       */
+      scanner = new Scanner(line);
+      double[] inserts = parseDoubles(scanner, numberOfSymbols);
+      node.setInsertEmissions(inserts);
+      scanner.close();
+
+      /*
+       * parse state transitions
+       */
+      line = input.readLine();
+      scanner = new Scanner(line);
+      double[] transitions = parseDoubles(scanner,
+              NUMBER_OF_TRANSITIONS);
+      node.setStateTransitions(transitions);
+      scanner.close();
+      line = input.readLine();
+
+      nodeNo++;
+    }
+
+    hmm.setNodes(nodes);
+  }
+
+  /**
+   * Parses the annotations on the match emission line and add them to the node.
+   * (See p109 of the HMMER User Guide (V3.1b2) for the specification.) Returns
+   * the residue position that the node maps to, if provided, else zero.
+   * 
+   * @param scanner
+   * @param node
+   */
+  int parseAnnotations(Scanner scanner, HMMNode node)
+  {
+    int mapTo = 0;
+
+    /*
+     * map from hmm node to sequence position, if provided
+     */
+    if (scanner.hasNext())
+    {
+      String value = scanner.next();
+      if (!"-".equals(value))
+      {
+        try
+        {
+          mapTo = Integer.parseInt(value);
+          node.setResidueNumber(mapTo);
+        } catch (NumberFormatException e)
+        {
+          // ignore
+        }
+      }
+    }
+
+    /*
+     * hmm consensus residue if provided, else '-'
+     */
+    if (scanner.hasNext())
+    {
+      node.setConsensusResidue(scanner.next().charAt(0));
+    }
+
+    /*
+     * RF reference annotation, if provided, else '-'
+     */
+    if (scanner.hasNext())
+    {
+      node.setReferenceAnnotation(scanner.next().charAt(0));
+    }
+
+    /*
+     * 'm' for masked position, if provided, else '-'
+     */
+    if (scanner.hasNext())
+    {
+      node.setMaskValue(scanner.next().charAt(0));
+    }
+
+    /*
+     * structure consensus symbol, if provided, else '-'
+     */
+    if (scanner.hasNext())
+    {
+      node.setConsensusStructure(scanner.next().charAt(0));
+    }
+
+    return mapTo;
+  }
+
+  /**
+   * Fills an array of doubles parsed from an input line
+   * 
+   * @param input
+   * @param numberOfElements
+   * @return
+   * @throws IOException
+   */
+  static double[] parseDoubles(Scanner input,
+          int numberOfElements) throws IOException
+  {
+    double[] values = new double[numberOfElements];
+    for (int i = 0; i < numberOfElements; i++)
+    {
+      if (!input.hasNext())
+      {
+        throw new IOException("Incomplete data");
+      }
+      String next = input.next();
+      if (next.contains("*"))
+      {
+        values[i] = Double.NEGATIVE_INFINITY;
+      }
+      else
+      {
+        double prob = Double.valueOf(next);
+        prob = Math.pow(Math.E, -prob);
+        values[i] = prob;
+      }
+    }
+    return values;
+  }
+
+  /**
+   * Returns a string to be added to the StringBuilder containing the entire
+   * output String.
+   * 
+   * @param initialColumnSeparation
+   *          The initial whitespace separation between the left side of the
+   *          file and first character.
+   * @param columnSeparation
+   *          The separation between subsequent data entries.
+   * @param data
+   *          The list of data to be added to the String.
+   * @return
+   */
+  String addData(int initialColumnSeparation,
+          int columnSeparation, List<String> data)
+  {
+    String line = "";
+    boolean first = true;
+    for (String value : data)
+    {
+      int sep = first ? initialColumnSeparation : columnSeparation;
+      line += String.format("%" + sep + "s", value);
+      first = false;
+    }
+    return line;
+  }
+
+  /**
+   * Converts list of characters into a list of Strings.
+   * 
+   * @param list
+   * @return Returns the list of Strings.
+   */
+  List<String> charListToStringList(List<Character> list)
+  {
+    List<String> strList = new ArrayList<>();
+    for (char value : list)
+    {
+      String strValue = Character.toString(value);
+      strList.add(strValue);
+    }
+    return strList;
+  }
+
+  /**
+   * Converts an array of doubles into a list of Strings, rounded to the nearest
+   * 5th decimal place
+   * 
+   * @param doubles
+   * @param noOfDecimals
+   * @return
+   */
+  List<String> doublesToStringList(double[] doubles)
+  {
+    List<String> strList = new ArrayList<>();
+    for (double value : doubles)
+    {
+      String strValue;
+      if (value > 0)
+      {
+        strValue = String.format("%.5f", value);
+      }
+      else if (value == -0.00000d)
+      {
+        strValue = "0.00000";
+      }
+      else
+      {
+        strValue = "*";
+      }
+      strList.add(strValue);
+    }
+    return strList;
+  }
+
+  /**
+   * Appends model data in string format to the string builder
+   * 
+   * @param output
+   */
+  void appendModelAsString(StringBuilder output)
+  {
+    output.append(HMM).append("  ");
+    String charSymbols = hmm.getSymbols();
+    for (char c : charSymbols.toCharArray())
+    {
+      output.append(String.format("%9s", c));
+    }
+    output.append(NL).append(TRANSITIONTYPELINE);
+
+    int length = hmm.getLength();
+
+    for (int nodeNo = 0; nodeNo <= length; nodeNo++)
+    {
+      String matchLine = String.format("%7s",
+              nodeNo == 0 ? COMPO : Integer.toString(nodeNo));
+
+      double[] doubleMatches = convertToLogSpace(
+              hmm.getNode(nodeNo).getMatchEmissions());
+      List<String> strMatches = doublesToStringList(doubleMatches);
+      matchLine += addData(10, 9, strMatches);
+
+      if (nodeNo != 0)
+      {
+        matchLine += SPACE + (hmm.getNodeMapPosition(nodeNo));
+        matchLine += SPACE + hmm.getConsensusResidue(nodeNo);
+        matchLine += SPACE + hmm.getReferenceAnnotation(nodeNo);
+        if (hmm.getFileHeader().contains("HMMER3/f"))
+        {
+          matchLine += SPACE + hmm.getMaskedValue(nodeNo);
+          matchLine += SPACE + hmm.getConsensusStructure(nodeNo);
+        }
+      }
+
+      output.append(NL).append(matchLine);
+      
+      String insertLine = "";
+
+      double[] doubleInserts = convertToLogSpace(
+              hmm.getNode(nodeNo).getInsertEmissions());
+      List<String> strInserts = doublesToStringList(doubleInserts);
+      insertLine += addData(17, 9, strInserts);
+
+      output.append(NL).append(insertLine);
+
+      String transitionLine = "";
+      double[] doubleTransitions = convertToLogSpace(
+              hmm.getNode(nodeNo).getStateTransitions());
+      List<String> strTransitions = doublesToStringList(
+              doubleTransitions);
+      transitionLine += addData(17, 9, strTransitions);
+
+      output.append(NL).append(transitionLine);
+    }
+  }
+
+  /**
+   * Appends formatted HMM file properties to the string builder
+   * 
+   * @param output
+   */
+  void appendProperties(StringBuilder output)
+  {
+    output.append(hmm.getFileHeader());
+
+    String format = "%n%-5s %1s";
+    appendProperty(output, format, NAME);
+    appendProperty(output, format, ACCESSION_NUMBER);
+    appendProperty(output, format, DESCRIPTION);
+    appendProperty(output, format, LENGTH);
+    appendProperty(output, format, MAX_LENGTH);
+    appendProperty(output, format, ALPHABET);
+    appendBooleanProperty(output, format, REFERENCE_ANNOTATION);
+    appendBooleanProperty(output, format, MASKED_VALUE);
+    appendBooleanProperty(output, format, CONSENSUS_RESIDUE);
+    appendBooleanProperty(output, format, CONSENSUS_STRUCTURE);
+    appendBooleanProperty(output, format, MAP);
+    appendProperty(output, format, DATE);
+    appendProperty(output, format, NUMBER_OF_SEQUENCES);
+    appendProperty(output, format, EFF_NUMBER_OF_SEQUENCES);
+    appendProperty(output, format, CHECK_SUM);
+    appendProperty(output, format, GATHERING_THRESHOLD);
+    appendProperty(output, format, TRUSTED_CUTOFF);
+    appendProperty(output, format, NOISE_CUTOFF);
+
+    if (hmm.getMSV() != null)
+    {
+      format = "%n%-19s %18s";
+      output.append(String.format(format, "STATS LOCAL MSV", hmm.getMSV()));
+
+      output.append(String.format(format, "STATS LOCAL VITERBI",
+              hmm.getViterbi()));
+
+      output.append(String.format(format, "STATS LOCAL FORWARD",
+              hmm.getForward()));
+    }
+  }
+
+  /**
+   * Appends 'yes' or 'no' for the given property, according to whether or not
+   * it is set in the HMM
+   * 
+   * @param output
+   * @param format
+   * @param propertyName
+   */
+  private void appendBooleanProperty(StringBuilder output, String format,
+          String propertyName)
+  {
+    boolean set = hmm.getBooleanProperty(propertyName);
+    output.append(String.format(format, propertyName,
+            set ? HiddenMarkovModel.YES : HiddenMarkovModel.NO));
+  }
+
+  /**
+   * Appends the value of the given property to the output, if not null
+   * 
+   * @param output
+   * @param format
+   * @param propertyName
+   */
+  private void appendProperty(StringBuilder output, String format,
+          String propertyName)
+  {
+    String value = hmm.getProperty(propertyName);
+    if (value != null)
+    {
+      output.append(String.format(format, propertyName, value));
+    }
+  }
+
+  @Override
+  public String print(SequenceI[] sequences, boolean jvsuffix)
+  {
+    if (sequences[0].getHMM() != null)
+    {
+      hmm = sequences[0].getHMM();
+    }
+    return print();
+  }
+
+  /**
+   * Prints the .hmm file to a String.
+   * 
+   * @return
+   */
+  public String print()
+  {
+    StringBuilder output = new StringBuilder();
+    appendProperties(output);
+    output.append(NL);
+    appendModelAsString(output);
+    output.append(NL).append(TERMINATOR).append(NL);
+    return output.toString();
+  }
+
+  /**
+   * Converts the probabilities contained in an array into log space
+   * 
+   * @param ds
+   */
+  double[] convertToLogSpace(double[] ds)
+  {
+    double[] converted = new double[ds.length];
+    for (int i = 0; i < ds.length; i++)
+    {
+      double prob = ds[i];
+      double logProb = -1 * Math.log(prob);
+
+      converted[i] = logProb;
+    }
+    return converted;
+  }
+
+  /**
+   * Returns the HMM sequence produced by reading a .hmm file.
+   */
+  @Override
+  public SequenceI[] getSeqsAsArray()
+  {
+    SequenceI hmmSeq = hmm.getConsensusSequence();
+    SequenceI[] seq = new SequenceI[1];
+    seq[0] = hmmSeq;
+    return seq;
+  }
+
+  @Override
+  public void setNewlineString(String newLine)
+  {
+    NL = newLine;
+  }
+
+  @Override
+  public void setExportSettings(AlignExportSettingsI exportSettings)
+  {
+
+  }
+
+  @Override
+  public void configureForView(AlignmentViewPanel viewpanel)
+  {
+
+  }
+
+  @Override
+  public boolean hasWarningMessage()
+  {
+    return false;
+  }
+
+  @Override
+  public String getWarningMessage()
+  {
+    return "warning message";
+  }
+
+}
+
index c21127e..c666bb0 100755 (executable)
@@ -21,7 +21,6 @@
 package jalview.io;
 
 import java.util.Locale;
-
 import java.io.File;
 import java.io.IOException;
 
@@ -189,6 +188,11 @@ public class IdentifyFile
           reply = FileFormat.ScoreMatrix;
           break;
         }
+        if (data.startsWith("HMMER3"))
+        {
+          reply = FileFormat.HMMER3;
+          break;
+        }
         if (data.startsWith("LOCUS"))
         {
           reply = FileFormat.GenBank;
@@ -347,6 +351,11 @@ public class IdentifyFile
             reply = FileFormat.Rnaml;
             break;
           }
+          if (upper.substring(lessThan).startsWith("<BSML"))
+          {
+            reply = FileFormat.BSML;
+            break;
+          }
         }
 
         if ((data.length() < 1) || (data.indexOf("#") == 0))
index e184d74..4976719 100755 (executable)
  */
 package jalview.io;
 
-import jalview.bin.Cache;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
+import jalview.bin.Console;
 
 import java.util.List;
 
@@ -96,10 +97,19 @@ public class ModellerDescription
     }
   };
 
+  private static Regex VALIDATION_REGEX;
+
+  private static Regex getRegex()
+  {
+    return (VALIDATION_REGEX == null
+            ? VALIDATION_REGEX = Platform
+                    .newRegex("\\s*((([-0-9]+).?)|FIRST|LAST|@)", null)
+            : VALIDATION_REGEX);
+  }
   private resCode validResidueCode(String field)
   {
     Integer val = null;
-    Regex r = new Regex("\\s*((([-0-9]+).?)|FIRST|LAST|@)");
+    Regex r = getRegex();
 
     if (!r.search(field))
     {
@@ -110,7 +120,7 @@ public class ModellerDescription
     {
       value = r.stringMatched(1);
     }
-    // Cache.debug("from '" + field + "' matched '" + value +
+    // jalview.bin.Console.debug("from '" + field + "' matched '" + value +
     // "'");
     try
     {
@@ -165,7 +175,7 @@ public class ModellerDescription
                 }
                 else
                 {
-                  // Cache.debug(
+                  // jalview.bin.Console.debug(
                   // "Ignoring non-Modeller description: invalid integer-like
                   // field '" + field + "'");
                   type = -1; /* invalid field! - throw the FieldSet away */
@@ -279,14 +289,15 @@ public class ModellerDescription
         List<DBRefEntry> dbr = seq.getDatasetSequence().getDBRefs();
         for (int i = 0, ni = dbr.size(); i < ni; i++)
         {
-          DBRefEntry dbri = dbr.get(i);
+               DBRefEntry dbri = dbr.get(i);
           if (dbri != null)
           {
             // JBPNote PDB dbRefEntry needs properties to propagate onto
             // ModellerField
             // JBPNote Need to get info from the user about whether the sequence
             // is the one being modelled, or if it is a template.
-            if (dbri.getSource().equals(jalview.datamodel.DBRefSource.PDB))
+            if (dbri.getSource()
+                    .equals(jalview.datamodel.DBRefSource.PDB))
             {
               fields.put(Fields[LOCALID], dbri.getAccessionId());
               t = 2;
index 027390a..8a464a4 100755 (executable)
@@ -30,6 +30,7 @@ import java.util.Locale;
 
 import jalview.datamodel.SequenceNode;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -39,6 +40,7 @@ import java.util.StringTokenizer;
 
 import com.stevesoft.pat.Regex;
 
+// TODO This class does not conform to Java standards for field name capitalization.
 /**
  * Parse a new hanpshire style tree Caveats: NHX files are NOT supported and the
  * tree distances and topology are unreliable when they are parsed. TODO: on
@@ -78,7 +80,7 @@ import com.stevesoft.pat.Regex;
  */
 public class NewickFile extends FileParse
 {
-  SequenceNode root;
+  private SequenceNode root;
 
   private boolean HasBootstrap = false;
 
@@ -87,21 +89,90 @@ public class NewickFile extends FileParse
   private boolean RootHasDistance = false;
 
   // File IO Flags
-  boolean ReplaceUnderscores = false;
+  private boolean ReplaceUnderscores = false;
 
-  boolean printRootInfo = true;
+  private boolean printRootInfo = true;
 
-  private Regex[] NodeSafeName = new Regex[] {
-      new Regex().perlCode("m/[\\[,:'()]/"), // test for
-      // requiring
-      // quotes
-      new Regex().perlCode("s/'/''/"), // escaping quote
-      // characters
-      new Regex().perlCode("s/\\/w/_/") // unqoted whitespace
-      // transformation
-  };
+  private static final int REGEX_PERL_NODE_REQUIRE_QUOTE = 0;
 
-  char QuoteChar = '\'';
+  private static final int REGEX_PERL_NODE_ESCAPE_QUOTE = 1;
+
+  private static final int REGEX_PERL_NODE_UNQUOTED_WHITESPACE = 2;
+
+  private static final int REGEX_MAJOR_SYMS = 3;
+
+  private static final int REGEX_QNODE_NAME = 4;
+
+  private static final int REGEX_COMMENT = 5;
+
+  private static final int REGEX_UQNODE_NAME = 6;
+
+  private static final int REGEX_NBOOTSTRAP = 7;
+
+  private static final int REGEX_NDIST = 8;
+
+  private static final int REGEX_NO_LINES = 9;
+
+  private static final int REGEX_PERL_EXPAND_QUOTES = 10;
+
+  private static final int REGEX_MAX = 11;
+
+  private static final Regex[] REGEX = new Regex[REGEX_MAX];
+
+  private static Regex getRegex(int id)
+  {
+    if (REGEX[id] == null)
+    {
+      String code = null;
+      String code2 = null;
+      String codePerl = null;
+      switch (id)
+      {
+      case REGEX_PERL_NODE_REQUIRE_QUOTE:
+        codePerl = "m/[\\[,:'()]/";
+        break;
+      case REGEX_PERL_NODE_ESCAPE_QUOTE:
+        codePerl = "s/'/''/";
+        break;
+      case REGEX_PERL_NODE_UNQUOTED_WHITESPACE:
+        codePerl = "s/\\/w/_/";
+        break;
+      case REGEX_PERL_EXPAND_QUOTES:
+        codePerl = "s/''/'/";
+        break;
+      case REGEX_MAJOR_SYMS:
+        code = "[(\\['),;]";
+        break;
+      case REGEX_QNODE_NAME:
+        code = "'([^']|'')+'";
+        break;
+      case REGEX_COMMENT:
+        code = "]";
+        break;
+      case REGEX_UQNODE_NAME:
+        code = "\\b([^' :;\\](),]+)";
+        break;
+      case REGEX_NBOOTSTRAP:
+        code = "\\s*([0-9+]+)\\s*:";
+        break;
+      case REGEX_NDIST:
+        code = ":([-0-9Ee.+]+)";
+        break;
+      case REGEX_NO_LINES:
+        code = "\n+";
+        code2 = "";
+        break;
+      default:
+        return null;
+      }
+      return codePerl == null ? Platform.newRegex(code, code2)
+              : Platform.newRegexPerl(codePerl);
+    }
+    return REGEX[id];
+  }
+
+
+  private char quoteChar = '\'';
 
   /**
    * Creates a new NewickFile object.
@@ -259,6 +330,7 @@ public class NewickFile extends FileParse
    */
   public void parse() throws IOException
   {
+    Platform.ensureRegex();
     String nf;
 
     { // fill nf with complete tree file
@@ -296,7 +368,7 @@ public class NewickFile extends FileParse
     boolean ascending = false; // flag indicating that we are leaving the
     // current node
 
-    Regex majorsyms = new Regex("[(\\['),;]");
+    Regex majorsyms = getRegex(REGEX_MAJOR_SYMS); // "[(\\['),;]"
 
     int nextcp = 0;
     int ncp = cp;
@@ -355,7 +427,7 @@ public class NewickFile extends FileParse
       // Deal with quoted fields
       case '\'':
 
-        Regex qnodename = new Regex("'([^']|'')+'");
+        Regex qnodename = getRegex(REGEX_QNODE_NAME);// "'([^']|'')+'");
 
         if (qnodename.searchFrom(nf, fcp))
         {
@@ -363,7 +435,7 @@ public class NewickFile extends FileParse
           nodename = new String(
                   qnodename.stringMatched().substring(1, nl - 1));
           // unpack any escaped colons
-          Regex xpandquotes = Regex.perlCode("s/''/'/");
+          Regex xpandquotes = getRegex(REGEX_PERL_EXPAND_QUOTES);
           String widernodename = xpandquotes.replaceAll(nodename);
           nodename = widernodename;
           // jump to after end of quoted nodename
@@ -397,7 +469,7 @@ public class NewickFile extends FileParse
            * '"+nf.substring(cp,fcp)+"'"); }
            */
           // verify termination.
-          Regex comment = new Regex("]");
+          Regex comment = getRegex(REGEX_COMMENT); // "]"
           if (comment.searchFrom(nf, fcp))
           {
             // Skip the comment field
@@ -428,9 +500,9 @@ public class NewickFile extends FileParse
                   + fstring.substring(cend + 1);
 
         }
-        Regex uqnodename = new Regex("\\b([^' :;\\](),]+)");
-        Regex nbootstrap = new Regex("\\s*([0-9+]+)\\s*:");
-        Regex ndist = new Regex(":([-0-9Ee.+]+)");
+        Regex uqnodename = getRegex(REGEX_UQNODE_NAME);// "\\b([^' :;\\](),]+)"
+        Regex nbootstrap = getRegex(REGEX_NBOOTSTRAP);// "\\s*([0-9+]+)\\s*:");
+        Regex ndist = getRegex(REGEX_NDIST);// ":([-0-9Ee.+]+)");
 
         if (!parsednodename && uqnodename.search(fstring)
                 && ((uqnodename.matchedFrom(1) == 0) || (fstring
@@ -785,7 +857,7 @@ public class NewickFile extends FileParse
    */
   char getQuoteChar()
   {
-    return QuoteChar;
+    return quoteChar;
   }
 
   /**
@@ -798,8 +870,8 @@ public class NewickFile extends FileParse
    */
   char setQuoteChar(char c)
   {
-    char old = QuoteChar;
-    QuoteChar = c;
+    char old = quoteChar;
+    quoteChar = c;
 
     return old;
   }
@@ -814,13 +886,15 @@ public class NewickFile extends FileParse
    */
   private String nodeName(String name)
   {
-    if (NodeSafeName[0].search(name))
+    if (getRegex(REGEX_PERL_NODE_REQUIRE_QUOTE).search(name))
     {
-      return QuoteChar + NodeSafeName[1].replaceAll(name) + QuoteChar;
+      return quoteChar
+              + getRegex(REGEX_PERL_NODE_ESCAPE_QUOTE).replaceAll(name)
+              + quoteChar;
     }
     else
     {
-      return NodeSafeName[2].replaceAll(name);
+      return getRegex(REGEX_PERL_NODE_UNQUOTED_WHITESPACE).replaceAll(name);
     }
   }
 
@@ -967,7 +1041,7 @@ public class NewickFile extends FileParse
       trf.parse();
       System.out.println("Original file :\n");
 
-      Regex nonl = new Regex("\n+", "");
+      Regex nonl = getRegex(REGEX_NO_LINES);// "\n+", "");
       System.out.println(nonl.replaceAll(newickfile.toString()) + "\n");
 
       System.out.println("Parsed file.\n");
index 4d3ddc1..9551992 100644 (file)
@@ -26,6 +26,7 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
@@ -227,7 +228,7 @@ public class RnamlFile extends AlignFile
       dataName = dataName.substring(0, b - 1);
     }
     b = 0;
-    Regex m = new Regex("[\\/]?([-A-Za-z0-9]+)\\.?");
+    Regex m = Platform.newRegex("[\\/]?([-A-Za-z0-9]+)\\.?");
     String mm = dataName;
     while (m.searchFrom(dataName, b))
     {
index 95bd1cc..d2e8aba 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.io;
 
 import java.util.ArrayList;
+
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
@@ -29,6 +30,7 @@ import java.util.Locale;
 import java.util.Map;
 
 import jalview.api.FeatureColourI;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.GeneLociI;
@@ -59,7 +61,7 @@ public class SequenceAnnotationReport
 
   private static String linkImageURL;
 
-  // public static final String[][] PRIMARY_SOURCES moved to DBRefSource.java
+ // public static final String[][] PRIMARY_SOURCES  moved to DBRefSource.java
 
   /*
    * Comparator to order DBRefEntry by Source + accession id (case-insensitive),
@@ -91,32 +93,32 @@ public class SequenceAnnotationReport
       {
         return 1;
       }
-      int comp = s1 == null ? -1
-              : (s2 == null ? 1 : s1.compareToIgnoreCase(s2));
+      int comp = s1 == null ? -1 : (s2 == null ? 1 : s1
+              .compareToIgnoreCase(s2));
       if (comp == 0)
       {
         String a1 = ref1.getAccessionId();
         String a2 = ref2.getAccessionId();
-        comp = a1 == null ? -1
-                : (a2 == null ? 1 : a1.compareToIgnoreCase(a2));
+        comp = a1 == null ? -1 : (a2 == null ? 1 : a1
+                .compareToIgnoreCase(a2));
       }
       return comp;
     }
 
-    // private boolean isPrimarySource(String source)
-    // {
-    // for (String[] primary : DBRefSource.PRIMARY_SOURCES)
-    // {
-    // for (String s : primary)
-    // {
-    // if (source.equals(s))
-    // {
-    // return true;
-    // }
-    // }
-    // }
-    // return false;
-    // }
+//    private boolean isPrimarySource(String source)
+//    {
+//      for (String[] primary : DBRefSource.PRIMARY_SOURCES)
+//      {
+//        for (String s : primary)
+//        {
+//          if (source.equals(s))
+//          {
+//            return true;
+//          }
+//        }
+//      }
+//      return false;
+//    }
   };
 
   private boolean forTooltip;
@@ -150,9 +152,9 @@ public class SequenceAnnotationReport
    * @param minmax
    * @param maxlength
    */
-  public int appendFeatures(final StringBuilder sb, int residuePos,
-          List<SequenceFeature> features, FeatureRendererModel fr,
-          int maxlength)
+  public int appendFeatures(final StringBuilder sb,
+          int residuePos, List<SequenceFeature> features,
+          FeatureRendererModel fr, int maxlength)
   {
     for (int i = 0; i < features.size(); i++)
     {
@@ -293,7 +295,9 @@ public class SequenceAnnotationReport
         int linkindex = description.toLowerCase(Locale.ROOT).indexOf("<a ");
         boolean hasLink = linkindex > -1
                 && linkindex < MAX_DESCRIPTION_LENGTH;
-        if (description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
+        if (
+                // BH suggestion maxlength == 0 && 
+                description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
         {
           description = description.substring(0, MAX_DESCRIPTION_LENGTH)
                   + ELLIPSIS;
@@ -412,19 +416,21 @@ public class SequenceAnnotationReport
           {
             for (List<String> urllink : createLinksFrom(null, urlstring))
             {
-              sb.append("<br/> <a href=\"" + urllink.get(3) + "\" target=\""
-                      + urllink.get(0) + "\">"
-                      + (urllink.get(0).toLowerCase(Locale.ROOT).equals(
-                              urllink.get(1).toLowerCase(Locale.ROOT))
-                                      ? urllink.get(0)
-                                      : (urllink.get(0) + ":"
-                                              + urllink.get(1)))
-                      + "</a><br/>");
+              sb.append("<br> <a href=\""
+                      + urllink.get(3)
+                      + "\" target=\""
+                      + urllink.get(0)
+                      + "\">"
+                      + (urllink.get(0).toLowerCase(Locale.ROOT)
+                              .equals(urllink.get(1).toLowerCase(Locale.ROOT)) ? urllink
+                              .get(0) : (urllink.get(0) + ":" + urllink
+                                              .get(1)))
+                      + "</a><br>");
             }
           } catch (Exception x)
           {
-            System.err.println(
-                    "problem when creating links from " + urlstring);
+            System.err.println("problem when creating links from "
+                    + urlstring);
             x.printStackTrace();
           }
         }
@@ -500,6 +506,27 @@ public class SequenceAnnotationReport
       ds = ds.getDatasetSequence();
     }
 
+    
+    /*
+     * add any annotation scores
+     */
+    AlignmentAnnotation[] anns = ds.getAnnotation();
+    if (anns!=null && anns.length>0) {
+      boolean first=true;
+      for (int i = 0; anns != null && i < anns.length; i++)
+      {
+        AlignmentAnnotation aa = anns[i];
+        if (aa != null && aa.hasScore() && aa.sequenceRef != null)
+        {
+          if (first) {
+                 sb.append("<br>").append("Annotation Scores<br>");
+                 first=false;
+          }
+          sb.append("<br>").append(aa.label).append(": ")
+                  .append(aa.getScore());
+        }
+      }
+    }
     if (showDbRefs)
     {
       maxWidth = Math.max(maxWidth, appendDbRefs(sb, ds, summary));
@@ -520,6 +547,20 @@ public class SequenceAnnotationReport
         maxWidth = Math.max(maxWidth, sz);
       }
     }
+    if (sequence.getAnnotation("Search Scores") != null)
+    {
+      sb.append("<br>");
+      String eValue = " E-Value: "
+              + sequence.getAnnotation("Search Scores")[0].getEValue();
+      String bitScore = " Bit Score: "
+              + sequence.getAnnotation("Search Scores")[0].getBitScore();
+      sb.append(eValue);
+      sb.append("<br>");
+      sb.append(bitScore);
+      maxWidth = Math.max(maxWidth, eValue.length());
+      maxWidth = Math.max(maxWidth, bitScore.length());
+      sb.append("<br>");
+    }
     sb.append("</i>");
     return maxWidth;
   }
@@ -548,7 +589,6 @@ public class SequenceAnnotationReport
     dbrefs = new ArrayList<DBRefEntry>();
 
     dbrefs.addAll(dbrefset);
-
     // note this sorts the refs held on the sequence!
     dbrefs.sort(comparator);
     boolean ellipsis = false;
@@ -627,8 +667,8 @@ public class SequenceAnnotationReport
           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
           FeatureRendererModel fr)
   {
-    int maxWidth = createSequenceAnnotationReport(tip, sequence, showDbRefs,
-            showNpFeats, fr, true);
+    int maxWidth = createSequenceAnnotationReport(tip, sequence,
+            showDbRefs, showNpFeats, fr, true);
 
     if (maxWidth > 60)
     {
index a3f7531..a4bc631 100644 (file)
  */
 package jalview.io;
 
+import jalview.analysis.Rna;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.Format;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
@@ -40,21 +57,6 @@ import com.stevesoft.pat.Regex;
 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
 import fr.orsay.lri.varna.factories.RNAFactory;
 import fr.orsay.lri.varna.models.rna.RNA;
-import jalview.analysis.Rna;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.Mapping;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.schemes.ResidueProperties;
-import jalview.util.Comparison;
-import jalview.util.DBRefUtils;
-import jalview.util.Format;
-import jalview.util.MessageManager;
 
 /**
  * This class is supposed to parse a Stockholm format file into Jalview There
@@ -76,32 +78,116 @@ public class StockholmFile extends AlignFile
 {
   private static final String ANNOTATION = "annotation";
 
-  // private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
-  //
-  // private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
-
-  public static final Regex DETECT_BRACKETS = new Regex(
-          "(<|>|\\[|\\]|\\(|\\)|\\{|\\})");
+  private static final char UNDERSCORE = '_';
+  
+  // WUSS extended symbols. Avoid ambiguity with protein SS annotations by using NOT_RNASS first.
 
-  // WUSS extended symbols. Avoid ambiguity with protein SS annotations by using
-  // NOT_RNASS first.
   public static final String RNASS_BRACKETS = "<>[](){}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
 
+  public static final int REGEX_STOCKHOLM = 0;
+
+  public static final int REGEX_BRACKETS = 1;
   // use the following regex to decide an annotations (whole) line is NOT an RNA
   // SS (it contains only E,H,e,h and other non-brace/non-alpha chars)
-  private static final Regex NOT_RNASS = new Regex(
-          "^[^<>[\\](){}ADFJ-RUVWYZadfj-ruvwyz]*$");
+  public static final int REGEX_NOT_RNASS = 2;
+
+  private static final int REGEX_ANNOTATION = 3;
+
+  private static final int REGEX_PFAM = 4;
+
+  private static final int REGEX_RFAM = 5;
+
+  private static final int REGEX_ALIGN_END = 6;
+
+  private static final int REGEX_SPLIT_ID = 7;
+
+  private static final int REGEX_SUBTYPE = 8;
+
+  private static final int REGEX_ANNOTATION_LINE = 9;
+
+  private static final int REGEX_REMOVE_ID = 10;
+
+  private static final int REGEX_OPEN_PAREN = 11;
+
+  private static final int REGEX_CLOSE_PAREN = 12;
+
+  public static final int REGEX_MAX = 13;
+
+  private static Regex REGEX[] = new Regex[REGEX_MAX];
+
+  /**
+   * Centralize all actual Regex instantialization in Platform.
+   * // JBPNote: Why is this 'centralisation' better ?
+   * @param id
+   * @return
+   */
+  private static Regex getRegex(int id)
+  {
+    if (REGEX[id] == null)
+    {
+      String pat = null, pat2 = null;
+      switch (id)
+      {
+      case REGEX_STOCKHOLM:
+        pat = "# STOCKHOLM ([\\d\\.]+)";
+        break;
+      case REGEX_BRACKETS:
+        // for reference; not used
+        pat = "(<|>|\\[|\\]|\\(|\\)|\\{|\\})";
+        break;
+      case REGEX_NOT_RNASS:
+        pat = "^[^<>[\\](){}ADFJ-RUVWYZadfj-ruvwyz]*$"; // update 2.11.2
+        break;
+      case REGEX_ANNOTATION:
+        pat = "(\\w+)\\s*(.*)";
+        break;
+      case REGEX_PFAM:
+        pat = "PF[0-9]{5}(.*)";
+        break;
+      case REGEX_RFAM:
+        pat = "RF[0-9]{5}(.*)";
+        break;
+      case REGEX_ALIGN_END:
+        pat = "^\\s*\\/\\/";
+        break;
+      case REGEX_SPLIT_ID:
+        pat = "(\\S+)\\/(\\d+)\\-(\\d+)";
+        break;
+      case REGEX_SUBTYPE:
+        pat = "(\\S+)\\s+(\\S*)\\s+(.*)";
+        break;
+      case REGEX_ANNOTATION_LINE:
+        pat = "#=(G[FSRC]?)\\s+(.*)";
+        break;
+      case REGEX_REMOVE_ID:
+        pat = "(\\S+)\\s+(\\S+)";
+        break;
+      case REGEX_OPEN_PAREN:
+        pat = "(<|\\[)";
+        pat2 = "(";
+        break;
+      case REGEX_CLOSE_PAREN:
+        pat = "(>|\\])";
+        pat2 = ")";
+        break;
+      default:
+        return null;
+      }
+      REGEX[id] = Platform.newRegex(pat, pat2);
+    }
+    return REGEX[id];
+  }
 
   StringBuffer out; // output buffer
 
-  AlignmentI al;
+  private AlignmentI al;
 
   public StockholmFile()
   {
   }
 
   /**
-   * Creates a new StockholmFile object for output.
+   * Creates a new StockholmFile object for output
    */
   public StockholmFile(AlignmentI al)
   {
@@ -215,11 +301,11 @@ public class StockholmFile extends AlignFile
     // First, we have to check that this file has STOCKHOLM format, i.e. the
     // first line must match
 
-    r = new Regex("# STOCKHOLM ([\\d\\.]+)");
+    r = getRegex(REGEX_STOCKHOLM);
     if (!r.search(nextLine()))
     {
       throw new IOException(MessageManager
-                           .getString("exception.stockholm_invalid_format") +" ("+r+")");
+              .getString("exception.stockholm_invalid_format"));
     }
     else
     {
@@ -229,19 +315,22 @@ public class StockholmFile extends AlignFile
     }
 
     // We define some Regexes here that will be used regularily later
-    rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment
-    p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
+    rend = getRegex(REGEX_ALIGN_END);//"^\\s*\\/\\/"); // Find the end of an alignment
+    p = getRegex(REGEX_SPLIT_ID);//"(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
     // id/from/to
-    s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype
-    r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line
-    x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence
+    s = getRegex(REGEX_SUBTYPE);// "(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses
+                                // annotation subtype
+    r = getRegex(REGEX_ANNOTATION_LINE);// "#=(G[FSRC]?)\\s+(.*)"); // Finds any
+                                        // annotation line
+    x = getRegex(REGEX_REMOVE_ID);// "(\\S+)\\s+(\\S+)"); // split id from
+                                  // sequence
 
     // Convert all bracket types to parentheses (necessary for passing to VARNA)
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
+    Regex openparen = getRegex(REGEX_OPEN_PAREN);//"(<|\\[)", "(");
+    Regex closeparen = getRegex(REGEX_CLOSE_PAREN);//"(>|\\])", ")");
 
-    // // Detect if file is RNA by looking for bracket types
-    // Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
+//    // Detect if file is RNA by looking for bracket types
+//    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
 
     rend.optimize();
     p.optimize();
@@ -263,8 +352,8 @@ public class StockholmFile extends AlignFile
         this.noSeqs = seqs.size();
 
         String dbsource = null;
-        Regex pf = new Regex("PF[0-9]{5}(.*)"); // Finds AC for Pfam
-        Regex rf = new Regex("RF[0-9]{5}(.*)"); // Finds AC for Rfam
+        Regex pf = getRegex(REGEX_PFAM); // Finds AC for Pfam
+        Regex rf = getRegex(REGEX_RFAM); // Finds AC for Rfam
         if (getAlignmentProperty("AC") != null)
         {
           String dbType = getAlignmentProperty("AC").toString();
@@ -333,15 +422,14 @@ public class StockholmFile extends AlignFile
 
           if (accAnnotations != null && accAnnotations.containsKey("AC"))
           {
-            String dbr = (String) accAnnotations.get("AC");
-            if (dbr != null)
-            {
-              // we could get very clever here - but for now - just try to
-              // guess accession type from type of sequence, source of alignment
-              // plus
+              String dbr = (String) accAnnotations.get("AC");
+              if (dbr != null)
+              {
+                // we could get very clever here - but for now - just try to
+              // guess accession type from type of sequence, source of alignment plus
               // structure
-              // of accession
-              guessDatabaseFor(seqO, dbr, dbsource);
+                // of accession
+                guessDatabaseFor(seqO, dbr, dbsource);
             }
             // else - do what ? add the data anyway and prompt the user to
             // specify what references these are ?
@@ -506,7 +594,7 @@ public class StockholmFile extends AlignFile
            */
           // Let's save the annotations, maybe we'll be able to do something
           // with them later...
-          Regex an = new Regex("(\\w+)\\s*(.*)");
+          Regex an = getRegex(REGEX_ANNOTATION);
           if (an.search(annContent))
           {
             if (an.stringMatched(1).equals("NH"))
@@ -526,10 +614,8 @@ public class StockholmFile extends AlignFile
               treeName = an.stringMatched(2);
               treeString = new StringBuffer();
             }
-            // TODO: JAL-3532 - this is where GF comments and database
-            // references are lost
-            // suggest overriding this method for Stockholm files to catch and
-            // properly
+            // TODO: JAL-3532 - this is where GF comments and database references are lost
+            // suggest overriding this method for Stockholm files to catch and properly
             // process CC, DR etc into multivalued properties
             setAlignmentProperty(an.stringMatched(1), an.stringMatched(2));
           }
@@ -639,14 +725,15 @@ public class StockholmFile extends AlignFile
             if (features.containsKey(this.id2type(type)))
             {
               // logger.debug("Found content for " + this.id2type(type));
-              content = (Hashtable) features.get(this.id2type(type));
+              content = (Hashtable) features
+                      .get(this.id2type(type));
             }
             else
             {
               // logger.debug("Creating new content holder for " +
               // this.id2type(type));
               content = new Hashtable();
-              features.put(this.id2type(type), content);
+              features.put(id2type(type), content);
             }
             String ns = (String) content.get(ANNOTATION);
 
@@ -761,8 +848,7 @@ public class StockholmFile extends AlignFile
     }
     if (dbsource == null)
     {
-      // make up an origin based on whether the sequence looks like it is
-      // nucleotide
+      // make up an origin based on whether the sequence looks like it is nucleotide
       // or protein
       dbsource = (seqO.isProtein()) ? "PFAM" : "RFAM";
     }
@@ -828,10 +914,9 @@ public class StockholmFile extends AlignFile
           Vector<AlignmentAnnotation> annotation, String label,
           String annots)
   {
-    String convert1, convert2 = null;
-
-    // convert1 = OPEN_PAREN.replaceAll(annots);
-    // convert2 = CLOSE_PAREN.replaceAll(convert1);
+         String convert1, convert2 = null;
+    // String convert1 = OPEN_PAREN.replaceAll(annots);
+    // String convert2 = CLOSE_PAREN.replaceAll(convert1);
     // annots = convert2;
 
     String type = label;
@@ -848,7 +933,8 @@ public class StockholmFile extends AlignFile
     if (type.equalsIgnoreCase("secondary structure"))
     {
       ss = true;
-      isrnass = !NOT_RNASS.search(annots); // sorry about the double negative
+      isrnass = !getRegex(REGEX_NOT_RNASS).search(annots); // sorry about the double
+                                                     // negative
                                            // here (it's easier for dealing with
                                            // other non-alpha-non-brace chars)
     }
@@ -861,6 +947,11 @@ public class StockholmFile extends AlignFile
     for (int i = 0; i < annots.length(); i++)
     {
       String pos = annots.substring(i, i + 1);
+      // TODO 2.12 release: verify this Stockholm IO behaviour change in release notes
+      if (UNDERSCORE == pos.charAt(0))
+      {
+        pos = " ";
+      }
       Annotation ann;
       ann = new Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not
       // be written out
@@ -946,7 +1037,6 @@ public class StockholmFile extends AlignFile
     return ref.getSource().toString() + " ; "
             + ref.getAccessionId().toString();
   }
-
   @Override
   public String print(SequenceI[] s, boolean jvSuffix)
   {
@@ -986,17 +1076,12 @@ public class StockholmFile extends AlignFile
         }
         else
         {
-          for (int idb = 0; idb < seq.getDBRefs().size(); idb++)
+          for (int idb = 0; idb < ndb; idb++)
           {
-            DBRefEntry dbref = seq.getDBRefs().get(idb);
+            DBRefEntry dbref = seqrefs.get(idb);
             dataRef.put(tmp, dbref_to_ac_record(dbref));
             // if we put in a uniprot or EMBL record then we're done:
-            if (isAA && DBRefSource.UNIPROT
-                    .equals(DBRefUtils.getCanonicalName(dbref.getSource())))
-            {
-              break;
-            }
-            if (!isAA && DBRefSource.EMBL
+            if ((isAA ? DBRefSource.UNIPROT : DBRefSource.EMBL)
                     .equals(DBRefUtils.getCanonicalName(dbref.getSource())))
             {
               break;
@@ -1055,35 +1140,37 @@ public class StockholmFile extends AlignFile
       if (alAnot != null)
       {
         Annotation[] ann;
-        for (int j = 0, nj = alAnot.length; j < nj; j++)
+        for (int j = 0; j < alAnot.length; j++)
         {
 
-          String key = type2id(alAnot[j].label);
-          boolean isrna = alAnot[j].isValidStruc();
-
-          if (isrna)
-          {
-            // hardwire to secondary structure if there is RNA secondary
-            // structure on the annotation
-            key = "SS";
-          }
-          if (key == null)
+          if (alAnot[j].annotations != null)
           {
+            String key = type2id(alAnot[j].label);
+            boolean isrna = alAnot[j].isValidStruc();
 
-            continue;
-          }
+            if (isrna)
+            {
+              // hardwire to secondary structure if there is RNA secondary
+              // structure on the annotation
+              key = "SS";
+            }
+            if (key == null)
+            {
+              continue;
+            }
 
-          // out.append("#=GR ");
-          out.append(new Format("%-" + maxid + "s").form(
-                  "#=GR " + printId(seq, jvSuffix) + " " + key + " "));
-          ann = alAnot[j].annotations;
-          String sseq = "";
-          for (int k = 0, nk = ann.length; k < nk; k++)
-          {
-            sseq += outputCharacter(key, k, isrna, ann, seq);
-          }
-          out.append(sseq);
-          out.append(newline);
+            // out.append("#=GR ");
+            out.append(new Format("%-" + maxid + "s").form(
+                    "#=GR " + printId(s[i], jvSuffix) + " " + key + " "));
+            ann = alAnot[j].annotations;
+            String sseq = "";
+            for (int k = 0; k < ann.length; k++)
+            {
+              sseq += outputCharacter(key, k, isrna, ann, s[i]);
+            }
+            out.append(sseq);
+            out.append(newline);
+         }
         }
       }
 
@@ -1216,6 +1303,26 @@ public class StockholmFile extends AlignFile
             : seq;
   }
 
+  /**
+   * make a friendly ID string.
+   * 
+   * @param dataName
+   * @return truncated dataName to after last '/'
+   */
+  private String safeName(String dataName)
+  {
+    int b = 0;
+    while ((b = dataName.indexOf("/")) > -1 && b < dataName.length())
+    {
+      dataName = dataName.substring(b + 1).trim();
+
+    }
+    int e = (dataName.length() - dataName.indexOf(".")) + 1;
+    dataName = dataName.substring(1, e).trim();
+    return dataName;
+  }
+  
+  
   public String print()
   {
     out = new StringBuffer();
@@ -1254,6 +1361,7 @@ public class StockholmFile extends AlignFile
     }
   }
 
+  
   protected static String id2type(String id)
   {
     if (typeIds.containsKey(id))
@@ -1286,23 +1394,4 @@ public class StockholmFile extends AlignFile
             "Warning : Unknown Stockholm annotation type: " + type);
     return key;
   }
-
-  /**
-   * make a friendly ID string.
-   * 
-   * @param dataName
-   * @return truncated dataName to after last '/'
-   */
-  private String safeName(String dataName)
-  {
-    int b = 0;
-    while ((b = dataName.indexOf("/")) > -1 && b < dataName.length())
-    {
-      dataName = dataName.substring(b + 1).trim();
-
-    }
-    int e = (dataName.length() - dataName.indexOf(".")) + 1;
-    dataName = dataName.substring(1, e).trim();
-    return dataName;
-  }
 }
index dd4b72c..2a05ffd 100644 (file)
@@ -35,8 +35,10 @@ import jalview.io.vamsas.DatastoreRegistry;
 import jalview.io.vamsas.Rangetype;
 import jalview.project.Jalview2XML;
 import jalview.util.MessageManager;
+import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -718,9 +720,9 @@ public class VamsasAppDatastore
       // /SAVE THE TREES
       // /////////////////////////////////
       // FIND ANY ASSOCIATED TREES
-      if (Desktop.desktop != null)
+      if (Desktop.getDesktopPane() != null)
       {
-        javax.swing.JInternalFrame[] frames = Desktop.instance
+        javax.swing.JInternalFrame[] frames = Desktop.getInstance()
                 .getAllFrames();
 
         for (int t = 0; t < frames.length; t++)
@@ -1318,7 +1320,7 @@ public class VamsasAppDatastore
             // and
             // mapValuesToString
             fromxml.setSkipList(skipList);
-            jalview.util.jarInputStreamProvider jprovider = new jalview.util.jarInputStreamProvider()
+            jarInputStreamProvider jprovider = new jarInputStreamProvider()
             {
 
               @Override
@@ -1336,6 +1338,12 @@ public class VamsasAppDatastore
                         "Returning client input stream for Jalview from Vamsas Document.");
                 return new JarInputStream(cappdata.getClientInputStream());
               }
+
+              @Override
+              public File getFile()
+              {
+                return null;
+              }
             };
             if (dojvsync)
             {
@@ -1366,7 +1374,7 @@ public class VamsasAppDatastore
           fromxml.setSkipList(skipList);
           fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
                   mapValuesToString(jv2vobj));
-          jalview.util.jarInputStreamProvider jarstream = new jalview.util.jarInputStreamProvider()
+          jarInputStreamProvider jarstream = new jarInputStreamProvider()
           {
 
             @Override
@@ -1384,6 +1392,12 @@ public class VamsasAppDatastore
                       "Returning user input stream for Jalview from Vamsas Document.");
               return new JarInputStream(cappdata.getUserInputStream());
             }
+
+            @Override
+            public File getFile()
+            {
+              return null;
+            }
           };
           if (dojvsync)
           {
@@ -1476,7 +1490,7 @@ public class VamsasAppDatastore
           if (mappings != null)
           {
             jalview.structure.StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance)
+                    .getStructureSelectionManager(Desktop.getInstance())
                     .registerMappings(mappings);
           }
         }
index 63b78b2..e24c2f3 100755 (executable)
@@ -150,7 +150,7 @@ public class WSWUBlastClient
   {
     // This must be outside the run() body as java 1.5
     // will not return any value from the OptionPane to the expired thread.
-    int reply = JvOptionPane.showConfirmDialog(Desktop.desktop,
+    int reply = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
             "Automatically update suggested ids?",
             "Auto replace sequence ids", JvOptionPane.YES_NO_OPTION);
 
index eaf6ecd..fdcad08 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io.cache;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 
 import java.util.Hashtable;
@@ -31,22 +33,27 @@ import java.util.LinkedHashSet;
  * @author tcnofoegbu
  *
  */
-public class AppCache
+public class AppCache implements ApplicationSingletonI
 {
+
+  public static AppCache getInstance()
+  {
+    return (AppCache) ApplicationSingletonProvider.getInstance(AppCache.class);
+  }
+
+  private AppCache()
+  {
+    cacheItems = new Hashtable<String, LinkedHashSet<String>>();
+  }
+
   public static final String DEFAULT_LIMIT = "99";
 
   public static final String CACHE_DELIMITER = ";";
 
-  private static AppCache instance = null;
-
   private static final String DEFAULT_LIMIT_KEY = ".DEFAULT_LIMIT";
 
   private Hashtable<String, LinkedHashSet<String>> cacheItems;
 
-  private AppCache()
-  {
-    cacheItems = new Hashtable<String, LinkedHashSet<String>>();
-  }
 
   /**
    * Method to obtain all the cache items for a given cache key
@@ -66,20 +73,6 @@ public class AppCache
   }
 
   /**
-   * Returns a singleton instance of AppCache
-   * 
-   * @return
-   */
-  public static AppCache getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new AppCache();
-    }
-    return instance;
-  }
-
-  /**
    * Method for persisting cache items for a given cache key
    * 
    * @param cacheKey
index 1ef8848..e422ed4 100644 (file)
@@ -91,7 +91,7 @@ public class Gff3Helper extends GffHelperBase
       String atts = gff[ATTRIBUTES_COL];
       Map<String, List<String>> attributes = parseNameValuePairs(atts);
 
-      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
       if (so.isA(soTerm, SequenceOntologyI.PROTEIN_MATCH))
       {
         sf = processProteinMatch(attributes, seq, gff, align, newseqs,
@@ -385,7 +385,7 @@ public class Gff3Helper extends GffHelperBase
       desc = target.split(" ")[0];
     }
 
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = sf.getType();
     if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT))
     {
index 91f0018..2fda728 100644 (file)
@@ -109,7 +109,7 @@ public class InterProScanHelper extends Gff3Helper
    */
   public static boolean recognises(String[] columns)
   {
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = columns[TYPE_COL];
     if (so.isA(type, SequenceOntologyI.PROTEIN_MATCH)
             || (".".equals(columns[SOURCE_COL])
index 90cae7a..08a2c6a 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.io.gff;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A factory class that returns a model of the Sequence Ontology. By default a
  * hard-coded subset is used (for the applet, or testing), or setInstance() can
@@ -28,21 +31,53 @@ package jalview.io.gff;
  * @author gmcarstairs
  *
  */
-public class SequenceOntologyFactory
+public class SequenceOntologyFactory implements ApplicationSingletonI
 {
-  private static SequenceOntologyI instance;
+  /**
+   * Answers an instance of this class for the current application context. Note
+   * that this supports running two JS 'applets' on the same page, one with the
+   * full Sequence Ontology (USE_FULL_SO = true) and one with a hard-coded
+   * subset (USE_FULL_SO = false). If this is overkill, could change this method
+   * to just return a common static instance.
+   * 
+   * @return
+   */
+  private static synchronized SequenceOntologyFactory getInstance()
+  {
+    return (SequenceOntologyFactory) ApplicationSingletonProvider
+            .getInstance(SequenceOntologyFactory.class);
+  }
+
+  private SequenceOntologyFactory()
+  {
+    // private singleton
+  }
+
 
-  public static synchronized SequenceOntologyI getInstance()
+  /**
+   * Answers the configured model of the Sequence Ontology.
+   * 
+   * @return
+   */
+  public static synchronized SequenceOntologyI getSequenceOntology()
   {
-    if (instance == null)
-    {
-      instance = new SequenceOntologyLite();
-    }
-    return instance;
+    SequenceOntologyFactory f = getInstance();
+    return (f.sequenceOntology == null
+            ? f.sequenceOntology = new SequenceOntologyLite()
+            : f.sequenceOntology);
   }
 
-  public static void setInstance(SequenceOntologyI so)
+  /**
+   * For testng only
+   * 
+   * @param so
+   */
+  public static void setSequenceOntology(SequenceOntologyI so)
   {
-    instance = so;
+    getInstance().sequenceOntology = so;
   }
+
+
+  private SequenceOntologyI sequenceOntology;
+
 }
index 9f84c16..d4047d1 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.io.packed;
 
+import jalview.analysis.SeqsetUtils.SequenceInfo;
 import jalview.analysis.TreeModel;
 import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentI;
@@ -76,7 +77,7 @@ public class JalviewDataset
   /**
    * @return the seqDetails
    */
-  public Hashtable getSeqDetails()
+  public Map<String, SequenceInfo> getSeqDetails()
   {
     return seqDetails;
   }
@@ -193,7 +194,7 @@ public class JalviewDataset
   /**
    * original identity of each sequence in results
    */
-  Hashtable seqDetails;
+  Map<String, SequenceInfo> seqDetails;
 
   public boolean relaxedIdMatching = false;
 
@@ -211,7 +212,7 @@ public class JalviewDataset
    * @param parentAlignment
    */
   public JalviewDataset(AlignmentI aldataset,
-          Map<String, FeatureColourI> fc, Hashtable seqDets)
+          Map<String, FeatureColourI> fc, Map<String, SequenceInfo> seqDets)
   {
     // TODO not used - remove?
     this(aldataset, fc, seqDets, null);
@@ -233,7 +234,7 @@ public class JalviewDataset
    *          with.
    */
   public JalviewDataset(AlignmentI aldataset,
-          Map<String, FeatureColourI> fc, Hashtable seqDets,
+          Map<String, FeatureColourI> fc, Map<String, SequenceInfo> seqDets,
           AlignmentI parentAlignment)
   {
     this();
index 37dd66b..1dc7638 100644 (file)
  */
 package jalview.io.packed;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 
 import jalview.api.FeatureColourI;
@@ -31,11 +36,6 @@ import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 import jalview.io.packed.DataProvider.JvDataType;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 
 public class ParsePackedSet
 {
index a33390f..6a5ec3e 100644 (file)
@@ -363,7 +363,7 @@ public class Sequencemapping extends Rangetype
     }
     bindjvvobj(mapping, sequenceMapping);
     jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance)
+            .getStructureSelectionManager(Desktop.getInstance())
             .registerMapping(acf);
     // Try to link up any conjugate database references in the two sequences
     // matchConjugateDBRefs(from, to, mapping);
index 71b6b9e..0fa4379 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.analysis.GeneticCodeI;
-import jalview.analysis.GeneticCodes;
-import jalview.api.SplitContainerI;
-import jalview.bin.Cache;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.Preferences;
-import jalview.io.FileFormats;
-import jalview.schemes.ResidueColourScheme;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -39,9 +28,11 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusAdapter;
 import java.awt.event.FocusEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -61,6 +52,19 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.analysis.GeneticCodeI;
+import jalview.analysis.GeneticCodes;
+import jalview.api.SplitContainerI;
+import jalview.bin.Cache;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.Preferences;
+import jalview.hmmer.HmmerCommand;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormats;
+import jalview.schemes.ResidueColourScheme;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
 @SuppressWarnings("serial")
 public class GAlignFrame extends JInternalFrame
 {
@@ -70,9 +74,11 @@ public class GAlignFrame extends JInternalFrame
 
   public JMenu webService = new JMenu();// BH 2019 was protected, but not
                                         // sufficient for AlignFrame thread run
+    // JBP - followed suite for these other service related GUI elements.
+    // TODO: check we really need these to be public
+  public JMenu hmmerMenu = new JMenu();
 
-  public JMenuItem webServiceNoServices;// BH 2019 was protected, but not
-                                        // sufficient for AlignFrame thread run
+  public JMenuItem webServiceNoServices;
 
   protected JCheckBoxMenuItem viewBoxesMenuItem = new JCheckBoxMenuItem();
 
@@ -202,6 +208,11 @@ public class GAlignFrame extends JInternalFrame
 
   protected JCheckBoxMenuItem normaliseSequenceLogo = new JCheckBoxMenuItem();
 
+  protected JCheckBoxMenuItem showInformationHistogram = new JCheckBoxMenuItem();
+
+  protected JCheckBoxMenuItem showHMMSequenceLogo = new JCheckBoxMenuItem();
+
+  protected JCheckBoxMenuItem normaliseHMMSequenceLogo = new JCheckBoxMenuItem();
   protected JCheckBoxMenuItem applyAutoAnnotationSettings = new JCheckBoxMenuItem();
 
   protected JMenuItem openFeatureSettings;
@@ -220,7 +231,7 @@ public class GAlignFrame extends JInternalFrame
     {
 
       // for Web-page embedding using id=align-frame-div
-      setName("jalview-alignment");
+      setName(Platform.getAppID("alignment"));
 
       jbInit();
       setJMenuBar(alignFrameMenuBar);
@@ -263,6 +274,7 @@ public class GAlignFrame extends JInternalFrame
   {
     initColourMenu();
 
+  
     JMenuItem saveAs = new JMenuItem(
             MessageManager.getString("action.save_as"));
     ActionListener al = new ActionListener()
@@ -274,18 +286,15 @@ public class GAlignFrame extends JInternalFrame
       }
     };
 
+  
     // FIXME getDefaultToolkit throws an exception in Headless mode
-    KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+    KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Platform.SHORTCUT_KEY_MASK | InputEvent.SHIFT_DOWN_MASK,
             false);
     addMenuActionAndAccelerator(keyStroke, saveAs, al);
 
+  
     closeMenuItem.setText(MessageManager.getString("action.close"));
-    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W, Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -296,6 +305,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, closeMenuItem, al);
 
+  
     JMenu editMenu = new JMenu(MessageManager.getString("action.edit"));
     JMenu viewMenu = new JMenu(MessageManager.getString("action.view"));
     JMenu annotationsMenu = new JMenu(
@@ -305,12 +315,11 @@ public class GAlignFrame extends JInternalFrame
     JMenu calculateMenu = new JMenu(
             MessageManager.getString("action.calculate"));
     webService.setText(MessageManager.getString("action.web_service"));
+    initHMMERMenu();
     JMenuItem selectAllSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.select_all"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -321,6 +330,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, selectAllSequenceMenuItem, al);
 
+  
     JMenuItem deselectAllSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.deselect_all"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
@@ -334,12 +344,11 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, deselectAllSequenceMenuItem, al);
 
+  
     JMenuItem invertSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.invert_sequence_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -350,6 +359,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, invertSequenceMenuItem, al);
 
+  
     JMenuItem grpsFromSelection = new JMenuItem(
             MessageManager.getString("action.make_groups_selection"));
     grpsFromSelection.addActionListener(new ActionListener()
@@ -375,9 +385,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem remove2LeftMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_left"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -388,12 +396,11 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, remove2LeftMenuItem, al);
 
+  
     JMenuItem remove2RightMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_right"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -404,12 +411,11 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, remove2RightMenuItem, al);
 
+  
     JMenuItem removeGappedColumnMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_empty_columns"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -420,11 +426,12 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, removeGappedColumnMenuItem, al);
 
+  
     JMenuItem removeAllGapsMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_all_gaps"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -436,6 +443,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, removeAllGapsMenuItem, al);
 
+  
     JMenuItem justifyLeftMenuItem = new JMenuItem(
             MessageManager.getString("action.left_justify_alignment"));
     justifyLeftMenuItem.addActionListener(new ActionListener()
@@ -528,12 +536,31 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+    JMenuItem sortEValueMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_evalue"));
+    sortEValueMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sortEValueMenuItem_actionPerformed(e);
+      }
+    });
+    JMenuItem sortBitScoreMenuItem = new JMenuItem(
+            MessageManager.getString("action.by_bit_score"));
+    sortBitScoreMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        sortBitScoreMenuItem_actionPerformed(e);
+      }
+    });
+  
     JMenuItem removeRedundancyMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_redundancy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -544,6 +571,30 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, removeRedundancyMenuItem, al);
 
+    JMenuItem filterByEValue = new JMenuItem(
+            MessageManager.getString("action.filter_by_evalue"));
+    filterByEValue.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterByEValue_actionPerformed();
+      }
+
+    });
+
+    JMenuItem filterByScore = new JMenuItem(
+            MessageManager.getString("action.filter_by_score"));
+    filterByScore.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        filterByScore_actionPerformed();
+      }
+
+    });
+  
     JMenuItem pairwiseAlignmentMenuItem = new JMenuItem(
             MessageManager.getString("action.pairwise_alignment"));
     pairwiseAlignmentMenuItem.addActionListener(new ActionListener()
@@ -555,6 +606,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     this.getContentPane().setLayout(new BorderLayout());
     alignFrameMenuBar.setFont(new java.awt.Font("Verdana", 0, 11));
     statusBar.setBackground(Color.white);
@@ -672,6 +724,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem htmlMenuItem = new JMenuItem(
             MessageManager.getString("label.html"));
     htmlMenuItem.addActionListener(new ActionListener()
@@ -683,6 +736,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem createBioJS = new JMenuItem(
             MessageManager.getString("label.biojs_html_export"));
     createBioJS.addActionListener(new java.awt.event.ActionListener()
@@ -694,6 +748,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem overviewMenuItem = new JMenuItem(
             MessageManager.getString("label.overview_window"));
     overviewMenuItem.addActionListener(new ActionListener()
@@ -705,12 +760,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     undoMenuItem.setEnabled(false);
     undoMenuItem.setText(MessageManager.getString("action.undo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -721,12 +775,11 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, undoMenuItem, al);
 
+  
     redoMenuItem.setEnabled(false);
     redoMenuItem.setText(MessageManager.getString("action.redo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -737,6 +790,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, redoMenuItem, al);
 
+  
     wrapMenuItem.setText(MessageManager.getString("label.wrap"));
     wrapMenuItem.addActionListener(new ActionListener()
     {
@@ -747,12 +801,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem printMenuItem = new JMenuItem(
             MessageManager.getString("action.print"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -763,6 +816,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, printMenuItem, al);
 
+  
     renderGapsMenuItem
             .setText(MessageManager.getString("action.show_gaps"));
     renderGapsMenuItem.setState(true);
@@ -775,12 +829,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem findMenuItem = new JMenuItem(
             MessageManager.getString("action.find"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.find_tip")));
     al = new ActionListener()
@@ -814,36 +867,42 @@ public class GAlignFrame extends JInternalFrame
     showDbRefsMenuitem.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showDbRefs_actionPerformed(e);
       }
 
+  
     });
     showNpFeatsMenuitem.setText(
             MessageManager.getString("label.show_non_positional_features"));
     showNpFeatsMenuitem.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showNpFeats_actionPerformed(e);
       }
 
+  
     });
     showGroupConservation
             .setText(MessageManager.getString("label.group_conservation"));
     showGroupConservation.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showGroupConservation_actionPerformed(e);
       }
 
+  
     });
 
     showGroupConsensus
@@ -851,48 +910,56 @@ public class GAlignFrame extends JInternalFrame
     showGroupConsensus.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showGroupConsensus_actionPerformed(e);
       }
 
+  
     });
     showConsensusHistogram.setText(
             MessageManager.getString("label.show_consensus_histogram"));
     showConsensusHistogram.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showConsensusHistogram_actionPerformed(e);
       }
 
+  
     });
     showSequenceLogo
             .setText(MessageManager.getString("label.show_consensus_logo"));
     showSequenceLogo.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         showSequenceLogo_actionPerformed(e);
       }
 
+  
     });
     normaliseSequenceLogo
             .setText(MessageManager.getString("label.norm_consensus_logo"));
     normaliseSequenceLogo.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         normaliseSequenceLogo_actionPerformed(e);
       }
 
+  
     });
     applyAutoAnnotationSettings
             .setText(MessageManager.getString("label.apply_all_groups"));
@@ -907,6 +974,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     ButtonGroup buttonGroup = new ButtonGroup();
     final JRadioButtonMenuItem showAutoFirst = new JRadioButtonMenuItem(
             MessageManager.getString("label.show_first"));
@@ -938,12 +1006,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem deleteGroups = new JMenuItem(
             MessageManager.getString("action.undefine_groups"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -954,6 +1021,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, deleteGroups, al);
 
+  
     JMenuItem annotationColumn = new JMenuItem(
             MessageManager.getString("action.select_by_annotation"));
     annotationColumn.addActionListener(new ActionListener()
@@ -965,12 +1033,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem createGroup = new JMenuItem(
             MessageManager.getString("action.create_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -981,11 +1048,12 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, createGroup, al);
 
+  
     JMenuItem unGroup = new JMenuItem(
             MessageManager.getString("action.remove_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -997,11 +1065,10 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, unGroup, al);
 
+  
     copy.setText(MessageManager.getString("action.copy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
 
     al = new ActionListener()
     {
@@ -1013,11 +1080,10 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, copy, al);
 
+  
     cut.setText(MessageManager.getString("action.cut"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1028,6 +1094,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, cut, al);
 
+  
     JMenuItem delete = new JMenuItem(
             MessageManager.getString("action.delete"));
     delete.addActionListener(new ActionListener()
@@ -1039,39 +1106,54 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     pasteMenu.setText(MessageManager.getString("action.paste"));
     JMenuItem pasteNew = new JMenuItem(
             MessageManager.getString("label.to_new_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        pasteNew_actionPerformed(e);
+        try
+        {
+          pasteNew_actionPerformed(e);
+        } catch (IOException | InterruptedException e1)
+        {
+          // TODO Auto-generated catch block
+          e1.printStackTrace();
+        }
       }
     };
     addMenuActionAndAccelerator(keyStroke, pasteNew, al);
 
+  
     JMenuItem pasteThis = new JMenuItem(
             MessageManager.getString("label.to_this_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        pasteThis_actionPerformed(e);
+        try
+        {
+          pasteThis_actionPerformed(e);
+        } catch (IOException | InterruptedException e1)
+        {
+          // TODO Auto-generated catch block
+          e1.printStackTrace();
+        }
       }
     };
     addMenuActionAndAccelerator(keyStroke, pasteThis, al);
 
+  
     JMenuItem createPNG = new JMenuItem("PNG");
     createPNG.addActionListener(new ActionListener()
     {
@@ -1114,6 +1196,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem createSVG = new JMenuItem("SVG");
     createSVG.addActionListener(new ActionListener()
     {
@@ -1124,6 +1207,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem loadTreeMenuItem = new JMenuItem(
             MessageManager.getString("label.load_associated_tree"));
     loadTreeMenuItem.setActionCommand(
@@ -1137,6 +1221,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     scaleAbove.setVisible(false);
     scaleAbove.setText(MessageManager.getString("action.scale_above"));
     scaleAbove.addActionListener(new ActionListener()
@@ -1188,14 +1273,17 @@ public class GAlignFrame extends JInternalFrame
     followHighlightMenuItem.addActionListener(new ActionListener()
     {
 
+  
       @Override
       public void actionPerformed(ActionEvent e)
       {
         followHighlight_actionPerformed();
       }
 
+  
     });
 
+  
     sortByTreeMenu
             .setText(MessageManager.getString("action.by_tree_order"));
     sort.setText(MessageManager.getString("action.sort"));
@@ -1204,7 +1292,7 @@ public class GAlignFrame extends JInternalFrame
       @Override
       public void menuSelected(MenuEvent e)
       {
-        buildTreeSortMenu();
+        enableSortMenuOptions();
       }
 
       @Override
@@ -1217,22 +1305,43 @@ public class GAlignFrame extends JInternalFrame
       {
       }
     });
+    sortByTreeMenu.addMenuListener(new MenuListener()
+    {
+      @Override
+      public void menuSelected(MenuEvent e)
+      {
+        buildTreeSortMenu();
+      }
+  
+      @Override
+      public void menuDeselected(MenuEvent e)
+      {
+      }
+  
+      @Override
+      public void menuCanceled(MenuEvent e)
+      {
+      }
+    });
     sortByAnnotScore
             .setText(MessageManager.getString("label.sort_by_score"));
     sort.add(sortByAnnotScore);
     sort.addMenuListener(new javax.swing.event.MenuListener()
     {
 
+  
       @Override
       public void menuCanceled(MenuEvent e)
       {
       }
 
+  
       @Override
       public void menuDeselected(MenuEvent e)
       {
       }
 
+  
       @Override
       public void menuSelected(MenuEvent e)
       {
@@ -1312,6 +1421,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem extractScores = new JMenuItem(
             MessageManager.getString("label.extract_scores"));
     extractScores.addActionListener(new ActionListener()
@@ -1325,9 +1435,11 @@ public class GAlignFrame extends JInternalFrame
     extractScores.setVisible(true);
     // JBPNote: TODO: make gui for regex based score extraction
 
+  
     // for show products actions see AlignFrame.canShowProducts
     showProducts.setText(MessageManager.getString("label.get_cross_refs"));
 
+  
     runGroovy.setText(MessageManager.getString("label.run_groovy"));
     runGroovy.setToolTipText(
             MessageManager.getString("label.run_groovy_tip"));
@@ -1365,6 +1477,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem associatedData = new JMenuItem(
             MessageManager.getString("label.load_features_annotations"));
     associatedData.addActionListener(new ActionListener()
@@ -1372,7 +1485,14 @@ public class GAlignFrame extends JInternalFrame
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        associatedData_actionPerformed(e);
+        try
+        {
+          associatedData_actionPerformed(e);
+        } catch (IOException | InterruptedException e1)
+        {
+          // TODO Auto-generated catch block
+          e1.printStackTrace();
+        }
       }
     });
     loadVcf = new JMenuItem(
@@ -1426,6 +1546,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenu addSequenceMenu = new JMenu(
             MessageManager.getString("label.add_sequences"));
     JMenuItem addFromFile = new JMenuItem(
@@ -1572,11 +1693,12 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem invertColSel = new JMenuItem(
             MessageManager.getString("action.invert_column_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.ALT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.ALT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1588,6 +1710,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, invertColSel, al);
 
+  
     showComplementMenuItem.setVisible(false);
     showComplementMenuItem.addActionListener(new ActionListener()
     {
@@ -1598,6 +1721,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     tabbedPane.addChangeListener(new javax.swing.event.ChangeListener()
     {
       @Override
@@ -1619,6 +1743,7 @@ public class GAlignFrame extends JInternalFrame
         }
       }
 
+  
       @Override
       public void mouseReleased(MouseEvent e)
       {
@@ -1637,11 +1762,10 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem save = new JMenuItem(MessageManager.getString("action.save"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1652,6 +1776,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, save, al);
 
+  
     reload.setEnabled(false);
     reload.setText(MessageManager.getString("action.reload"));
     reload.addActionListener(new ActionListener()
@@ -1663,12 +1788,11 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     JMenuItem newView = new JMenuItem(
             MessageManager.getString("action.new_view"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
-            false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1679,10 +1803,12 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, newView, al);
 
+  
     tabbedPane.setToolTipText("<html><i>"
             + MessageManager.getString("label.rename_tab_eXpand_reGroup")
             + "</i></html>");
 
+  
     formatMenu.setText(MessageManager.getString("action.format"));
     JMenu selectMenu = new JMenu(MessageManager.getString("action.select"));
 
@@ -1697,6 +1823,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
 
+  
     gatherViews.setEnabled(false);
     gatherViews.setText(MessageManager.getString("action.gather_views"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, 0, false);
@@ -1710,6 +1837,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, gatherViews, al);
 
+  
     expandViews.setEnabled(false);
     expandViews.setText(MessageManager.getString("action.expand_views"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
@@ -1723,6 +1851,7 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, expandViews, al);
 
+  
     JMenuItem pageSetup = new JMenuItem(
             MessageManager.getString("action.page_setup"));
     pageSetup.addActionListener(new ActionListener()
@@ -1745,6 +1874,18 @@ public class GAlignFrame extends JInternalFrame
     });
     JMenuItem selectHighlighted = new JMenuItem(
             MessageManager.getString("action.select_highlighted_columns"));
+    selectHighlighted.setToolTipText(JvSwingUtils.wrapTooltip(true, 
+            MessageManager.getString("tooltip.select_highlighted_columns")));
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        selectHighlightedColumns_actionPerformed(actionEvent);
+      }
+    };
+    JMenuItem Filter = new JMenuItem(
+            MessageManager.getString("action.select_highlighted_columns"));
     selectHighlighted.setToolTipText(
             MessageManager.getString("tooltip.select_highlighted_columns"));
     al = new ActionListener()
@@ -1761,6 +1902,7 @@ public class GAlignFrame extends JInternalFrame
     JMenu autoAnnMenu = new JMenu(
             MessageManager.getString("label.autocalculated_annotation"));
 
+  
     JMenu exportImageMenu = new JMenu(
             MessageManager.getString("label.export_image"));
     JMenu fileMenu = new JMenu(MessageManager.getString("action.file"));
@@ -1772,11 +1914,13 @@ public class GAlignFrame extends JInternalFrame
     alignFrameMenuBar.add(formatMenu);
     alignFrameMenuBar.add(colourMenu);
     alignFrameMenuBar.add(calculateMenu);
+    alignFrameMenuBar.add(webService);
     if (!Platform.isJS())
     {
-      alignFrameMenuBar.add(webService);
+      alignFrameMenuBar.add(hmmerMenu);
     }
 
+  
     fileMenu.add(fetchSequence);
     fileMenu.add(addSequenceMenu);
     fileMenu.add(reload);
@@ -1800,6 +1944,7 @@ public class GAlignFrame extends JInternalFrame
     fileMenu.addSeparator();
     fileMenu.add(closeMenuItem);
 
+  
     pasteMenu.add(pasteNew);
     pasteMenu.add(pasteThis);
     editMenu.add(undoMenuItem);
@@ -1822,6 +1967,10 @@ public class GAlignFrame extends JInternalFrame
     // editMenu.addSeparator();
     editMenu.add(padGapsMenuitem);
 
+    editMenu.addSeparator();
+    editMenu.add(filterByEValue);
+    editMenu.add(filterByScore);
+  
     showMenu.add(showAllColumns);
     showMenu.add(showAllSeqs);
     showMenu.add(showAllhidden);
@@ -1850,6 +1999,7 @@ public class GAlignFrame extends JInternalFrame
     viewMenu.addSeparator();
     viewMenu.add(overviewMenuItem);
 
+  
     annotationsMenu.add(annotationPanelMenuItem);
     annotationsMenu.addSeparator();
     annotationsMenu.add(showAllAlAnnotations);
@@ -1876,6 +2026,8 @@ public class GAlignFrame extends JInternalFrame
     sort.add(sortLengthMenuItem);
     sort.add(sortGroupMenuItem);
     sort.add(sortPairwiseMenuItem);
+    sort.add(sortEValueMenuItem);
+    sort.add(sortBitScoreMenuItem);
     sort.add(sortByTreeMenu);
     calculateMenu.add(sort);
     calculateMenu.add(calculateTree);
@@ -1918,6 +2070,7 @@ public class GAlignFrame extends JInternalFrame
     statusPanel.add(statusBar, null);
     this.getContentPane().add(tabbedPane, java.awt.BorderLayout.CENTER);
 
+  
     formatMenu.add(font);
     formatMenu.addSeparator();
     formatMenu.add(wrapMenuItem);
@@ -1951,6 +2104,174 @@ public class GAlignFrame extends JInternalFrame
     // selectMenu.add(listenToViewSelections);
   }
 
+  /**
+   * Constructs the entries on the HMMER menu
+   */
+  protected void initHMMERMenu()
+  {
+    /*
+     * hmmbuild
+     */
+    JMenu hmmBuild = new JMenu(MessageManager.getString("label.hmmbuild"));
+    JMenuItem hmmBuildSettings = new JMenuItem(
+            MessageManager.getString("label.edit_settings_and_run"));
+    hmmBuildSettings.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmBuild_actionPerformed(false);
+      }
+    });
+    JMenuItem hmmBuildRun = new JMenuItem(MessageManager.formatMessage(
+            "label.action_with_default_settings", "hmmbuild"));
+    hmmBuildRun.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmBuild_actionPerformed(true);
+      }
+    });
+    hmmBuild.add(hmmBuildRun);
+    hmmBuild.add(hmmBuildSettings);
+
+    /*
+     * hmmalign
+     */
+    JMenu hmmAlign = new JMenu(MessageManager.getString("label.hmmalign"));
+    JMenuItem hmmAlignRun = new JMenuItem(MessageManager.formatMessage(
+            "label.action_with_default_settings", "hmmalign"));
+    hmmAlignRun.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmAlign_actionPerformed(true);
+      }
+    });
+    JMenuItem hmmAlignSettings = new JMenuItem(
+            MessageManager.getString("label.edit_settings_and_run"));
+    hmmAlignSettings.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmAlign_actionPerformed(false);
+      }
+    });
+    hmmAlign.add(hmmAlignRun);
+    hmmAlign.add(hmmAlignSettings);
+
+    /*
+     * hmmsearch
+     */
+    JMenu hmmSearch = new JMenu(
+            MessageManager.getString("label.hmmsearch"));
+    JMenuItem hmmSearchSettings = new JMenuItem(
+            MessageManager.getString("label.edit_settings_and_run"));
+    hmmSearchSettings.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmSearch_actionPerformed(false);
+      }
+    });
+    JMenuItem hmmSearchRun = new JMenuItem(MessageManager.formatMessage(
+            "label.action_with_default_settings", "hmmsearch"));
+    hmmSearchRun.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hmmSearch_actionPerformed(true);
+      }
+    });
+    JMenuItem addDatabase = new JMenuItem(
+            MessageManager.getString("label.add_database"));
+    addDatabase.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        try
+        {
+          addDatabase_actionPerformed();
+        } catch (IOException e1)
+        {
+          e1.printStackTrace();
+        }
+      }
+    });
+    hmmSearch.add(hmmSearchRun);
+    hmmSearch.add(hmmSearchSettings);
+    // hmmSearch.add(addDatabase);
+
+    /*
+     * jackhmmer
+     */
+    JMenu jackhmmer = new JMenu(
+            MessageManager.getString("label.jackhmmer"));
+    JMenuItem jackhmmerSettings = new JMenuItem(
+            MessageManager.getString("label.edit_settings_and_run"));
+    jackhmmerSettings.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        jackhmmer_actionPerformed(false);
+      }
+    });
+    JMenuItem jackhmmerRun = new JMenuItem(MessageManager.formatMessage(
+            "label.action_with_default_settings", "jackhmmer"));
+    jackhmmerRun.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        jackhmmer_actionPerformed(true);
+      }
+
+    });
+    /*
+    JMenuItem addDatabase = new JMenuItem(
+            MessageManager.getString("label.add_database"));
+    addDatabase.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        try
+        {
+          addDatabase_actionPerformed();
+        } catch (IOException e1)
+        {
+          e1.printStackTrace();
+        }
+      }
+    });
+    */
+    jackhmmer.add(jackhmmerRun);
+    jackhmmer.add(jackhmmerSettings);
+    // hmmSearch.add(addDatabase);
+
+    /*
+     * top level menu
+     */
+    hmmerMenu.setText(MessageManager.getString("action.hmmer"));
+    hmmerMenu.setEnabled(HmmerCommand.isHmmerAvailable());
+    hmmerMenu.add(hmmBuild);
+    hmmerMenu.add(hmmAlign);
+    hmmerMenu.add(hmmSearch);
+    hmmerMenu.add(jackhmmer);
+
+  }
+
+  protected void enableSortMenuOptions()
+  {
+  }
+  
   protected void loadVcf_actionPerformed()
   {
   }
@@ -2366,6 +2687,13 @@ public class GAlignFrame extends JInternalFrame
   {
   }
 
+  protected void sortEValueMenuItem_actionPerformed(ActionEvent e)
+  {
+  }
+
+  protected void sortBitScoreMenuItem_actionPerformed(ActionEvent e)
+  {
+  }
   protected void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
   {
   }
@@ -2427,10 +2755,12 @@ public class GAlignFrame extends JInternalFrame
   }
 
   protected void pasteNew_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
   }
 
   protected void pasteThis_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
   }
 
@@ -2438,6 +2768,26 @@ public class GAlignFrame extends JInternalFrame
   {
   }
 
+  protected void hmmBuild_actionPerformed(boolean withDefaults)
+  {
+  }
+
+  protected void hmmSearch_actionPerformed(boolean withDefaults)
+  {
+  }
+
+  protected void jackhmmer_actionPerformed(boolean b)
+  {
+  }
+
+  protected void addDatabase_actionPerformed()
+          throws FileFormatException, IOException
+  {
+  }
+
+  protected void hmmAlign_actionPerformed(boolean withDefaults)
+  {
+  }
   public void createPNG(java.io.File f)
   {
   }
@@ -2494,6 +2844,13 @@ public class GAlignFrame extends JInternalFrame
   {
   }
 
+  protected void filterByEValue_actionPerformed()
+  {
+  }
+
+  protected void filterByScore_actionPerformed()
+  {
+  }
   protected void scaleRight_actionPerformed(ActionEvent e)
   {
   }
@@ -2553,6 +2910,7 @@ public class GAlignFrame extends JInternalFrame
   }
 
   public void associatedData_actionPerformed(ActionEvent e)
+          throws IOException, InterruptedException
   {
 
   }
@@ -2749,4 +3107,5 @@ public class GAlignFrame extends JInternalFrame
   protected void showComplement_actionPerformed(boolean complement)
   {
   }
+  
 }
index ad1c1cf..121917e 100644 (file)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.gui.JvSwingUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
@@ -133,19 +134,15 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
         cancel_actionPerformed(e);
       }
     });
-    close.setAccelerator(
-            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W,
-                    jalview.util.ShortcutKeyMaskExWrapper
-                            .getMenuShortcutKeyMaskEx(),
-                    false));
+    close.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_W,
+            Platform.SHORTCUT_KEY_MASK,
+            false));
     selectAll.setText(MessageManager.getString("action.select_all"));
-    selectAll
-            .setAccelerator(
-                    javax.swing.KeyStroke
-                            .getKeyStroke(java.awt.event.KeyEvent.VK_A,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx(),
-                                    false));
+    selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_A,
+            Platform.SHORTCUT_KEY_MASK,
+            false));
     selectAll.addActionListener(new ActionListener()
     {
       @Override
@@ -156,11 +153,10 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     });
     jMenu1.setText(MessageManager.getString("action.file"));
     save.setText(MessageManager.getString("action.save"));
-    save.setAccelerator(
-            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S,
-                    jalview.util.ShortcutKeyMaskExWrapper
-                            .getMenuShortcutKeyMaskEx(),
-                    false));
+    save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_S,
+            Platform.SHORTCUT_KEY_MASK,
+            false));
     save.addActionListener(new ActionListener()
     {
       @Override
@@ -169,11 +165,10 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
         save_actionPerformed(e);
       }
     });
-    copyItem.setAccelerator(
-            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C,
-                    jalview.util.ShortcutKeyMaskExWrapper
-                            .getMenuShortcutKeyMaskEx(),
-                    false));
+    copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_C,
+            Platform.SHORTCUT_KEY_MASK,
+            false));
 
     editMenubar.add(jMenu1);
     editMenubar.add(editMenu);
index b75dd8c..bffb075 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
@@ -39,6 +36,9 @@ import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
 /**
  * DOCUMENT ME!
  * 
@@ -121,13 +121,9 @@ public class GCutAndPasteTransfer extends JInternalFrame
     textarea.setBorder(null);
 
     selectAll.setText(MessageManager.getString("action.select_all"));
-    selectAll
-            .setAccelerator(
-                    javax.swing.KeyStroke
-                            .getKeyStroke(java.awt.event.KeyEvent.VK_A,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx(),
-                                    false));
+    selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_A,
+            Platform.SHORTCUT_KEY_MASK, false));
     selectAll.addActionListener(new ActionListener()
     {
       @Override
@@ -138,11 +134,9 @@ public class GCutAndPasteTransfer extends JInternalFrame
     });
     jMenu1.setText(MessageManager.getString("action.file"));
     save.setText(MessageManager.getString("action.save"));
-    save.setAccelerator(
-            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S,
-                    jalview.util.ShortcutKeyMaskExWrapper
-                            .getMenuShortcutKeyMaskEx(),
-                    false));
+    save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_S,
+            Platform.SHORTCUT_KEY_MASK, false));
     save.addActionListener(new ActionListener()
     {
       @Override
@@ -151,18 +145,12 @@ public class GCutAndPasteTransfer extends JInternalFrame
         save_actionPerformed(e);
       }
     });
-    copyItem.setAccelerator(
-            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C,
-                    jalview.util.ShortcutKeyMaskExWrapper
-                            .getMenuShortcutKeyMaskEx(),
-                    false));
-    pasteMenu
-            .setAccelerator(
-                    javax.swing.KeyStroke
-                            .getKeyStroke(java.awt.event.KeyEvent.VK_V,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx(),
-                                    false));
+    copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_C,
+            Platform.SHORTCUT_KEY_MASK, false));
+    pasteMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
+            java.awt.event.KeyEvent.VK_V,
+            Platform.SHORTCUT_KEY_MASK, false));
     editMenubar.add(jMenu1);
     editMenubar.add(editMenu);
     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12));
index ca95222..2a86d69 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.jbgui;
 
+
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -35,7 +36,6 @@ import jalview.bin.Cache;
 import jalview.io.FileFormatException;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-
 /**
  * DOCUMENT ME!
  * 
@@ -46,7 +46,7 @@ import jalview.util.Platform;
 public class GDesktop extends JFrame
 {
 
-  protected static JMenu windowMenu = new JMenu();
+  protected JMenu windowMenu = new JMenu();
 
   JMenuBar desktopMenubar = new JMenuBar();
 
@@ -117,10 +117,10 @@ public class GDesktop extends JFrame
       e.printStackTrace();
     }
 
-    if (Platform.allowMnemonics())
+    if (Platform.allowMnemonics()) 
     {
-      // BH was !Platform.isAMacAndNotJS()) i.e. "JS or not Mac"
-      // but here we want just not a Mac, period, right?
+       //BH was !Platform.isAMacAndNotJS()) i.e. "JS or not Mac"
+       // but here we want just not a Mac, period, right?
       FileMenu.setMnemonic('F');
       inputLocalFileMenuItem.setMnemonic('L');
       inputURLMenuItem.setMnemonic('U');
@@ -148,6 +148,7 @@ public class GDesktop extends JFrame
      */
     try
     {
+      // TODO: if (!Platform.isJS()
       apqHandlersSet = APQHandlers.setAPQHandlers(this);
     } catch (Exception e)
     {
@@ -160,18 +161,15 @@ public class GDesktop extends JFrame
       jalview.bin.Console.trace(Cache.getStackTraceString(t));
     }
 
-    setName("jalview-desktop");
+    setName(Platform.getAppID("desktop"));
     FileMenu.setText(MessageManager.getString("action.file"));
     HelpMenu.setText(MessageManager.getString("action.help"));
     inputLocalFileMenuItem
             .setText(MessageManager.getString("label.load_tree_from_file"));
-    inputLocalFileMenuItem
-            .setAccelerator(
-                    javax.swing.KeyStroke
-                            .getKeyStroke(java.awt.event.KeyEvent.VK_O,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx(),
-                                    false));
+    inputLocalFileMenuItem.setAccelerator(
+            javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O,
+                    Platform.SHORTCUT_KEY_MASK,
+                    false));
     inputLocalFileMenuItem
             .addActionListener(new java.awt.event.ActionListener()
             {
@@ -390,6 +388,10 @@ public class GDesktop extends JFrame
       }
     });
 
+    Float specversion = Platform.isJS() ? Float.valueOf(8)
+            : Float.parseFloat(
+                    System.getProperty("java.specification.version"));
+    
     desktopMenubar.add(FileMenu);
     desktopMenubar.add(toolsMenu);
     desktopMenubar.add(HelpMenu);
@@ -397,7 +399,7 @@ public class GDesktop extends JFrame
     FileMenu.add(inputMenu);
     FileMenu.add(inputSequence);
     FileMenu.addSeparator();
-    // FileMenu.add(saveState);
+    //FileMenu.add(saveState);
     FileMenu.add(saveAsState);
     FileMenu.add(loadState);
     FileMenu.addSeparator();
@@ -519,7 +521,7 @@ public class GDesktop extends JFrame
    */
   protected void quit()
   {
-    // System.out.println("********** GDesktop.quit()");
+    //System.out.println("********** GDesktop.quit()");
   }
 
   /**
index a6498d2..aa2f549 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -88,7 +89,7 @@ public class GPCAPanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-    setName("jalview-pca");
+    setName(Platform.getAppID("pca"));
     this.getContentPane().setLayout(new BorderLayout());
     JPanel jPanel2 = new JPanel();
     jPanel2.setLayout(new FlowLayout());
index 69c0f4d..dfe08ed 100755 (executable)
  */
 package jalview.jbgui;
 
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.bin.MemorySetting;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.gui.Desktop;
+import jalview.gui.JalviewBooleanRadioButtons;
+import jalview.gui.JvOptionPane;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.BackupFilenameParts;
+import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.IntKeyStringValueEntry;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.StringUtils;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -41,6 +60,7 @@ import java.awt.event.MouseEvent;
 import java.util.Arrays;
 import java.util.List;
 
+import javax.swing.AbstractButton;
 import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
@@ -49,6 +69,7 @@ import javax.swing.DefaultListCellRenderer;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
+import javax.swing.JComponent;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -76,24 +97,7 @@ import javax.swing.event.DocumentListener;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
 
-import jalview.bin.Cache;
-import jalview.bin.Console;
-import jalview.bin.MemorySetting;
-import jalview.fts.core.FTSDataColumnPreferences;
-import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
-import jalview.fts.service.pdb.PDBFTSRestClient;
-import jalview.gui.Desktop;
-import jalview.gui.JalviewBooleanRadioButtons;
-import jalview.gui.JvOptionPane;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.BackupFilenameParts;
-import jalview.io.BackupFiles;
-import jalview.io.BackupFilesPresetEntry;
-import jalview.io.IntKeyStringValueEntry;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.util.StringUtils;
+import net.miginfocom.swing.MigLayout;
 
 /**
  * Base class for the Preferences panel.
@@ -169,6 +173,9 @@ public class GPreferences extends JPanel
 
   protected JCheckBox showConsensLogo = new JCheckBox();
 
+  protected JCheckBox showInformationHistogram = new JCheckBox();
+
+  protected JCheckBox showHMMLogo = new JCheckBox();
   protected JCheckBox showDbRefTooltip = new JCheckBox();
 
   protected JCheckBox showNpTooltip = new JCheckBox();
@@ -269,6 +276,8 @@ public class GPreferences extends JPanel
 
   protected JPasswordField proxyAuthPasswordPB = new JPasswordField();
 
+  protected JTextField defaultBrowser = new JTextField();
+
   protected ButtonGroup proxyType = new ButtonGroup();
 
   protected JRadioButton noProxy = new JRadioButton();
@@ -328,10 +337,28 @@ public class GPreferences extends JPanel
   protected JCheckBox sortByTree = new JCheckBox();
 
   /*
+   * hmmer tab and components
+   */
+  protected JPanel hmmerTab;
+
+  protected JCheckBox hmmrTrimTermini;
+
+  protected AbstractButton hmmerBackgroundUniprot;
+
+  protected AbstractButton hmmerBackgroundAlignment;
+
+  protected JTextField hmmerSequenceCount;
+
+  protected JTextField hmmerPath;
+
+  protected JTextField cygwinPath;
+
+  /*
    * Web Services tab
    */
   protected JPanel wsTab = new JPanel();
 
+  protected JPanel slivkaTab = new JPanel();
   /*
    * Backups tab components
    * a lot of these are member variables instead of local variables only so that they
@@ -405,7 +432,6 @@ public class GPreferences extends JPanel
   protected JTextField jvmMemoryMaxTextField = new JTextField(null, 8);
 
   protected JComboBox<Object> lafCombo = new JComboBox<>();
-
   /**
    * Creates a new GPreferences object.
    */
@@ -467,17 +493,20 @@ public class GPreferences extends JPanel
     tabbedPane.add(initEditingTab(),
             MessageManager.getString("label.editing"));
 
-    tabbedPane.add(initStartupTab(),
-            MessageManager.getString("label.startup"));
-
     /*
      * See WsPreferences for the real work of configuring this tab.
      */
     if (!Platform.isJS())
     {
+      tabbedPane.add(initHMMERTab(), MessageManager.getString("label.hmmer"));
+      tabbedPane.add(initStartupTab(),
+              MessageManager.getString("label.startup"));
       wsTab.setLayout(new BorderLayout());
       tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
     }
+    
+    slivkaTab.setLayout(new BorderLayout());
+    tabbedPane.add(slivkaTab, "Slivka Services");
 
     /*
      * Handler to validate a tab before leaving it - currently only for
@@ -588,7 +617,135 @@ public class GPreferences extends JPanel
   }
 
   /**
-   * Initialises the Output tab
+   * Initialises the hmmer tabbed panel
+   * 
+   * @return
+   */
+  private JPanel initHMMERTab()
+  {
+    hmmerTab = new JPanel();
+    hmmerTab.setLayout(new BoxLayout(hmmerTab, BoxLayout.Y_AXIS));
+    hmmerTab.setLayout(new MigLayout("flowy"));
+
+    /*
+     * path to hmmer binaries folder
+     */
+    JPanel installationPanel = new JPanel(new MigLayout("flowy"));
+    // new FlowLayout(FlowLayout.LEFT));
+    JvSwingUtils.createTitledBorder(installationPanel,
+            MessageManager.getString("label.installation"), true);
+    hmmerTab.add(installationPanel);
+    JLabel hmmerLocation = new JLabel(
+            MessageManager.getString("label.hmmer_location"));
+    hmmerLocation.setFont(LABEL_FONT);
+    final int pathFieldLength = 40;
+    hmmerPath = new JTextField(pathFieldLength);
+    hmmerPath.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        if (e.getClickCount() == 2)
+        {
+          String chosen = openFileChooser(true);
+          if (chosen != null)
+          {
+            hmmerPath.setText(chosen);
+            validateHmmerPath();
+          }
+        }
+      }
+    });
+    installationPanel.add(hmmerLocation);
+    installationPanel.add(hmmerPath);
+
+    /*
+     * path to Cygwin binaries folder (for Windows)
+     */
+    if (Platform.isWindowsAndNotJS())
+    {
+      JLabel cygwinLocation = new JLabel(
+              MessageManager.getString("label.cygwin_location"));
+      cygwinLocation.setFont(LABEL_FONT);
+      cygwinPath = new JTextField(pathFieldLength);
+      cygwinPath.addMouseListener(new MouseAdapter()
+      {
+        @Override
+        public void mouseClicked(MouseEvent e)
+        {
+          if (e.getClickCount() == 2)
+          {
+            String chosen = openFileChooser(true);
+            if (chosen != null)
+            {
+              cygwinPath.setText(chosen);
+              validateCygwinPath();
+            }
+          }
+        }
+      });
+      installationPanel.add(cygwinLocation);
+      installationPanel.add(cygwinPath);
+    }
+
+    /*
+     * preferences for hmmalign
+     */
+    JPanel alignOptionsPanel = new JPanel(new MigLayout());
+    // new FlowLayout(FlowLayout.LEFT));
+    JvSwingUtils.createTitledBorder(alignOptionsPanel,
+            MessageManager.getString("label.hmmalign_options"), true);
+    hmmerTab.add(alignOptionsPanel);
+    hmmrTrimTermini = new JCheckBox();
+    hmmrTrimTermini.setFont(LABEL_FONT);
+    hmmrTrimTermini.setText(MessageManager.getString("label.trim_termini"));
+    alignOptionsPanel.add(hmmrTrimTermini);
+
+    /*
+     * preferences for hmmsearch
+     */
+    JPanel searchOptions = new JPanel(new MigLayout());
+    // FlowLayout(FlowLayout.LEFT));
+    JvSwingUtils.createTitledBorder(searchOptions,
+            MessageManager.getString("label.hmmsearch_options"), true);
+    hmmerTab.add(searchOptions);
+    JLabel sequencesToKeep = new JLabel(
+            MessageManager.getString("label.no_of_sequences"));
+    sequencesToKeep.setFont(LABEL_FONT);
+    searchOptions.add(sequencesToKeep);
+    hmmerSequenceCount = new JTextField(5);
+    searchOptions.add(hmmerSequenceCount);
+
+    /*
+     * preferences for Information Content annotation
+     */
+    // JPanel dummy = new JPanel(new FlowLayout(FlowLayout.LEFT));
+    JPanel annotationOptions = new JPanel(new MigLayout("left"));
+    JvSwingUtils.createTitledBorder(annotationOptions,
+            MessageManager.getString("label.information_annotation"), true);
+    // dummy.add(annotationOptions);
+    hmmerTab.add(annotationOptions);
+    ButtonGroup backgroundOptions = new ButtonGroup();
+    hmmerBackgroundUniprot = new JRadioButton(
+            MessageManager.getString("label.freq_uniprot"));
+    hmmerBackgroundUniprot.setFont(LABEL_FONT);
+    hmmerBackgroundAlignment = new JRadioButton(
+            MessageManager.getString("label.freq_alignment"));
+    hmmerBackgroundAlignment.setFont(LABEL_FONT);
+    backgroundOptions.add(hmmerBackgroundUniprot);
+    backgroundOptions.add(hmmerBackgroundAlignment);
+    backgroundOptions.setSelected(hmmerBackgroundUniprot.getModel(), true);
+    // disable buttons for now as annotation only uses Uniprot background
+    hmmerBackgroundAlignment.setEnabled(false);
+    hmmerBackgroundUniprot.setEnabled(false);
+    annotationOptions.add(hmmerBackgroundUniprot, "wrap");
+    annotationOptions.add(hmmerBackgroundAlignment);
+
+    return hmmerTab;
+  }
+
+  /**
+   * Initialises the Output tabbed panel.
    * 
    * @return
    */
@@ -738,6 +895,7 @@ public class GPreferences extends JPanel
     connectTab = new JPanel();
     connectTab.setLayout(new GridBagLayout());
 
+
     JPanel proxyPanel = initConnTabProxyPanel();
     initConnTabCheckboxes();
 
@@ -1440,7 +1598,7 @@ public class GPreferences extends JPanel
     protColourLabel.setHorizontalAlignment(SwingConstants.LEFT);
     protColourLabel.setText(
             MessageManager.getString("label.prot_alignment_colour") + " ");
-    JvSwingUtils.addtoLayout(coloursTab,
+    GPreferences.addtoLayout(coloursTab,
             MessageManager
                     .getString("label.default_colour_scheme_for_alignment"),
             protColourLabel, protColour);
@@ -1452,7 +1610,7 @@ public class GPreferences extends JPanel
     nucColourLabel.setHorizontalAlignment(SwingConstants.LEFT);
     nucColourLabel.setText(
             MessageManager.getString("label.nuc_alignment_colour") + " ");
-    JvSwingUtils.addtoLayout(coloursTab,
+    GPreferences.addtoLayout(coloursTab,
             MessageManager
                     .getString("label.default_colour_scheme_for_alignment"),
             nucColourLabel, nucColour);
@@ -1461,11 +1619,11 @@ public class GPreferences extends JPanel
     annotationShding.setBorder(new TitledBorder(
             MessageManager.getString("label.annotation_shading_default")));
     annotationShding.setLayout(new GridLayout(1, 2));
-    JvSwingUtils.addtoLayout(annotationShding,
+    GPreferences.addtoLayout(annotationShding,
             MessageManager.getString(
                     "label.default_minimum_colour_annotation_shading"),
             mincolourLabel, minColour);
-    JvSwingUtils.addtoLayout(annotationShding,
+    GPreferences.addtoLayout(annotationShding,
             MessageManager.getString(
                     "label.default_maximum_colour_annotation_shading"),
             maxcolourLabel, maxColour);
@@ -1720,7 +1878,7 @@ public class GPreferences extends JPanel
       {
         if (structureViewerPath.isEnabled() && e.getClickCount() == 2)
         {
-          String chosen = openFileChooser();
+          String chosen = openFileChooser(false);
           if (chosen != null)
           {
             structureViewerPath.setText(chosen);
@@ -1781,12 +1939,12 @@ 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
    */
-  protected String openFileChooser()
+  protected String openFileChooser(boolean forFolder)
   {
     String choice = null;
     JFileChooser chooser = new JFileChooser();
@@ -1799,6 +1957,10 @@ public class GPreferences extends JPanel
               true);
     }
 
+    if (forFolder)
+    {
+      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+    }
     // chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.open_local_file"));
@@ -2153,12 +2315,14 @@ public class GPreferences extends JPanel
     visualTab.add(fontSizeCB);
     visualTab.add(fontStyleCB);
 
+    
     if (Platform.isJS())
     {
       startupCheckbox.setVisible(false);
       startupFileTextfield.setVisible(false);
     }
 
+    
     return visualTab;
   }
 
@@ -2170,8 +2334,8 @@ public class GPreferences extends JPanel
   {
     BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
             .getSavedBackupEntry();
-    enableBackupFiles.setSelected(
-            Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
+    enableBackupFiles
+            .setSelected(Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
 
     BackupFilesPresetEntry backupfilesCustomEntry = BackupFilesPresetEntry
             .createBackupFilesPresetEntry(Cache
@@ -2207,7 +2371,6 @@ public class GPreferences extends JPanel
     jvmMemoryMaxTextField.setText(
             Cache.getDefault(MemorySetting.MEMORY_JVMMEMMAX, "32g"));
   }
-
   private boolean warnAboutSuffixReverseChange()
   {
     BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
@@ -2612,8 +2775,8 @@ public class GPreferences extends JPanel
     presetsComboLabel = new JLabel(title + ":");
     presetsPanel.add(presetsComboLabel, gbc);
 
-    List<Object> entries = Arrays.asList(
-            (Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
+    List<Object> entries = Arrays
+            .asList((Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
     List<String> tooltips = Arrays.asList(
             BackupFilesPresetEntry.backupfilesPresetEntryDescriptions);
     backupfilesPresetsCombo = JvSwingUtils.buildComboWithTooltips(entries,
@@ -2640,8 +2803,7 @@ public class GPreferences extends JPanel
         {
           if (customiseCheckbox.isSelected())
           {
-            // got here by clicking on customiseCheckbox so don't change the
-            // values
+            // got here by clicking on customiseCheckbox so don't change the values
             backupfilesCustomOptionsSetEnabled();
           }
           else
@@ -2720,7 +2882,8 @@ public class GPreferences extends JPanel
 
   private JPanel initBackupsTabFilenameExamplesPanel()
   {
-    String title = MessageManager.getString("label.scheme_examples");
+    String title = MessageManager
+            .getString("label.scheme_examples");
     TitledBorder tb = new TitledBorder(title);
     exampleFilesPanel.setBorder(tb);
     exampleFilesPanel.setLayout(new GridBagLayout());
@@ -2785,7 +2948,8 @@ public class GPreferences extends JPanel
   }
 
   protected void setComboIntStringKey(
-          JComboBox<Object> backupfilesPresetsCombo2, int key)
+          JComboBox<Object> backupfilesPresetsCombo2,
+          int key)
   {
     for (int i = 0; i < backupfilesPresetsCombo2.getItemCount(); i++)
     {
@@ -2953,7 +3117,7 @@ public class GPreferences extends JPanel
     boolean ret = false;
     String warningMessage = MessageManager
             .getString("label.warning_confirm_change_reverse");
-    int confirm = JvOptionPane.showConfirmDialog(Desktop.desktop,
+    int confirm = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
             warningMessage,
             MessageManager.getString("label.change_increment_decrement"),
             JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE);
@@ -3055,8 +3219,9 @@ public class GPreferences extends JPanel
 
     JPanel jp = new JPanel();
     jp.setLayout(new FlowLayout());
-    oldBackupFilesLabel.setText(
-            MessageManager.getString("label.autodelete_old_backup_files"));
+    oldBackupFilesLabel
+            .setText(MessageManager
+                    .getString("label.autodelete_old_backup_files"));
     oldBackupFilesLabel.setFont(LABEL_FONT);
     oldBackupFilesLabel.setHorizontalAlignment(SwingConstants.LEFT);
     jp.add(oldBackupFilesLabel);
@@ -3216,8 +3381,7 @@ public class GPreferences extends JPanel
 
     }
 
-    // add some extra empty lines to pad out the example files box. ugh, please
-    // tell
+    // add some extra empty lines to pad out the example files box. ugh, please tell
     // me how to do this better
     int remainingLines = lowersurround + uppersurround + 1 - lineNumber;
     if (remainingLines > 0)
@@ -3296,7 +3460,8 @@ public class GPreferences extends JPanel
   private void backupfilesKeepAllSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && customiseCheckbox.isSelected() && suffixTemplate.getText()
+            && customiseCheckbox.isSelected()
+            && suffixTemplate.getText()
                     .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     keepfilesPanel.setEnabled(enabled);
     backupfilesKeepAll.setEnabled(enabled);
@@ -3498,6 +3663,11 @@ public class GPreferences extends JPanel
 
   }
 
+  public void defaultBrowser_mouseClicked(MouseEvent e)
+  {
+
+  }
+
   public void linkURLList_keyTyped(KeyEvent e)
   {
 
@@ -3615,4 +3785,40 @@ public class GPreferences extends JPanel
     }
 
   }
+
+  protected void validateHmmerPath()
+  {
+  }
+
+  protected void validateCygwinPath()
+  {
+  }
+
+  /**
+   * A helper method to add a panel containing a label and a component to a
+   * panel
+   * 
+   * @param panel
+   * @param tooltip
+   * @param label
+   * @param valBox
+   */
+  protected static void addtoLayout(JPanel panel, String tooltip,
+          JComponent label, JComponent valBox)
+  {
+    JPanel laypanel = new JPanel(new GridLayout(1, 2));
+    JPanel labPanel = new JPanel(new BorderLayout());
+    JPanel valPanel = new JPanel();
+    labPanel.setBounds(new Rectangle(7, 7, 158, 23));
+    valPanel.setBounds(new Rectangle(172, 7, 270, 23));
+    labPanel.add(label, BorderLayout.WEST);
+    valPanel.add(valBox);
+    laypanel.add(labPanel);
+    laypanel.add(valPanel);
+    valPanel.setToolTipText(tooltip);
+    labPanel.setToolTipText(tooltip);
+    valBox.setToolTipText(tooltip);
+    panel.add(laypanel);
+    panel.validate();
+  }
 }
index 5170a6c..14ebbc0 100644 (file)
@@ -103,7 +103,7 @@ public class GRestInputParamEditDialog
     optionsPanel = new JPanel(new MigLayout("", "[fill]", "[fill]"));
     JScrollPane optionView = new JScrollPane();
     optionView.setViewportView(options);
-    JvSwingUtils.mgAddtoLayout(dpane,
+    JvSwingUtils.addtoLayout(dpane,
             MessageManager.getString("label.input_parameter_name"),
             new JLabel(MessageManager.getString("label.name")), tok,
             "grow,spanx 3,wrap");
index a4dca4b..db68757 100644 (file)
@@ -109,20 +109,20 @@ public class GRestServiceEditorPane extends JPanel
     cpanel = details;
     name = new JTextArea(1, 12);
 
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager
                     .getString("label.short_descriptive_name_for_service"),
             new JLabel(MessageManager.getString("label.name")), name,
             "wrap");
     action = new JComboBox();
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager.getString("label.function_service_performs"),
             new JLabel(MessageManager.getString("label.service_action")),
             action, "wrap");
     descr = new JTextArea(4, 60);
     descrVp = new JScrollPane();
     descrVp.setViewportView(descr);
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager.getString("label.brief_description_service"),
             new JLabel(MessageManager.getString("label.description")),
             descrVp, "wrap");
@@ -130,7 +130,7 @@ public class GRestServiceEditorPane extends JPanel
     url = new JTextArea(2, 60);
     urlVp = new JScrollPane();
     urlVp.setViewportView(url);
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager.getString("label.url_post_data_service"),
             new JLabel(MessageManager.getString("label.post_url")), urlVp,
             "wrap");
@@ -138,7 +138,7 @@ public class GRestServiceEditorPane extends JPanel
     urlsuff = new JTextArea();
     urlsuff.setColumns(60);
 
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager.getString("label.optional_suffix"),
             new JLabel(MessageManager.getString("label.url_suffix")),
             urlsuff, "wrap");
@@ -175,7 +175,7 @@ public class GRestServiceEditorPane extends JPanel
       }
     });
     gapChar = new JComboBox();
-    JvSwingUtils.mgAddtoLayout(cpanel,
+    JvSwingUtils.addtoLayout(cpanel,
             MessageManager.getString("label.preferred_gap_character"),
             new JLabel(
                     MessageManager.getString("label.gap_character") + ":"),
index a43e504..2ec7051 100755 (executable)
@@ -219,7 +219,7 @@ public class GSequenceLink extends JPanel
       return true;
     }
 
-    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.getDesktopPane(),
             MessageManager.getString("warn.url_must_contain"),
             MessageManager.getString("label.invalid_url"),
             JvOptionPane.WARNING_MESSAGE);
@@ -228,7 +228,7 @@ public class GSequenceLink extends JPanel
 
   public void notifyDuplicate()
   {
-    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.getDesktopPane(),
             MessageManager.getString("warn.name_cannot_be_duplicate"),
             MessageManager.getString("label.invalid_name"),
             JvOptionPane.WARNING_MESSAGE);
index 4e13032..131ec16 100644 (file)
  */
 package jalview.jbgui;
 
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.gui.ColourMenuHelper.ColourChangeListener;
+import jalview.util.ImageMaker.TYPE;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 import java.awt.BorderLayout;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
@@ -33,11 +39,6 @@ import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JRadioButtonMenuItem;
 
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.gui.ColourMenuHelper.ColourChangeListener;
-import jalview.util.ImageMaker.TYPE;
-import jalview.util.MessageManager;
-
 @SuppressWarnings("serial")
 public abstract class GStructureViewer extends JInternalFrame
         implements JalviewStructureDisplayI, ColourChangeListener
@@ -89,7 +90,7 @@ public abstract class GStructureViewer extends JInternalFrame
   private void jbInit() throws Exception
   {
 
-    setName("jalview-structureviewer");
+    setName(Platform.getAppID("structureviewer"));
 
     JMenuBar menuBar = new JMenuBar();
     this.setJMenuBar(menuBar);
@@ -166,6 +167,7 @@ public abstract class GStructureViewer extends JInternalFrame
     JMenu helpMenu = new JMenu();
     helpMenu.setText(MessageManager.getString("action.help"));
     helpItem = new JMenuItem();
+    helpItem.setText(MessageManager.getString("label.jmol_help"));
     helpItem.addActionListener(new ActionListener()
     {
       @Override
index d184e76..3aff0e0 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -92,7 +93,7 @@ public class GTreePanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-    setName("jalview-tree");
+    setName(Platform.getAppID("tree"));
     this.getContentPane().setLayout(borderLayout1);
     this.setBackground(Color.white);
     this.setFont(new java.awt.Font("Verdana", 0, 12));
index 359f787..4b10546 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.bin.Jalview;
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -45,6 +41,9 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 import javax.swing.colorchooser.AbstractColorChooserPanel;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
index d4b2c04..123792c 100644 (file)
  */
 package jalview.project;
 
+
 import static jalview.math.RotatableMatrix.Axis.X;
 import static jalview.math.RotatableMatrix.Axis.Y;
 import static jalview.math.RotatableMatrix.Axis.Z;
 
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Rectangle;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.math.BigInteger;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.Vector;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-
-import javax.swing.JInternalFrame;
-import javax.swing.SwingUtilities;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.Marshaller;
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.datatype.XMLGregorianCalendar;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamReader;
 
 import jalview.analysis.Conservation;
 import jalview.analysis.PCA;
 import jalview.analysis.scoremodels.ScoreModels;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
@@ -92,6 +45,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GeneLocus;
 import jalview.datamodel.GraphLine;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Point;
 import jalview.datamodel.RnaViewerModel;
@@ -109,7 +63,9 @@ import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.AppVarna;
+import jalview.gui.ChimeraViewFrame;
 import jalview.gui.Desktop;
+import jalview.gui.FeatureRenderer;
 import jalview.gui.JvOptionPane;
 import jalview.gui.OOMWarning;
 import jalview.gui.PCAPanel;
@@ -122,6 +78,7 @@ import jalview.gui.TreePanel;
 import jalview.io.BackupFiles;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
+import jalview.io.HMMFile;
 import jalview.io.NewickFile;
 import jalview.math.Matrix;
 import jalview.math.MatrixI;
@@ -147,9 +104,9 @@ import jalview.viewmodel.ViewportRanges;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
-import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.PreferredServiceRegistry;
 import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
@@ -201,6 +158,57 @@ import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties;
 import jalview.xml.binding.jalview.ThresholdType;
 import jalview.xml.binding.jalview.VAMSAS;
 
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.SwingUtilities;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
  * 
@@ -214,4376 +222,4593 @@ import jalview.xml.binding.jalview.VAMSAS;
 public class Jalview2XML
 {
 
-  // BH 2018 we add the .jvp binary extension to J2S so that
-  // it will declare that binary when we do the file save from the browser
+    // BH 2018 we add the .jvp binary extension to J2S so that
+    // it will declare that binary when we do the file save from the browser
 
-  static
-  {
-    Platform.addJ2SBinaryType(".jvp?");
-  }
+    static
+    {
+       Platform.addJ2SBinaryType(".jvp?");
+    }
 
-  private static final String VIEWER_PREFIX = "viewer_";
+    private static final String VIEWER_PREFIX = "viewer_";
 
-  private static final String RNA_PREFIX = "rna_";
+    private static final String RNA_PREFIX = "rna_";
 
-  private static final String UTF_8 = "UTF-8";
+    private static final String HMMER_PREFIX = "hmmer_";
+    private static final String UTF_8 = "UTF-8";
 
-  /**
-   * prefix for recovering datasets for alignments with multiple views where
-   * non-existent dataset IDs were written for some views
-   */
-  private static final String UNIQSEQSETID = "uniqueSeqSetId.";
+    /**
+     * prefix for recovering datasets for alignments with multiple views where
+     * non-existent dataset IDs were written for some views
+     */
+    private static final String UNIQSEQSETID = "uniqueSeqSetId.";
 
-  // use this with nextCounter() to make unique names for entities
-  private int counter = 0;
+    // use this with nextCounter() to make unique names for entities
+    private int counter = 0;
 
-  /*
-   * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
-   * of sequence objects are created.
-   */
-  IdentityHashMap<SequenceI, String> seqsToIds = null;
+    /*
+     * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
+     * of sequence objects are created.
+     */
+    IdentityHashMap<SequenceI, String> seqsToIds = null;
 
-  /**
-   * jalview XML Sequence ID to jalview sequence object reference (both dataset
-   * and alignment sequences. Populated as XML reps of sequence objects are
-   * created.)
-   */
-  Map<String, SequenceI> seqRefIds = null;
+    /**
+     * jalview XML Sequence ID to jalview sequence object reference (both dataset
+     * and alignment sequences. Populated as XML reps of sequence objects are
+     * created.)
+     */
+    Map<String, SequenceI> seqRefIds = null;
 
-  Map<String, SequenceI> incompleteSeqs = null;
+    Map<String, SequenceI> incompleteSeqs = null;
 
-  List<SeqFref> frefedSequence = null;
+    List<SeqFref> frefedSequence = null;
 
-  boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
+    boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
-  /*
-   * Map of reconstructed AlignFrame objects that appear to have come from
-   * SplitFrame objects (have a dna/protein complement view).
-   */
-  private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<>();
+    /*
+     * Map of reconstructed AlignFrame objects that appear to have come from
+     * SplitFrame objects (have a dna/protein complement view).
+     */
+    private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<>();
 
-  /*
-   * Map from displayed rna structure models to their saved session state jar
-   * entry names
-   */
-  private Map<RnaModel, String> rnaSessions = new HashMap<>();
+    /*
+     * Map from displayed rna structure models to their saved session state jar
+     * entry names
+     */
+    private Map<RnaModel, String> rnaSessions = new HashMap<>();
 
-  /**
-   * A helper method for safely using the value of an optional attribute that
-   * may be null if not present in the XML. Answers the boolean value, or false
-   * if null.
-   * 
-   * @param b
-   * @return
-   */
-  public static boolean safeBoolean(Boolean b)
-  {
-    return b == null ? false : b.booleanValue();
-  }
+    /**
+     * contains last error message (if any) encountered by XML loader.
+     */
+    String errorMessage = null;
 
-  /**
-   * A helper method for safely using the value of an optional attribute that
-   * may be null if not present in the XML. Answers the integer value, or zero
-   * if null.
-   * 
-   * @param i
-   * @return
-   */
-  public static int safeInt(Integer i)
-  {
-    return i == null ? 0 : i.intValue();
-  }
+    /**
+     * flag to control whether the Jalview2XML_V1 parser should be deferred to if
+     * exceptions are raised during project XML parsing
+     */
+    public boolean attemptversion1parse = false;
 
-  /**
-   * A helper method for safely using the value of an optional attribute that
-   * may be null if not present in the XML. Answers the float value, or zero if
-   * null.
-   * 
-   * @param f
-   * @return
-   */
-  public static float safeFloat(Float f)
-  {
-    return f == null ? 0f : f.floatValue();
-  }
+    /*
+     * JalviewJS only -- to allow read file bytes to be saved in the
+     * created AlignFrame, allowing File | Reload of a project file to work
+     * 
+     * BH 2019 JAL-3436
+     */
+    private File jarFile;
 
-  /**
-   * create/return unique hash string for sq
-   * 
-   * @param sq
-   * @return new or existing unique string for sq
-   */
-  String seqHash(SequenceI sq)
-  {
-    if (seqsToIds == null)
+    /**
+     * A helper method for safely using the value of an optional attribute that
+     * may be null if not present in the XML. Answers the boolean value, or false
+     * if null.
+     * 
+     * @param b
+     * @return
+     */
+    public static boolean safeBoolean(Boolean b)
     {
-      initSeqRefs();
+       return b == null ? false : b.booleanValue();
     }
-    if (seqsToIds.containsKey(sq))
+
+    /**
+     * A helper method for safely using the value of an optional attribute that
+     * may be null if not present in the XML. Answers the integer value, or zero
+     * if null.
+     * 
+     * @param i
+     * @return
+     */
+    public static int safeInt(Integer i)
     {
-      return seqsToIds.get(sq);
+       return i == null ? 0 : i.intValue();
     }
-    else
+
+    /**
+     * A helper method for safely using the value of an optional attribute that
+     * may be null if not present in the XML. Answers the float value, or zero if
+     * null.
+     * 
+     * @param f
+     * @return
+     */
+    public static float safeFloat(Float f)
     {
-      // create sequential key
-      String key = "sq" + (seqsToIds.size() + 1);
-      key = makeHashCode(sq, key); // check we don't have an external reference
-      // for it already.
-      seqsToIds.put(sq, key);
-      return key;
+       return f == null ? 0f : f.floatValue();
     }
-  }
 
-  void initSeqRefs()
-  {
-    if (seqsToIds == null)
+    /**
+     * create/return unique hash string for sq
+     * 
+     * @param sq
+     * @return new or existing unique string for sq
+     */
+    String seqHash(SequenceI sq)
     {
-      seqsToIds = new IdentityHashMap<>();
+       if (seqsToIds == null)
+           {
+               initSeqRefs();
+           }
+       if (seqsToIds.containsKey(sq))
+           {
+               return seqsToIds.get(sq);
+           }
+       else
+           {
+               // create sequential key
+               String key = "sq" + (seqsToIds.size() + 1);
+               key = makeHashCode(sq, key); // check we don't have an external reference
+               // for it already.
+               seqsToIds.put(sq, key);
+               return key;
+           }
     }
-    if (seqRefIds == null)
+
+    void initSeqRefs()
     {
-      seqRefIds = new HashMap<>();
+       if (seqsToIds == null)
+           {
+               seqsToIds = new IdentityHashMap<>();
+           }
+       if (seqRefIds == null)
+           {
+               seqRefIds = new HashMap<>();
+           }
+       if (incompleteSeqs == null)
+           {
+               incompleteSeqs = new HashMap<>();
+           }
+       if (frefedSequence == null)
+           {
+               frefedSequence = new ArrayList<>();
+           }
     }
-    if (incompleteSeqs == null)
+
+    public Jalview2XML()
     {
-      incompleteSeqs = new HashMap<>();
     }
-    if (frefedSequence == null)
+
+    public Jalview2XML(boolean raiseGUI)
     {
-      frefedSequence = new ArrayList<>();
+       this.raiseGUI = raiseGUI;
     }
-  }
 
-  public Jalview2XML()
-  {
-  }
+    /**
+     * base class for resolving forward references to sequences by their ID
+     * 
+     * @author jprocter
+     *
+     */
+    abstract class SeqFref
+    {
+       String sref;
+
+       String type;
+
+       public SeqFref(String _sref, String type)
+       {
+           sref = _sref;
+           this.type = type;
+       }
+
+       public String getSref()
+       {
+           return sref;
+       }
+
+       public SequenceI getSrefSeq()
+       {
+           return seqRefIds.get(sref);
+       }
+
+       public boolean isResolvable()
+       {
+           return seqRefIds.get(sref) != null;
+       }
+
+       public SequenceI getSrefDatasetSeq()
+       {
+           SequenceI sq = seqRefIds.get(sref);
+           if (sq != null)
+               {
+                   while (sq.getDatasetSequence() != null)
+                       {
+                           sq = sq.getDatasetSequence();
+                       }
+               }
+           return sq;
+       }
+
+       /**
+        * @return true if the forward reference was fully resolved
+        */
+       abstract boolean resolve();
+
+       @Override
+       public String toString()
+       {
+           return type + " reference to " + sref;
+       }
+    }
 
-  public Jalview2XML(boolean raiseGUI)
-  {
-    this.raiseGUI = raiseGUI;
-  }
+    /**
+     * create forward reference for a mapping
+     * 
+     * @param sref
+     * @param _jmap
+     * @return
+     */
+    protected SeqFref newMappingRef(final String sref,
+                                   final jalview.datamodel.Mapping _jmap)
+    {
+       SeqFref fref = new SeqFref(sref, "Mapping")
+           {
+               public jalview.datamodel.Mapping jmap = _jmap;
+
+               @Override
+               boolean resolve()
+               {
+                   SequenceI seq = getSrefDatasetSeq();
+                   if (seq == null)
+                       {
+                           return false;
+                       }
+                   jmap.setTo(seq);
+                   return true;
+               }
+           };
+       return fref;
+    }
+
+    protected SeqFref newAlcodMapRef(final String sref,
+                                    final AlignedCodonFrame _cf,
+                                    final jalview.datamodel.Mapping _jmap)
+    {
+
+       SeqFref fref = new SeqFref(sref, "Codon Frame")
+           {
+               AlignedCodonFrame cf = _cf;
+
+               public jalview.datamodel.Mapping mp = _jmap;
+
+               @Override
+               public boolean isResolvable()
+               {
+                   return super.isResolvable() && mp.getTo() != null;
+               }
+
+               @Override
+               boolean resolve()
+               {
+                   SequenceI seq = getSrefDatasetSeq();
+                   if (seq == null)
+                       {
+                           return false;
+                       }
+                   cf.addMap(seq, mp.getTo(), mp.getMap());
+                   return true;
+               }
+           };
+       return fref;
+    }
+
+    protected void resolveFrefedSequences()
+    {
+       Iterator<SeqFref> nextFref = frefedSequence.iterator();
+       int toresolve = frefedSequence.size();
+       int unresolved = 0, failedtoresolve = 0;
+       while (nextFref.hasNext())
+           {
+               SeqFref ref = nextFref.next();
+               if (ref.isResolvable())
+                   {
+                       try
+                           {
+                               if (ref.resolve())
+                                   {
+                                       nextFref.remove();
+                                   }
+                               else
+                                   {
+                                       failedtoresolve++;
+                                   }
+                           } catch (Exception x)
+                           {
+                               System.err.println(
+                                                  "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
+                                                  + ref.getSref());
+                               x.printStackTrace();
+                               failedtoresolve++;
+                           }
+                   }
+               else
+                   {
+                       unresolved++;
+                   }
+           }
+       if (unresolved > 0)
+           {
+               System.err.println("Jalview Project Import: There were " + unresolved
+                                  + " forward references left unresolved on the stack.");
+           }
+       if (failedtoresolve > 0)
+           {
+               System.err.println("SERIOUS! " + failedtoresolve
+                                  + " resolvable forward references failed to resolve.");
+           }
+       if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+           {
+               System.err.println(
+                                  "Jalview Project Import: There are " + incompleteSeqs.size()
+                                  + " sequences which may have incomplete metadata.");
+               if (incompleteSeqs.size() < 10)
+                   {
+                       for (SequenceI s : incompleteSeqs.values())
+                           {
+                               System.err.println(s.toString());
+                           }
+                   }
+               else
+                   {
+                       System.err.println(
+                                          "Too many to report. Skipping output of incomplete sequences.");
+                   }
+           }
+    }
 
-  /**
-   * base class for resolving forward references to sequences by their ID
-   * 
-   * @author jprocter
-   *
-   */
-  abstract class SeqFref
-  {
-    String sref;
+    /**
+     * This maintains a map of viewports, the key being the seqSetId. Important to
+     * set historyItem and redoList for multiple views
+     */
+    Map<String, AlignViewport> viewportsAdded = new HashMap<>();
 
-    String type;
+    Map<String, AlignmentAnnotation> annotationIds = new HashMap<>();
 
-    public SeqFref(String _sref, String type)
-    {
-      sref = _sref;
-      this.type = type;
+    String uniqueSetSuffix = "";
+
+    /**
+     * List of pdbfiles added to Jar
+     */
+    List<String> pdbfiles = null;
+
+    // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
+    public void saveState(File statefile)
+    {
+       FileOutputStream fos = null;
+
+       try
+           {
+
+               fos = new FileOutputStream(statefile);
+
+               JarOutputStream jout = new JarOutputStream(fos);
+               saveState(jout);
+               fos.close();
+
+           } catch (Exception e)
+           {
+               Console.error("Couln't write Jalview state to " + statefile, e);
+               // TODO: inform user of the problem - they need to know if their data was
+               // not saved !
+               if (errorMessage == null)
+                   {
+                       errorMessage = "Did't write Jalview Archive to output file '"
+                           + statefile + "' - See console error log for details";
+                   }
+               else
+                   {
+                       errorMessage += "(Didn't write Jalview Archive to output file '"
+                           + statefile + ")";
+                   }
+               e.printStackTrace();
+           } finally
+           {
+               if (fos != null)
+                   {
+                       try
+                           {
+                               fos.close();
+                           } catch (IOException e)
+                           {
+                               // ignore
+                           }
+                   }
+           }
+       reportErrors();
     }
 
-    public String getSref()
+    /**
+     * Writes a jalview project archive to the given Jar output stream.
+     * 
+     * @param jout
+     */
+    public void saveState(JarOutputStream jout)
     {
-      return sref;
+       AlignFrame[] frames = Desktop.getAlignFrames();
+
+       if (frames == null)
+           {
+               return;
+           }
+       saveAllFrames(Arrays.asList(frames), jout);
     }
 
-    public SequenceI getSrefSeq()
-    {
-      return seqRefIds.get(sref);
+    /**
+     * core method for storing state for a set of AlignFrames.
+     * 
+     * @param frames
+     *          - frames involving all data to be exported (including those
+     *          contained in splitframes, though not the split frames themselves)
+     * @param jout
+     *          - project output stream
+     */
+    private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
+    {
+       Hashtable<String, AlignFrame> dsses = new Hashtable<>();
+
+       /*
+        * ensure cached data is clear before starting
+        */
+       // todo tidy up seqRefIds, seqsToIds initialisation / reset
+       rnaSessions.clear();
+       splitFrameCandidates.clear();
+
+       try
+           {
+
+               // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
+               // //////////////////////////////////////////////////
+
+               List<String> shortNames = new ArrayList<>();
+               List<String> viewIds = new ArrayList<>();
+
+               // REVERSE ORDER
+               for (int i = frames.size() - 1; i > -1; i--)
+                   {
+                       AlignFrame af = frames.get(i);
+                       AlignViewport vp = af.getViewport();
+                       // skip ?
+                       if (skipList != null && skipList
+                           .containsKey(vp.getSequenceSetId()))
+                           {
+                               continue;
+                           }
+
+                       String shortName = makeFilename(af, shortNames);
+
+                       AlignmentI alignment = vp.getAlignment();
+                       List<? extends AlignmentViewPanel> panels = af.getAlignPanels();
+                       int apSize = panels.size();
+
+                       for (int ap = 0; ap < apSize; ap++)
+                           {
+                               AlignmentPanel apanel = (AlignmentPanel) panels.get(ap);
+                               String fileName = apSize == 1 ? shortName : ap + shortName;
+                               if (!fileName.endsWith(".xml"))
+                                   {
+                                       fileName = fileName + ".xml";
+                                   }
+
+                               saveState(apanel, fileName, jout, viewIds);
+
+                           }
+                       if (apSize > 0)
+                           {
+                               // BH moved next bit out of inner loop, not that it really matters.
+                               // so we are testing to make sure we actually have an alignment,
+                               // apparently.
+                               String dssid = getDatasetIdRef(alignment.getDataset());
+                               if (!dsses.containsKey(dssid))
+                                   {
+                                       // We have not already covered this data by reference from another
+                                       // frame.
+                                       dsses.put(dssid, af);
+                                   }
+                           }
+                   }
+
+               writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
+                               jout);
+
+               try
+                   {
+                       jout.flush();
+                   } catch (Exception foo)
+                   {
+                   }
+               jout.close();
+           } catch (Exception ex)
+           {
+               // TODO: inform user of the problem - they need to know if their data was
+               // not saved !
+               if (errorMessage == null)
+                   {
+                       errorMessage = "Couldn't write Jalview Archive - see error output for details";
+                   }
+               ex.printStackTrace();
+           }
     }
 
-    public boolean isResolvable()
-    {
-      return seqRefIds.get(sref) != null;
+    /**
+     * Generates a distinct file name, based on the title of the AlignFrame, by
+     * appending _n for increasing n until an unused name is generated. The new
+     * name (without its extension) is added to the list.
+     * 
+     * @param af
+     * @param namesUsed
+     * @return the generated name, with .xml extension
+     */
+    protected String makeFilename(AlignFrame af, List<String> namesUsed)
+    {
+       String shortName = af.getTitle();
+
+       if (shortName.indexOf(File.separatorChar) > -1)
+           {
+               shortName = shortName
+                   .substring(shortName.lastIndexOf(File.separatorChar) + 1);
+           }
+
+       int count = 1;
+
+       while (namesUsed.contains(shortName))
+           {
+               if (shortName.endsWith("_" + (count - 1)))
+                   {
+                       shortName = shortName.substring(0, shortName.lastIndexOf("_"));
+                   }
+
+               shortName = shortName.concat("_" + count);
+               count++;
+           }
+
+       namesUsed.add(shortName);
+
+       if (!shortName.endsWith(".xml"))
+           {
+               shortName = shortName + ".xml";
+           }
+       return shortName;
+    }
+
+    // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
+    public boolean saveAlignment(AlignFrame af, String jarFile,
+                                String fileName)
+    {
+       try
+           {
+               // create backupfiles object and get new temp filename destination
+               boolean doBackup = BackupFiles.getEnabled();
+               BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
+               FileOutputStream fos = new FileOutputStream(doBackup ? 
+                                                           backupfiles.getTempFilePath() : jarFile);
+
+               JarOutputStream jout = new JarOutputStream(fos);
+               List<AlignFrame> frames = new ArrayList<>();
+
+               // resolve splitframes
+               if (af.getViewport().getCodingComplement() != null)
+                   {
+                       frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
+                   }
+               else
+                   {
+                       frames.add(af);
+                   }
+               saveAllFrames(frames, jout);
+               try
+                   {
+                       jout.flush();
+                   } catch (Exception foo)
+                   {
+                   }
+               jout.close();
+               boolean success = true;
+
+               if (doBackup)
+                   {
+                       backupfiles.setWriteSuccess(success);
+                       success = backupfiles.rollBackupsAndRenameTempFile();
+                   }
+
+               return success;
+           } catch (Exception ex)
+           {
+               errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
+               ex.printStackTrace();
+               return false;
+           }
     }
 
-    public SequenceI getSrefDatasetSeq()
+    /**
+     * Each AlignFrame has a single data set associated with it. Note that none of
+     * these frames are split frames, because Desktop.getAlignFrames() collects
+     * top and bottom separately here.
+     * 
+     * @param dsses
+     * @param fileName
+     * @param jout
+     */
+    private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
+                                String fileName, JarOutputStream jout)
     {
-      SequenceI sq = seqRefIds.get(sref);
-      if (sq != null)
-      {
-        while (sq.getDatasetSequence() != null)
-        {
-          sq = sq.getDatasetSequence();
-        }
-      }
-      return sq;
+
+       // Note that in saveAllFrames we have associated each specific dataset to
+       // ONE of its associated frames.
+       for (String dssids : dsses.keySet())
+           {
+               AlignFrame _af = dsses.get(dssids);
+               String jfileName = fileName + " Dataset for " + _af.getTitle();
+               if (!jfileName.endsWith(".xml"))
+                   {
+                       jfileName = jfileName + ".xml";
+                   }
+               saveState(_af.alignPanel, jfileName, true, jout, null);
+           }
     }
 
     /**
-     * @return true if the forward reference was fully resolved
+     * create a JalviewModel from an alignment view and marshall it to a
+     * JarOutputStream
+     * 
+     * @param ap
+     *          panel to create jalview model for
+     * @param fileName
+     *          name of alignment panel written to output stream
+     * @param jout
+     *          jar output stream
+     * @param viewIds
+     * @param out
+     *          jar entry name
      */
-    abstract boolean resolve();
-
-    @Override
-    public String toString()
+    protected JalviewModel saveState(AlignmentPanel ap, String fileName,
+                                    JarOutputStream jout, List<String> viewIds)
     {
-      return type + " reference to " + sref;
+       return saveState(ap, fileName, false, jout, viewIds);
     }
-  }
 
-  /**
-   * create forward reference for a mapping
-   * 
-   * @param sref
-   * @param _jmap
-   * @return
-   */
-  public SeqFref newMappingRef(final String sref,
-          final jalview.datamodel.Mapping _jmap)
-  {
-    SeqFref fref = new SeqFref(sref, "Mapping")
-    {
-      public jalview.datamodel.Mapping jmap = _jmap;
+    /**
+     * create a JalviewModel from an alignment view and marshall it to a
+     * JarOutputStream
+     * 
+     * @param ap
+     *          panel to create jalview model for
+     * @param fileName
+     *          name of alignment panel written to output stream
+     * @param storeDS
+     *          when true, only write the dataset for the alignment, not the data
+     *          associated with the view.
+     * @param jout
+     *          jar output stream
+     * @param out
+     *          jar entry name
+     */
+    protected JalviewModel saveState(AlignmentPanel ap, String fileName,
+                                    boolean storeDS, JarOutputStream jout, List<String> viewIds)
+    {
+       if (viewIds == null)
+           {
+               viewIds = new ArrayList<>();
+           }
+
+       initSeqRefs();
+
+       List<UserColourScheme> userColours = new ArrayList<>();
+
+       AlignViewport av = ap.av;
+       ViewportRanges vpRanges = av.getRanges();
+
+       final ObjectFactory objectFactory = new ObjectFactory();
+       JalviewModel object = objectFactory.createJalviewModel();
+       object.setVamsasModel(new VAMSAS());
+
+       // object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
+       try
+           {
+               GregorianCalendar c = new GregorianCalendar();
+               DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
+               XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar);
+               object.setCreationDate(now);
+           } catch (DatatypeConfigurationException e)
+           {
+               System.err.println("error writing date: " + e.toString());
+           }
+       object.setVersion(Cache.getDefault("VERSION", "Development Build"));
+
+       /**
+        * rjal is full height alignment, jal is actual alignment with full metadata
+        * but excludes hidden sequences.
+        */
+       jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
+
+       if (av.hasHiddenRows())
+           {
+               rjal = jal.getHiddenSequences().getFullAlignment();
+           }
+
+       SequenceSet vamsasSet = new SequenceSet();
+       Sequence vamsasSeq;
+       // JalviewModelSequence jms = new JalviewModelSequence();
+
+       vamsasSet.setGapChar(jal.getGapCharacter() + "");
+
+       if (jal.getDataset() != null)
+           {
+               // dataset id is the dataset's hashcode
+               vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
+               if (storeDS)
+                   {
+                       // switch jal and the dataset
+                       jal = jal.getDataset();
+                       rjal = jal;
+                   }
+           }
+       if (jal.getProperties() != null)
+           {
+               Enumeration en = jal.getProperties().keys();
+               while (en.hasMoreElements())
+                   {
+                       String key = en.nextElement().toString();
+                       SequenceSetProperties ssp = new SequenceSetProperties();
+                       ssp.setKey(key);
+                       ssp.setValue(jal.getProperties().get(key).toString());
+                       // vamsasSet.addSequenceSetProperties(ssp);
+                       vamsasSet.getSequenceSetProperties().add(ssp);
+                   }
+           }
+
+       JSeq jseq;
+       Set<String> calcIdSet = new HashSet<>();
+       // record the set of vamsas sequence XML POJO we create.
+       HashMap<String, Sequence> vamsasSetIds = new HashMap<>();
+       // SAVE SEQUENCES
+       for (final SequenceI jds : rjal.getSequences())
+           {
+               final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
+                   : jds.getDatasetSequence();
+               String id = seqHash(jds);
+               if (vamsasSetIds.get(id) == null)
+                   {
+                       if (seqRefIds.get(id) != null && !storeDS)
+                           {
+                               // This happens for two reasons: 1. multiple views are being
+                               // serialised.
+                               // 2. the hashCode has collided with another sequence's code. This
+                               // DOES
+                               // HAPPEN! (PF00072.15.stk does this)
+                               // JBPNote: Uncomment to debug writing out of files that do not read
+                               // back in due to ArrayOutOfBoundExceptions.
+                               // System.err.println("vamsasSeq backref: "+id+"");
+                               // System.err.println(jds.getName()+"
+                               // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
+                               // System.err.println("Hashcode: "+seqHash(jds));
+                               // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
+                               // System.err.println(rsq.getName()+"
+                               // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
+                               // System.err.println("Hashcode: "+seqHash(rsq));
+                           }
+                       else
+                           {
+                               vamsasSeq = createVamsasSequence(id, jds);
+                               //          vamsasSet.addSequence(vamsasSeq);
+                               vamsasSet.getSequence().add(vamsasSeq);
+                               vamsasSetIds.put(id, vamsasSeq);
+                               seqRefIds.put(id, jds);
+                           }
+                   }
+               jseq = new JSeq();
+               jseq.setStart(jds.getStart());
+               jseq.setEnd(jds.getEnd());
+               jseq.setColour(av.getSequenceColour(jds).getRGB());
+
+               jseq.setId(id); // jseq id should be a string not a number
+               if (!storeDS)
+                   {
+                       // Store any sequences this sequence represents
+                       if (av.hasHiddenRows())
+                           {
+                               // use rjal, contains the full height alignment
+                               jseq.setHidden(
+                                              av.getAlignment().getHiddenSequences().isHidden(jds));
+
+                               if (av.isHiddenRepSequence(jds))
+                                   {
+                                       jalview.datamodel.SequenceI[] reps = av
+                                           .getRepresentedSequences(jds).getSequencesInOrder(rjal);
+
+                                       for (int h = 0; h < reps.length; h++)
+                                           {
+                                               if (reps[h] != jds)
+                                                   {
+                                                       // jseq.addHiddenSequences(rjal.findIndex(reps[h]));
+                                                       jseq.getHiddenSequences().add(rjal.findIndex(reps[h]));
+                                                   }
+                                           }
+                                   }
+                           }
+                       // mark sequence as reference - if it is the reference for this view
+                       if (jal.hasSeqrep())
+                           {
+                               jseq.setViewreference(jds == jal.getSeqrep());
+                           }
+                   }
+
+               // TODO: omit sequence features from each alignment view's XML dump if we
+               // are storing dataset
+               List<SequenceFeature> sfs = jds.getSequenceFeatures();
+               for (SequenceFeature sf : sfs)
+                   {
+                       // Features features = new Features();
+                       Feature features = new Feature();
+
+                       features.setBegin(sf.getBegin());
+                       features.setEnd(sf.getEnd());
+                       features.setDescription(sf.getDescription());
+                       features.setType(sf.getType());
+                       features.setFeatureGroup(sf.getFeatureGroup());
+                       features.setScore(sf.getScore());
+                       if (sf.links != null)
+                           {
+                               for (int l = 0; l < sf.links.size(); l++)
+                                   {
+                                       OtherData keyValue = new OtherData();
+                                       keyValue.setKey("LINK_" + l);
+                                       keyValue.setValue(sf.links.elementAt(l).toString());
+                                       // features.addOtherData(keyValue);
+                                       features.getOtherData().add(keyValue);
+                                   }
+                           }
+                       if (sf.otherDetails != null)
+                           {
+                               /*
+                                * save feature attributes, which may be simple strings or
+                                * map valued (have sub-attributes)
+                                */
+                               for (Entry<String, Object> entry : sf.otherDetails.entrySet())
+                                   {
+                                       String key = entry.getKey();
+                                       Object value = entry.getValue();
+                                       if (value instanceof Map<?, ?>)
+                                           {
+                                               for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
+                                                        .entrySet())
+                                                   {
+                                                       OtherData otherData = new OtherData();
+                                                       otherData.setKey(key);
+                                                       otherData.setKey2(subAttribute.getKey());
+                                                       otherData.setValue(subAttribute.getValue().toString());
+                                                       // features.addOtherData(otherData);
+                                                       features.getOtherData().add(otherData);
+                                                   }
+                                           }
+                                       else
+                                           {
+                                               OtherData otherData = new OtherData();
+                                               otherData.setKey(key);
+                                               otherData.setValue(value.toString());
+                                               // features.addOtherData(otherData);
+                                               features.getOtherData().add(otherData);
+                                           }
+                                   }
+                           }
+
+                       // jseq.addFeatures(features);
+                       jseq.getFeatures().add(features);
+                   }
+
+               /*
+                * save PDB entries for sequence
+                */
+               if (jdatasq.getAllPDBEntries() != null)
+                   {
+                       Enumeration<PDBEntry> en = jdatasq.getAllPDBEntries().elements();
+                       while (en.hasMoreElements())
+                           {
+                               Pdbids pdb = new Pdbids();
+                               jalview.datamodel.PDBEntry entry = en.nextElement();
+
+                               String pdbId = entry.getId();
+                               pdb.setId(pdbId);
+                               pdb.setType(entry.getType());
+
+                               /*
+                                * Store any structure views associated with this sequence. This
+                                * section copes with duplicate entries in the project, so a dataset
+                                * only view *should* be coped with sensibly.
+                                */
+                               // This must have been loaded, is it still visible?
+                               JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
+                               String matchedFile = null;
+                               for (int f = frames.length - 1; f > -1; f--)
+                                   {
+                                       if (frames[f] instanceof StructureViewerBase)
+                                           {
+                                               StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
+                                               matchedFile = saveStructureViewer(ap, jds, pdb, entry,
+                                                                                 viewIds, matchedFile, viewFrame);
+                                               /*
+                                                * Only store each structure viewer's state once in the project
+                                                * jar. First time through only (storeDS==false)
+                                                */
+                                               String viewId = viewFrame.getViewId();
+                                               String viewerType = viewFrame.getViewerType().toString();
+                                               if (!storeDS && !viewIds.contains(viewId))
+                                                   {
+                                                       viewIds.add(viewId);
+                                                       File viewerState = viewFrame.saveSession();
+                                                       if (viewerState != null)
+                                                           {
+                                                               copyFileToJar(jout, viewerState.getPath(),
+                                                                             getViewerJarEntryName(viewId), viewerType);
+                                                           }
+                                                       else
+                                                           {
+                                                               Console.error(
+                                                                             "Failed to save viewer state for " + viewerType);
+                                                           }
+                                                   }
+                                           }
+                                   }
+
+                               if (matchedFile != null || entry.getFile() != null)
+                                   {
+                                       if (entry.getFile() != null)
+                                           {
+                                               // use entry's file
+                                               matchedFile = entry.getFile();
+                                           }
+                                       pdb.setFile(matchedFile); // entry.getFile());
+                                       if (pdbfiles == null)
+                                           {
+                                               pdbfiles = new ArrayList<>();
+                                           }
+
+                                       if (!pdbfiles.contains(pdbId))
+                                           {
+                                               pdbfiles.add(pdbId);
+                                               copyFileToJar(jout, matchedFile, pdbId, pdbId);
+                                           }
+                                   }
+
+                               Enumeration<String> props = entry.getProperties();
+                               if (props.hasMoreElements())
+                                   {
+                                       // PdbentryItem item = new PdbentryItem();
+                                       while (props.hasMoreElements())
+                                           {
+                                               Property prop = new Property();
+                                               String key = props.nextElement();
+                                               prop.setName(key);
+                                               prop.setValue(entry.getProperty(key).toString());
+                                               // item.addProperty(prop);
+                                               pdb.getProperty().add(prop);
+                                           }
+                                       // pdb.addPdbentryItem(item);
+                                   }
+
+                               // jseq.addPdbids(pdb);
+                               jseq.getPdbids().add(pdb);
+                           }
+                   }
+
+               saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS);
+
+               if (jds.hasHMMProfile())
+                   {
+                       saveHmmerProfile(jout, jseq, jds);
+                   }
+               // jms.addJSeq(jseq);
+               object.getJSeq().add(jseq);
+           }
+
+       if (!storeDS && av.hasHiddenRows())
+           {
+               jal = av.getAlignment();
+           }
+       // SAVE MAPPINGS
+       // FOR DATASET
+       if (storeDS && jal.getCodonFrames() != null)
+           {
+               List<AlignedCodonFrame> jac = jal.getCodonFrames();
+               for (AlignedCodonFrame acf : jac)
+                   {
+                       AlcodonFrame alc = new AlcodonFrame();
+                       if (acf.getProtMappings() != null
+                           && acf.getProtMappings().length > 0)
+                           {
+                               boolean hasMap = false;
+                               SequenceI[] dnas = acf.getdnaSeqs();
+                               jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+                               for (int m = 0; m < pmaps.length; m++)
+                                   {
+                                       AlcodMap alcmap = new AlcodMap();
+                                       alcmap.setDnasq(seqHash(dnas[m]));
+                                       alcmap.setMapping(
+                                                         createVamsasMapping(pmaps[m], dnas[m], null, false));
+                                       // alc.addAlcodMap(alcmap);
+                                       alc.getAlcodMap().add(alcmap);
+                                       hasMap = true;
+                                   }
+                               if (hasMap)
+                                   {
+                                       // vamsasSet.addAlcodonFrame(alc);
+                                       vamsasSet.getAlcodonFrame().add(alc);
+                                   }
+                           }
+                       // TODO: delete this ? dead code from 2.8.3->2.9 ?
+                       // {
+                       // AlcodonFrame alc = new AlcodonFrame();
+                       // vamsasSet.addAlcodonFrame(alc);
+                       // for (int p = 0; p < acf.aaWidth; p++)
+                       // {
+                       // Alcodon cmap = new Alcodon();
+                       // if (acf.codons[p] != null)
+                       // {
+                       // // Null codons indicate a gapped column in the translated peptide
+                       // // alignment.
+                       // cmap.setPos1(acf.codons[p][0]);
+                       // cmap.setPos2(acf.codons[p][1]);
+                       // cmap.setPos3(acf.codons[p][2]);
+                       // }
+                       // alc.addAlcodon(cmap);
+                       // }
+                       // if (acf.getProtMappings() != null
+                       // && acf.getProtMappings().length > 0)
+                       // {
+                       // SequenceI[] dnas = acf.getdnaSeqs();
+                       // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+                       // for (int m = 0; m < pmaps.length; m++)
+                       // {
+                       // AlcodMap alcmap = new AlcodMap();
+                       // alcmap.setDnasq(seqHash(dnas[m]));
+                       // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
+                       // false));
+                       // alc.addAlcodMap(alcmap);
+                       // }
+                       // }
+                   }
+           }
+
+       // SAVE TREES
+       // /////////////////////////////////
+       if (!storeDS && av.getCurrentTree() != null)
+           {
+               // FIND ANY ASSOCIATED TREES
+               // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
+               if (Desktop.getDesktopPane() != null)
+                   {
+                       JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
+
+                       for (int t = 0; t < frames.length; t++)
+                           {
+                               if (frames[t] instanceof TreePanel)
+                                   {
+                                       TreePanel tp = (TreePanel) frames[t];
+
+                                       if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
+                                           {
+                                               JalviewModel.Tree tree = new JalviewModel.Tree();
+                                               tree.setTitle(tp.getTitle());
+                                               tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
+                                               tree.setNewick(tp.getTree().print());
+                                               tree.setThreshold(tp.getTreeCanvas().getThreshold());
+
+                                               tree.setFitToWindow(tp.fitToWindow.getState());
+                                               tree.setFontName(tp.getTreeFont().getName());
+                                               tree.setFontSize(tp.getTreeFont().getSize());
+                                               tree.setFontStyle(tp.getTreeFont().getStyle());
+                                               tree.setMarkUnlinked(tp.placeholdersMenu.getState());
+
+                                               tree.setShowBootstrap(tp.bootstrapMenu.getState());
+                                               tree.setShowDistances(tp.distanceMenu.getState());
+
+                                               tree.setHeight(tp.getHeight());
+                                               tree.setWidth(tp.getWidth());
+                                               tree.setXpos(tp.getX());
+                                               tree.setYpos(tp.getY());
+                                               tree.setId(makeHashCode(tp, null));
+                                               tree.setLinkToAllViews(
+                                                                      tp.getTreeCanvas().isApplyToAllViews());
+
+                                               // jms.addTree(tree);
+                                               object.getTree().add(tree);
+                                           }
+                                   }
+                           }
+                   }
+           }
+
+       /*
+        * save PCA viewers
+        */
+       if (!storeDS && Desktop.getDesktopPane() != null)
+           {
+               for (JInternalFrame frame : Desktop.getDesktopPane().getAllFrames())
+                   {
+                       if (frame instanceof PCAPanel)
+                           {
+                               PCAPanel panel = (PCAPanel) frame;
+                               if (panel.getAlignViewport().getAlignment() == jal)
+                                   {
+                                       savePCA(panel, object);
+                                   }
+                           }
+                   }
+           }
+
+       // SAVE ANNOTATIONS
+       /**
+        * store forward refs from an annotationRow to any groups
+        */
+       IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<>();
+       if (storeDS)
+           {
+               for (SequenceI sq : jal.getSequences())
+                   {
+                       // Store annotation on dataset sequences only
+                       AlignmentAnnotation[] aa = sq.getAnnotation();
+                       if (aa != null && aa.length > 0)
+                           {
+                               storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                                                        vamsasSet);
+                           }
+                   }
+           }
+       else
+           {
+               if (jal.getAlignmentAnnotation() != null)
+                   {
+                       // Store the annotation shown on the alignment.
+                       AlignmentAnnotation[] aa = jal.getAlignmentAnnotation();
+                       storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                                                vamsasSet);
+                   }
+           }
+       // SAVE GROUPS
+       if (jal.getGroups() != null)
+           {
+               JGroup[] groups = new JGroup[jal.getGroups().size()];
+               int i = -1;
+               for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
+                   {
+                       JGroup jGroup = new JGroup();
+                       groups[++i] = jGroup;
+
+                       jGroup.setStart(sg.getStartRes());
+                       jGroup.setEnd(sg.getEndRes());
+                       jGroup.setName(sg.getName());
+                       if (groupRefs.containsKey(sg))
+                           {
+                               // group has references so set its ID field
+                               jGroup.setId(groupRefs.get(sg));
+                           }
+                       ColourSchemeI colourScheme = sg.getColourScheme();
+                       if (colourScheme != null)
+                           {
+                               ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
+                               if (groupColourScheme.conservationApplied())
+                                   {
+                                       jGroup.setConsThreshold(groupColourScheme.getConservationInc());
+
+                                       if (colourScheme instanceof jalview.schemes.UserColourScheme)
+                                           {
+                                               jGroup.setColour(
+                                                                setUserColourScheme(colourScheme, userColours,
+                                                                                    object));
+                                           }
+                                       else
+                                           {
+                                               jGroup.setColour(colourScheme.getSchemeName());
+                                           }
+                                   }
+                               else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
+                                   {
+                                       jGroup.setColour("AnnotationColourGradient");
+                                       jGroup.setAnnotationColours(constructAnnotationColours(
+                                                                                              (jalview.schemes.AnnotationColourGradient) colourScheme,
+                                                                                              userColours, object));
+                                   }
+                               else if (colourScheme instanceof jalview.schemes.UserColourScheme)
+                                   {
+                                       jGroup.setColour(
+                                                        setUserColourScheme(colourScheme, userColours, object));
+                                   }
+                               else
+                                   {
+                                       jGroup.setColour(colourScheme.getSchemeName());
+                                   }
+
+                               jGroup.setPidThreshold(groupColourScheme.getThreshold());
+                           }
+
+                       jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
+                       jGroup.setDisplayBoxes(sg.getDisplayBoxes());
+                       jGroup.setDisplayText(sg.getDisplayText());
+                       jGroup.setColourText(sg.getColourText());
+                       jGroup.setTextCol1(sg.textColour.getRGB());
+                       jGroup.setTextCol2(sg.textColour2.getRGB());
+                       jGroup.setTextColThreshold(sg.thresholdTextColour);
+                       jGroup.setShowUnconserved(sg.getShowNonconserved());
+                       jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
+                       jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram());
+                       jGroup.setShowSequenceLogo(sg.isShowSequenceLogo());
+                       jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
+                       for (SequenceI seq : sg.getSequences())
+                           {
+                               // jGroup.addSeq(seqHash(seq));
+                               jGroup.getSeq().add(seqHash(seq));
+                           }
+                   }
+
+               //jms.setJGroup(groups);
+               Object group;
+               for (JGroup grp : groups)
+                   {
+                       object.getJGroup().add(grp);
+                   }
+           }
+       if (!storeDS)
+           {
+               // /////////SAVE VIEWPORT
+               Viewport view = new Viewport();
+               view.setTitle(ap.alignFrame.getTitle());
+               view.setSequenceSetId(
+                                     makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
+               view.setId(av.getViewId());
+               if (av.getCodingComplement() != null)
+                   {
+                       view.setComplementId(av.getCodingComplement().getViewId());
+                   }
+               view.setViewName(av.getViewName());
+               view.setGatheredViews(av.isGatherViewsHere());
+
+               Rectangle size = ap.av.getExplodedGeometry();
+               Rectangle position = size;
+               if (size == null)
+                   {
+                       size = ap.alignFrame.getBounds();
+                       if (av.getCodingComplement() != null)
+                           {
+                               position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
+                                   .getBounds();
+                           }
+                       else
+                           {
+                               position = size;
+                           }
+                   }
+               view.setXpos(position.x);
+               view.setYpos(position.y);
+
+               view.setWidth(size.width);
+               view.setHeight(size.height);
+
+               view.setStartRes(vpRanges.getStartRes());
+               view.setStartSeq(vpRanges.getStartSeq());
+
+               if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
+                   {
+                       view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
+                                                            userColours, object));
+                   }
+               else if (av
+                        .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+                   {
+                       AnnotationColourScheme ac = constructAnnotationColours(
+                                                                              (jalview.schemes.AnnotationColourGradient) av
+                                                                              .getGlobalColourScheme(),
+                                                                              userColours, object);
+
+                       view.setAnnotationColours(ac);
+                       view.setBgColour("AnnotationColourGradient");
+                   }
+               else
+                   {
+                       view.setBgColour(ColourSchemeProperty
+                                        .getColourName(av.getGlobalColourScheme()));
+                   }
+
+               ResidueShaderI vcs = av.getResidueShading();
+               ColourSchemeI cs = av.getGlobalColourScheme();
+
+               if (cs != null)
+                   {
+                       if (vcs.conservationApplied())
+                           {
+                               view.setConsThreshold(vcs.getConservationInc());
+                               if (cs instanceof jalview.schemes.UserColourScheme)
+                                   {
+                                       view.setBgColour(setUserColourScheme(cs, userColours, object));
+                                   }
+                           }
+                       view.setPidThreshold(vcs.getThreshold());
+                   }
+
+               view.setConservationSelected(av.getConservationSelected());
+               view.setPidSelected(av.getAbovePIDThreshold());
+               final Font font = av.getFont();
+               view.setFontName(font.getName());
+               view.setFontSize(font.getSize());
+               view.setFontStyle(font.getStyle());
+               view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
+               view.setRenderGaps(av.isRenderGaps());
+               view.setShowAnnotation(av.isShowAnnotation());
+               view.setShowBoxes(av.getShowBoxes());
+               view.setShowColourText(av.getColourText());
+               view.setShowFullId(av.getShowJVSuffix());
+               view.setRightAlignIds(av.isRightAlignIds());
+               view.setShowSequenceFeatures(av.isShowSequenceFeatures());
+               view.setShowText(av.getShowText());
+               view.setShowUnconserved(av.getShowUnconserved());
+               view.setWrapAlignment(av.getWrapAlignment());
+               view.setTextCol1(av.getTextColour().getRGB());
+               view.setTextCol2(av.getTextColour2().getRGB());
+               view.setTextColThreshold(av.getThresholdTextColour());
+               view.setShowConsensusHistogram(av.isShowConsensusHistogram());
+               view.setShowSequenceLogo(av.isShowSequenceLogo());
+               view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
+               view.setShowGroupConsensus(av.isShowGroupConsensus());
+               view.setShowGroupConservation(av.isShowGroupConservation());
+               view.setShowNPfeatureTooltip(av.isShowNPFeats());
+               view.setShowDbRefTooltip(av.isShowDBRefs());
+               view.setFollowHighlight(av.isFollowHighlight());
+               view.setFollowSelection(av.followSelection);
+               view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
+               view.setShowComplementFeatures(av.isShowComplementFeatures());
+               view.setShowComplementFeaturesOnTop(
+                                                   av.isShowComplementFeaturesOnTop());
+               if (av.getFeaturesDisplayed() != null)
+                   {
+                       FeatureSettings fs = new FeatureSettings();
+
+                       FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
+                           .getFeatureRenderer();
+                       String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
+
+                       Vector<String> settingsAdded = new Vector<>();
+                       if (renderOrder != null)
+                           {
+                               for (String featureType : renderOrder)
+                                   {
+                                       FeatureSettings.Setting setting = new FeatureSettings.Setting();
+                                       setting.setType(featureType);
+
+                                       /*
+                                        * save any filter for the feature type
+                                        */
+                                       FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+                                       if (filter != null)  {
+                                           Iterator<FeatureMatcherI> filters = filter.getMatchers().iterator();
+                                           FeatureMatcherI firstFilter = filters.next();
+                                           setting.setMatcherSet(Jalview2XML.marshalFilter(
+                                                                                           firstFilter, filters, filter.isAnded()));
+                                       }
+
+                                       /*
+                                        * save colour scheme for the feature type
+                                        */
+                                       FeatureColourI fcol = fr.getFeatureStyle(featureType);
+                                       if (!fcol.isSimpleColour())
+                                           {
+                                               setting.setColour(fcol.getMaxColour().getRGB());
+                                               setting.setMincolour(fcol.getMinColour().getRGB());
+                                               setting.setMin(fcol.getMin());
+                                               setting.setMax(fcol.getMax());
+                                               setting.setColourByLabel(fcol.isColourByLabel());
+                                               if (fcol.isColourByAttribute())
+                                                   {
+                                                       String[] attName = fcol.getAttributeName();
+                                                       setting.getAttributeName().add(attName[0]);
+                                                       if (attName.length > 1)
+                                                           {
+                                                               setting.getAttributeName().add(attName[1]);
+                                                           }
+                                                   }
+                                               setting.setAutoScale(fcol.isAutoScaled());
+                                               setting.setThreshold(fcol.getThreshold());
+                                               Color noColour = fcol.getNoColour();
+                                               if (noColour == null)
+                                                   {
+                                                       setting.setNoValueColour(NoValueColour.NONE);
+                                                   }
+                                               else if (noColour.equals(fcol.getMaxColour()))
+                                                   {
+                                                       setting.setNoValueColour(NoValueColour.MAX);
+                                                   }
+                                               else
+                                                   {
+                                                       setting.setNoValueColour(NoValueColour.MIN);
+                                                   }
+                                               // -1 = No threshold, 0 = Below, 1 = Above
+                                               setting.setThreshstate(fcol.isAboveThreshold() ? 1
+                                                                      : (fcol.isBelowThreshold() ? 0 : -1));
+                                           }
+                                       else
+                                           {
+                                               setting.setColour(fcol.getColour().getRGB());
+                                           }
+
+                                       setting.setDisplay(
+                                                          av.getFeaturesDisplayed().isVisible(featureType));
+                                       float rorder = fr
+                                           .getOrder(featureType);
+                                       if (rorder > -1)
+                                           {
+                                               setting.setOrder(rorder);
+                                           }
+                                       /// fs.addSetting(setting);
+                                       fs.getSetting().add(setting);
+                                       settingsAdded.addElement(featureType);
+                                   }
+                           }
+
+                       // is groups actually supposed to be a map here ?
+                       Iterator<String> en = fr.getFeatureGroups().iterator();
+                       Vector<String> groupsAdded = new Vector<>();
+                       while (en.hasNext())
+                           {
+                               String grp = en.next();
+                               if (groupsAdded.contains(grp))
+                                   {
+                                       continue;
+                                   }
+                               Group g = new Group();
+                               g.setName(grp);
+                               g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
+                                            .booleanValue());
+                               // fs.addGroup(g);
+                               fs.getGroup().add(g);
+                               groupsAdded.addElement(grp);
+                           }
+                       // jms.setFeatureSettings(fs);
+                       object.setFeatureSettings(fs);
+                   }
+
+               if (av.hasHiddenColumns())
+                   {
+                       jalview.datamodel.HiddenColumns hidden = av.getAlignment()
+                           .getHiddenColumns();
+                       if (hidden == null)
+                           {
+                               Console.warn(
+                                            "REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+                           }
+                       else
+                           {
+                               Iterator<int[]> hiddenRegions = hidden.iterator();
+                               while (hiddenRegions.hasNext())
+                                   {
+                                       int[] region = hiddenRegions.next();
+                                       HiddenColumns hc = new HiddenColumns();
+                                       hc.setStart(region[0]);
+                                       hc.setEnd(region[1]);
+                                       // view.addHiddenColumns(hc);
+                                       view.getHiddenColumns().add(hc);
+                                   }
+                           }
+                   }
+               if (calcIdSet.size() > 0)
+                   {
+                       for (String calcId : calcIdSet)
+                           {
+                               if (calcId.trim().length() > 0)
+                                   {
+                                       CalcIdParam cidp = createCalcIdParam(calcId, av);
+                                       // Some calcIds have no parameters.
+                                       if (cidp != null)
+                                           {
+                                               // view.addCalcIdParam(cidp);
+                                               view.getCalcIdParam().add(cidp);
+                                           }
+                                   }
+                           }
+                   }
+
+               // jms.addViewport(view);
+               object.getViewport().add(view);
+           }
+       // object.setJalviewModelSequence(jms);
+       // object.getVamsasModel().addSequenceSet(vamsasSet);
+       object.getVamsasModel().getSequenceSet().add(vamsasSet);
+
+       if (jout != null && fileName != null)
+           {
+               // We may not want to write the object to disk,
+               // eg we can copy the alignViewport to a new view object
+               // using save and then load
+               try
+                   {
+                       fileName = fileName.replace('\\', '/');
+                       System.out.println("Writing jar entry " + fileName);
+                       JarEntry entry = new JarEntry(fileName);
+                       jout.putNextEntry(entry);
+                       PrintWriter pout = new PrintWriter(
+                                                          new OutputStreamWriter(jout, UTF_8));
+                       JAXBContext jaxbContext = JAXBContext
+                           .newInstance(JalviewModel.class);
+                       Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+
+                       // output pretty printed
+                       // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+                       jaxbMarshaller.marshal(
+                                              new ObjectFactory().createJalviewModel(object), pout);
+
+                       // jaxbMarshaller.marshal(object, pout);
+                       // marshaller.marshal(object);
+                       pout.flush();
+                       jout.closeEntry();
+                   } catch (Exception ex)
+                   {
+                       // TODO: raise error in GUI if marshalling failed.
+                       System.err.println("Error writing Jalview project");
+                       ex.printStackTrace();
+                   }
+           }
+       return object;
+    }
+    /**
+     * Saves the HMMER profile associated with the sequence as a file in the jar,
+     * in HMMER format, and saves the name of the file as a child element of the
+     * XML sequence element
+     * 
+     * @param jout
+     * @param xmlSeq
+     * @param seq
+     */
+    protected void saveHmmerProfile(JarOutputStream jout, JSeq xmlSeq,
+                                   SequenceI seq)
+    {
+       HiddenMarkovModel profile = seq.getHMM();
+       if (profile == null)
+           {
+               Console.warn("Want to save HMM profile for " + seq.getName()
+                            + " but none found");
+               return;
+           }
+       HMMFile hmmFile = new HMMFile(profile);
+       String hmmAsString = hmmFile.print();
+       String jarEntryName = HMMER_PREFIX + nextCounter();
+       try
+           {
+               writeJarEntry(jout, jarEntryName, hmmAsString.getBytes());
+               xmlSeq.setHmmerProfile(jarEntryName);
+           } catch (IOException e)
+           {
+               Console.warn("Error saving HMM profile: " + e.getMessage());
+           }
+    }
+
+    
+    /**
+     * Writes PCA viewer attributes and computed values to an XML model object and
+     * adds it to the JalviewModel. Any exceptions are reported by logging.
+     */
+    protected void savePCA(PCAPanel panel, JalviewModel object)
+    {
+       try
+           {
+               PcaViewer viewer = new PcaViewer();
+               viewer.setHeight(panel.getHeight());
+               viewer.setWidth(panel.getWidth());
+               viewer.setXpos(panel.getX());
+               viewer.setYpos(panel.getY());
+               viewer.setTitle(panel.getTitle());
+               PCAModel pcaModel = panel.getPcaModel();
+               viewer.setScoreModelName(pcaModel.getScoreModelName());
+               viewer.setXDim(panel.getSelectedDimensionIndex(X));
+               viewer.setYDim(panel.getSelectedDimensionIndex(Y));
+               viewer.setZDim(panel.getSelectedDimensionIndex(Z));
+               viewer.setBgColour(
+                                  panel.getRotatableCanvas().getBackgroundColour().getRGB());
+               viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
+               float[] spMin = panel.getRotatableCanvas().getSeqMin();
+               SeqPointMin spmin = new SeqPointMin();
+               spmin.setXPos(spMin[0]);
+               spmin.setYPos(spMin[1]);
+               spmin.setZPos(spMin[2]);
+               viewer.setSeqPointMin(spmin);
+               float[] spMax = panel.getRotatableCanvas().getSeqMax();
+               SeqPointMax spmax = new SeqPointMax();
+               spmax.setXPos(spMax[0]);
+               spmax.setYPos(spMax[1]);
+               spmax.setZPos(spMax[2]);
+               viewer.setSeqPointMax(spmax);
+               viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
+               viewer.setLinkToAllViews(
+                                        panel.getRotatableCanvas().isApplyToAllViews());
+               SimilarityParamsI sp = pcaModel.getSimilarityParameters();
+               viewer.setIncludeGaps(sp.includeGaps());
+               viewer.setMatchGaps(sp.matchGaps());
+               viewer.setIncludeGappedColumns(sp.includeGappedColumns());
+               viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
+
+               /*
+                * sequence points on display
+                */
+               for (jalview.datamodel.SequencePoint spt : pcaModel
+                        .getSequencePoints())
+                   {
+                       SequencePoint point = new SequencePoint();
+                       point.setSequenceRef(seqHash(spt.getSequence()));
+                       point.setXPos(spt.coord.x);
+                       point.setYPos(spt.coord.y);
+                       point.setZPos(spt.coord.z);
+                       viewer.getSequencePoint().add(point);
+                   }
+
+               /*
+                * (end points of) axes on display
+                */
+               for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
+                   {
+
+                       Axis axis = new Axis();
+                       axis.setXPos(p.x);
+                       axis.setYPos(p.y);
+                       axis.setZPos(p.z);
+                       viewer.getAxis().add(axis);
+                   }
+
+               /*
+                * raw PCA data (note we are not restoring PCA inputs here -
+                * alignment view, score model, similarity parameters)
+                */
+               PcaDataType data = new PcaDataType();
+               viewer.setPcaData(data);
+               PCA pca = pcaModel.getPcaData();
+
+               DoubleMatrix pm = new DoubleMatrix();
+               saveDoubleMatrix(pca.getPairwiseScores(), pm);
+               data.setPairwiseMatrix(pm);
+
+               DoubleMatrix tm = new DoubleMatrix();
+               saveDoubleMatrix(pca.getTridiagonal(), tm);
+               data.setTridiagonalMatrix(tm);
+
+               DoubleMatrix eigenMatrix = new DoubleMatrix();
+               data.setEigenMatrix(eigenMatrix);
+               saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
+
+               object.getPcaViewer().add(viewer);
+           } catch (Throwable t)
+           {
+               Console.error("Error saving PCA: " + t.getMessage());
+           }
+    }
 
-      @Override
-      boolean resolve()
-      {
-        SequenceI seq = getSrefDatasetSeq();
-        if (seq == null)
-        {
-          return false;
-        }
-        jmap.setTo(seq);
-        return true;
-      }
-    };
-    return fref;
-  }
+    /**
+     * Stores values from a matrix into an XML element, including (if present) the
+     * D or E vectors
+     * 
+     * @param m
+     * @param xmlMatrix
+     * @see #loadDoubleMatrix(DoubleMatrix)
+     */
+    protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
+    {
+       xmlMatrix.setRows(m.height());
+       xmlMatrix.setColumns(m.width());
+       for (int i = 0; i < m.height(); i++)
+           {
+               DoubleVector row = new DoubleVector();
+               for (int j = 0; j < m.width(); j++)
+                   {
+                       row.getV().add(m.getValue(i, j));
+                   }
+               xmlMatrix.getRow().add(row);
+           }
+       if (m.getD() != null)
+           {
+               DoubleVector dVector = new DoubleVector();
+               for (double d : m.getD())
+                   {
+                       dVector.getV().add(d);
+                   }
+               xmlMatrix.setD(dVector);
+           }
+       if (m.getE() != null)
+           {
+               DoubleVector eVector = new DoubleVector();
+               for (double e : m.getE())
+                   {
+                       eVector.getV().add(e);
+                   }
+               xmlMatrix.setE(eVector);
+           }
+    }
 
-  public SeqFref newAlcodMapRef(final String sref,
-          final AlignedCodonFrame _cf,
-          final jalview.datamodel.Mapping _jmap)
-  {
+    /**
+     * Loads XML matrix data into a new Matrix object, including the D and/or E
+     * vectors (if present)
+     * 
+     * @param mData
+     * @return
+     * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
+     */
+    protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
+    {
+       int rows = mData.getRows();
+       double[][] vals = new double[rows][];
+
+       for (int i = 0; i < rows; i++)
+           {
+               List<Double> dVector = mData.getRow().get(i).getV();
+               vals[i] = new double[dVector.size()];
+               int dvi = 0;
+               for (Double d : dVector)
+                   {
+                       vals[i][dvi++] = d;
+                   }
+           }
+
+       MatrixI m = new Matrix(vals);
+
+       if (mData.getD() != null)
+           {
+               List<Double> dVector = mData.getD().getV();
+               double[] vec = new double[dVector.size()];
+               int dvi = 0;
+               for (Double d : dVector)
+                   {
+                       vec[dvi++] = d;
+                   }
+               m.setD(vec);
+           }
+       if (mData.getE() != null)
+           {
+               List<Double> dVector = mData.getE().getV();
+               double[] vec = new double[dVector.size()];
+               int dvi = 0;
+               for (Double d : dVector)
+                   {
+                       vec[dvi++] = d;
+                   }
+               m.setE(vec);
+           }
+
+       return m;
+    }
 
-    SeqFref fref = new SeqFref(sref, "Codon Frame")
-    {
-      AlignedCodonFrame cf = _cf;
+    /**
+     * Save any Varna viewers linked to this sequence. Writes an rnaViewer element
+     * for each viewer, with
+     * <ul>
+     * <li>viewer geometry (position, size, split pane divider location)</li>
+     * <li>index of the selected structure in the viewer (currently shows gapped
+     * or ungapped)</li>
+     * <li>the id of the annotation holding RNA secondary structure</li>
+     * <li>(currently only one SS is shown per viewer, may be more in future)</li>
+     * </ul>
+     * Varna viewer state is also written out (in native Varna XML) to separate
+     * project jar entries. A separate entry is written for each RNA structure
+     * displayed, with the naming convention
+     * <ul>
+     * <li>rna_viewId_sequenceId_annotationId_[gapped|trimmed]</li>
+     * </ul>
+     * 
+     * @param jout
+     * @param jseq
+     * @param jds
+     * @param viewIds
+     * @param ap
+     * @param storeDataset
+     */
+    protected void saveRnaViewers(JarOutputStream jout, JSeq jseq,
+                                 final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
+                                 boolean storeDataset)
+    {
+       if (Desktop.getDesktopPane() == null)
+           {
+               return;
+           }
+       JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
+       for (int f = frames.length - 1; f > -1; f--)
+           {
+               if (frames[f] instanceof AppVarna)
+                   {
+                       AppVarna varna = (AppVarna) frames[f];
+                       /*
+                        * link the sequence to every viewer that is showing it and is linked to
+                        * its alignment panel
+                        */
+                       if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel())
+                           {
+                               String viewId = varna.getViewId();
+                               RnaViewer rna = new RnaViewer();
+                               rna.setViewId(viewId);
+                               rna.setTitle(varna.getTitle());
+                               rna.setXpos(varna.getX());
+                               rna.setYpos(varna.getY());
+                               rna.setWidth(varna.getWidth());
+                               rna.setHeight(varna.getHeight());
+                               rna.setDividerLocation(varna.getDividerLocation());
+                               rna.setSelectedRna(varna.getSelectedIndex());
+                               // jseq.addRnaViewer(rna);
+                               jseq.getRnaViewer().add(rna);
+
+                               /*
+                                * Store each Varna panel's state once in the project per sequence.
+                                * First time through only (storeDataset==false)
+                                */
+                               // boolean storeSessions = false;
+                               // String sequenceViewId = viewId + seqsToIds.get(jds);
+                               // if (!storeDataset && !viewIds.contains(sequenceViewId))
+                               // {
+                               // viewIds.add(sequenceViewId);
+                               // storeSessions = true;
+                               // }
+                               for (RnaModel model : varna.getModels())
+                                   {
+                                       if (model.seq == jds)
+                                           {
+                                               /*
+                                                * VARNA saves each view (sequence or alignment secondary
+                                                * structure, gapped or trimmed) as a separate XML file
+                                                */
+                                               String jarEntryName = rnaSessions.get(model);
+                                               if (jarEntryName == null)
+                                                   {
+
+                                                       String varnaStateFile = varna.getStateInfo(model.rna);
+                                                       jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
+                                                       copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna");
+                                                       rnaSessions.put(model, jarEntryName);
+                                                   }
+                                               SecondaryStructure ss = new SecondaryStructure();
+                                               String annotationId = varna.getAnnotation(jds).annotationId;
+                                               ss.setAnnotationId(annotationId);
+                                               ss.setViewerState(jarEntryName);
+                                               ss.setGapped(model.gapped);
+                                               ss.setTitle(model.title);
+                                               // rna.addSecondaryStructure(ss);
+                                               rna.getSecondaryStructure().add(ss);
+                                           }
+                                   }
+                           }
+                   }
+           }
+    }
 
-      public jalview.datamodel.Mapping mp = _jmap;
+    /**
+     * Copy the contents of a file to a new entry added to the output jar
+     * 
+     * @param jout
+     * @param infilePath
+     * @param jarEntryName
+     * @param msg
+     *          additional identifying info to log to the console
+     */
+    protected void copyFileToJar(JarOutputStream jout, String infilePath,
+                                String jarEntryName, String msg)
+    {
+       try (InputStream is = new FileInputStream(infilePath))
+           {
+               File file = new File(infilePath);
+               if (file.exists() && jout != null)
+                   {
+                       System.out.println(
+                                          "Writing jar entry " + jarEntryName + " (" + msg + ")");
+                       jout.putNextEntry(new JarEntry(jarEntryName));
+                       copyAll(is, jout);
+                       jout.closeEntry();
+                       // dis = new DataInputStream(new FileInputStream(file));
+                       // byte[] data = new byte[(int) file.length()];
+                       // dis.readFully(data);
+                       // writeJarEntry(jout, jarEntryName, data);
+                   }
+           } catch (Exception ex)
+           {
+               ex.printStackTrace();
+           }
+    }
 
-      @Override
-      public boolean isResolvable()
-      {
-        return super.isResolvable() && mp.getTo() != null;
-      }
-
-      @Override
-      boolean resolve()
-      {
-        SequenceI seq = getSrefDatasetSeq();
-        if (seq == null)
-        {
-          return false;
-        }
-        cf.addMap(seq, mp.getTo(), mp.getMap());
-        return true;
-      }
-    };
-    return fref;
-  }
-
-  public void resolveFrefedSequences()
-  {
-    Iterator<SeqFref> nextFref = frefedSequence.iterator();
-    int toresolve = frefedSequence.size();
-    int unresolved = 0, failedtoresolve = 0;
-    while (nextFref.hasNext())
-    {
-      SeqFref ref = nextFref.next();
-      if (ref.isResolvable())
-      {
-        try
-        {
-          if (ref.resolve())
-          {
-            nextFref.remove();
-          }
-          else
-          {
-            failedtoresolve++;
-          }
-        } catch (Exception x)
-        {
-          System.err.println(
-                  "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
-                          + ref.getSref());
-          x.printStackTrace();
-          failedtoresolve++;
-        }
-      }
-      else
-      {
-        unresolved++;
-      }
-    }
-    if (unresolved > 0)
-    {
-      System.err.println("Jalview Project Import: There were " + unresolved
-              + " forward references left unresolved on the stack.");
-    }
-    if (failedtoresolve > 0)
-    {
-      System.err.println("SERIOUS! " + failedtoresolve
-              + " resolvable forward references failed to resolve.");
-    }
-    if (incompleteSeqs != null && incompleteSeqs.size() > 0)
-    {
-      System.err.println(
-              "Jalview Project Import: There are " + incompleteSeqs.size()
-                      + " sequences which may have incomplete metadata.");
-      if (incompleteSeqs.size() < 10)
-      {
-        for (SequenceI s : incompleteSeqs.values())
-        {
-          System.err.println(s.toString());
-        }
-      }
-      else
-      {
-        System.err.println(
-                "Too many to report. Skipping output of incomplete sequences.");
-      }
-    }
-  }
-
-  /**
-   * This maintains a map of viewports, the key being the seqSetId. Important to
-   * set historyItem and redoList for multiple views
-   */
-  Map<String, AlignViewport> viewportsAdded = new HashMap<>();
-
-  Map<String, AlignmentAnnotation> annotationIds = new HashMap<>();
-
-  String uniqueSetSuffix = "";
-
-  /**
-   * List of pdbfiles added to Jar
-   */
-  List<String> pdbfiles = null;
-
-  // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
-  public void saveState(File statefile)
-  {
-    FileOutputStream fos = null;
-
-    try
-    {
-
-      fos = new FileOutputStream(statefile);
-
-      JarOutputStream jout = new JarOutputStream(fos);
-      saveState(jout);
-      fos.close();
-
-    } catch (Exception e)
-    {
-      Console.error("Couln't write Jalview state to " + statefile, e);
-      // TODO: inform user of the problem - they need to know if their data was
-      // not saved !
-      if (errorMessage == null)
-      {
-        errorMessage = "Did't write Jalview Archive to output file '"
-                + statefile + "' - See console error log for details";
-      }
-      else
-      {
-        errorMessage += "(Didn't write Jalview Archive to output file '"
-                + statefile + ")";
-      }
-      e.printStackTrace();
-    } finally
-    {
-      if (fos != null)
-      {
-        try
-        {
-          fos.close();
-        } catch (IOException e)
-        {
-          // ignore
-        }
-      }
-    }
-    reportErrors();
-  }
-
-  /**
-   * Writes a jalview project archive to the given Jar output stream.
-   * 
-   * @param jout
-   */
-  public void saveState(JarOutputStream jout)
-  {
-    AlignFrame[] frames = Desktop.getAlignFrames();
-
-    if (frames == null)
-    {
-      return;
-    }
-    saveAllFrames(Arrays.asList(frames), jout);
-  }
-
-  /**
-   * core method for storing state for a set of AlignFrames.
-   * 
-   * @param frames
-   *          - frames involving all data to be exported (including containing
-   *          splitframes)
-   * @param jout
-   *          - project output stream
-   */
-  private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
-  {
-    Hashtable<String, AlignFrame> dsses = new Hashtable<>();
-
-    /*
-     * ensure cached data is clear before starting
-     */
-    // todo tidy up seqRefIds, seqsToIds initialisation / reset
-    rnaSessions.clear();
-    splitFrameCandidates.clear();
-
-    try
-    {
-
-      // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
-      // //////////////////////////////////////////////////
-
-      List<String> shortNames = new ArrayList<>();
-      List<String> viewIds = new ArrayList<>();
-
-      // REVERSE ORDER
-      for (int i = frames.size() - 1; i > -1; i--)
-      {
-        AlignFrame af = frames.get(i);
-        // skip ?
-        if (skipList != null && skipList
-                .containsKey(af.getViewport().getSequenceSetId()))
-        {
-          continue;
-        }
-
-        String shortName = makeFilename(af, shortNames);
-
-        int apSize = af.getAlignPanels().size();
-
-        for (int ap = 0; ap < apSize; ap++)
-        {
-          AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
-                  .get(ap);
-          String fileName = apSize == 1 ? shortName : ap + shortName;
-          if (!fileName.endsWith(".xml"))
-          {
-            fileName = fileName + ".xml";
-          }
-
-          saveState(apanel, fileName, jout, viewIds);
-
-          String dssid = getDatasetIdRef(
-                  af.getViewport().getAlignment().getDataset());
-          if (!dsses.containsKey(dssid))
-          {
-            dsses.put(dssid, af);
-          }
-        }
-      }
-
-      writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
-              jout);
-
-      try
-      {
-        jout.flush();
-      } catch (Exception foo)
-      {
-      }
-      jout.close();
-    } catch (Exception ex)
-    {
-      // TODO: inform user of the problem - they need to know if their data was
-      // not saved !
-      if (errorMessage == null)
-      {
-        errorMessage = "Couldn't write Jalview Archive - see error output for details";
-      }
-      ex.printStackTrace();
-    }
-  }
-
-  /**
-   * Generates a distinct file name, based on the title of the AlignFrame, by
-   * appending _n for increasing n until an unused name is generated. The new
-   * name (without its extension) is added to the list.
-   * 
-   * @param af
-   * @param namesUsed
-   * @return the generated name, with .xml extension
-   */
-  protected String makeFilename(AlignFrame af, List<String> namesUsed)
-  {
-    String shortName = af.getTitle();
-
-    if (shortName.indexOf(File.separatorChar) > -1)
-    {
-      shortName = shortName
-              .substring(shortName.lastIndexOf(File.separatorChar) + 1);
-    }
-
-    int count = 1;
-
-    while (namesUsed.contains(shortName))
-    {
-      if (shortName.endsWith("_" + (count - 1)))
-      {
-        shortName = shortName.substring(0, shortName.lastIndexOf("_"));
-      }
-
-      shortName = shortName.concat("_" + count);
-      count++;
-    }
-
-    namesUsed.add(shortName);
-
-    if (!shortName.endsWith(".xml"))
-    {
-      shortName = shortName + ".xml";
-    }
-    return shortName;
-  }
-
-  // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
-  public boolean saveAlignment(AlignFrame af, String jarFile,
-          String fileName)
-  {
-    try
-    {
-      // create backupfiles object and get new temp filename destination
-      boolean doBackup = BackupFiles.getEnabled();
-      BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
-      FileOutputStream fos = new FileOutputStream(
-              doBackup ? backupfiles.getTempFilePath() : jarFile);
-
-      JarOutputStream jout = new JarOutputStream(fos);
-      List<AlignFrame> frames = new ArrayList<>();
-
-      // resolve splitframes
-      if (af.getViewport().getCodingComplement() != null)
-      {
-        frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
-      }
-      else
-      {
-        frames.add(af);
-      }
-      saveAllFrames(frames, jout);
-      try
-      {
-        jout.flush();
-      } catch (Exception foo)
-      {
-      }
-      jout.close();
-      boolean success = true;
-
-      if (doBackup)
-      {
-        backupfiles.setWriteSuccess(success);
-        success = backupfiles.rollBackupsAndRenameTempFile();
-      }
-
-      return success;
-    } catch (Exception ex)
-    {
-      errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
-      ex.printStackTrace();
-      return false;
-    }
-  }
-
-  private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
-          String fileName, JarOutputStream jout)
-  {
-
-    for (String dssids : dsses.keySet())
-    {
-      AlignFrame _af = dsses.get(dssids);
-      String jfileName = fileName + " Dataset for " + _af.getTitle();
-      if (!jfileName.endsWith(".xml"))
-      {
-        jfileName = jfileName + ".xml";
-      }
-      saveState(_af.alignPanel, jfileName, true, jout, null);
-    }
-  }
-
-  /**
-   * create a JalviewModel from an alignment view and marshall it to a
-   * JarOutputStream
-   * 
-   * @param ap
-   *          panel to create jalview model for
-   * @param fileName
-   *          name of alignment panel written to output stream
-   * @param jout
-   *          jar output stream
-   * @param viewIds
-   * @param out
-   *          jar entry name
-   */
-  public JalviewModel saveState(AlignmentPanel ap, String fileName,
-          JarOutputStream jout, List<String> viewIds)
-  {
-    return saveState(ap, fileName, false, jout, viewIds);
-  }
-
-  /**
-   * create a JalviewModel from an alignment view and marshall it to a
-   * JarOutputStream
-   * 
-   * @param ap
-   *          panel to create jalview model for
-   * @param fileName
-   *          name of alignment panel written to output stream
-   * @param storeDS
-   *          when true, only write the dataset for the alignment, not the data
-   *          associated with the view.
-   * @param jout
-   *          jar output stream
-   * @param out
-   *          jar entry name
-   */
-  public JalviewModel saveState(AlignmentPanel ap, String fileName,
-          boolean storeDS, JarOutputStream jout, List<String> viewIds)
-  {
-    if (viewIds == null)
-    {
-      viewIds = new ArrayList<>();
-    }
-
-    initSeqRefs();
-
-    List<UserColourScheme> userColours = new ArrayList<>();
-
-    AlignViewport av = ap.av;
-    ViewportRanges vpRanges = av.getRanges();
-
-    final ObjectFactory objectFactory = new ObjectFactory();
-    JalviewModel object = objectFactory.createJalviewModel();
-    object.setVamsasModel(new VAMSAS());
-
-    // object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
-    try
-    {
-      GregorianCalendar c = new GregorianCalendar();
-      DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
-      XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar);
-      object.setCreationDate(now);
-    } catch (DatatypeConfigurationException e)
-    {
-      System.err.println("error writing date: " + e.toString());
-    }
-    object.setVersion(Cache.getDefault("VERSION", "Development Build"));
-
-    /**
-     * rjal is full height alignment, jal is actual alignment with full metadata
-     * but excludes hidden sequences.
-     */
-    jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
-
-    if (av.hasHiddenRows())
-    {
-      rjal = jal.getHiddenSequences().getFullAlignment();
-    }
-
-    SequenceSet vamsasSet = new SequenceSet();
-    Sequence vamsasSeq;
-    // JalviewModelSequence jms = new JalviewModelSequence();
-
-    vamsasSet.setGapChar(jal.getGapCharacter() + "");
-
-    if (jal.getDataset() != null)
-    {
-      // dataset id is the dataset's hashcode
-      vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
-      if (storeDS)
-      {
-        // switch jal and the dataset
-        jal = jal.getDataset();
-        rjal = jal;
-      }
-    }
-    if (jal.getProperties() != null)
-    {
-      Enumeration en = jal.getProperties().keys();
-      while (en.hasMoreElements())
-      {
-        String key = en.nextElement().toString();
-        SequenceSetProperties ssp = new SequenceSetProperties();
-        ssp.setKey(key);
-        ssp.setValue(jal.getProperties().get(key).toString());
-        // vamsasSet.addSequenceSetProperties(ssp);
-        vamsasSet.getSequenceSetProperties().add(ssp);
-      }
-    }
-
-    JSeq jseq;
-    Set<String> calcIdSet = new HashSet<>();
-    // record the set of vamsas sequence XML POJO we create.
-    HashMap<String, Sequence> vamsasSetIds = new HashMap<>();
-    // SAVE SEQUENCES
-    for (final SequenceI jds : rjal.getSequences())
-    {
-      final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
-              : jds.getDatasetSequence();
-      String id = seqHash(jds);
-      if (vamsasSetIds.get(id) == null)
-      {
-        if (seqRefIds.get(id) != null && !storeDS)
-        {
-          // This happens for two reasons: 1. multiple views are being
-          // serialised.
-          // 2. the hashCode has collided with another sequence's code. This
-          // DOES
-          // HAPPEN! (PF00072.15.stk does this)
-          // JBPNote: Uncomment to debug writing out of files that do not read
-          // back in due to ArrayOutOfBoundExceptions.
-          // System.err.println("vamsasSeq backref: "+id+"");
-          // System.err.println(jds.getName()+"
-          // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
-          // System.err.println("Hashcode: "+seqHash(jds));
-          // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
-          // System.err.println(rsq.getName()+"
-          // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
-          // System.err.println("Hashcode: "+seqHash(rsq));
-        }
-        else
-        {
-          vamsasSeq = createVamsasSequence(id, jds);
-          // vamsasSet.addSequence(vamsasSeq);
-          vamsasSet.getSequence().add(vamsasSeq);
-          vamsasSetIds.put(id, vamsasSeq);
-          seqRefIds.put(id, jds);
-        }
-      }
-      jseq = new JSeq();
-      jseq.setStart(jds.getStart());
-      jseq.setEnd(jds.getEnd());
-      jseq.setColour(av.getSequenceColour(jds).getRGB());
-
-      jseq.setId(id); // jseq id should be a string not a number
-      if (!storeDS)
-      {
-        // Store any sequences this sequence represents
-        if (av.hasHiddenRows())
-        {
-          // use rjal, contains the full height alignment
-          jseq.setHidden(
-                  av.getAlignment().getHiddenSequences().isHidden(jds));
-
-          if (av.isHiddenRepSequence(jds))
-          {
-            jalview.datamodel.SequenceI[] reps = av
-                    .getRepresentedSequences(jds).getSequencesInOrder(rjal);
-
-            for (int h = 0; h < reps.length; h++)
-            {
-              if (reps[h] != jds)
-              {
-                // jseq.addHiddenSequences(rjal.findIndex(reps[h]));
-                jseq.getHiddenSequences().add(rjal.findIndex(reps[h]));
-              }
-            }
-          }
-        }
-        // mark sequence as reference - if it is the reference for this view
-        if (jal.hasSeqrep())
-        {
-          jseq.setViewreference(jds == jal.getSeqrep());
-        }
-      }
-
-      // TODO: omit sequence features from each alignment view's XML dump if we
-      // are storing dataset
-      List<SequenceFeature> sfs = jds.getSequenceFeatures();
-      for (SequenceFeature sf : sfs)
-      {
-        // Features features = new Features();
-        Feature features = new Feature();
-
-        features.setBegin(sf.getBegin());
-        features.setEnd(sf.getEnd());
-        features.setDescription(sf.getDescription());
-        features.setType(sf.getType());
-        features.setFeatureGroup(sf.getFeatureGroup());
-        features.setScore(sf.getScore());
-        if (sf.links != null)
-        {
-          for (int l = 0; l < sf.links.size(); l++)
-          {
-            OtherData keyValue = new OtherData();
-            keyValue.setKey("LINK_" + l);
-            keyValue.setValue(sf.links.elementAt(l).toString());
-            // features.addOtherData(keyValue);
-            features.getOtherData().add(keyValue);
-          }
-        }
-        if (sf.otherDetails != null)
-        {
-          /*
-           * save feature attributes, which may be simple strings or
-           * map valued (have sub-attributes)
-           */
-          for (Entry<String, Object> entry : sf.otherDetails.entrySet())
-          {
-            String key = entry.getKey();
-            Object value = entry.getValue();
-            if (value instanceof Map<?, ?>)
-            {
-              for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
-                      .entrySet())
-              {
-                OtherData otherData = new OtherData();
-                otherData.setKey(key);
-                otherData.setKey2(subAttribute.getKey());
-                otherData.setValue(subAttribute.getValue().toString());
-                // features.addOtherData(otherData);
-                features.getOtherData().add(otherData);
-              }
-            }
-            else
-            {
-              OtherData otherData = new OtherData();
-              otherData.setKey(key);
-              otherData.setValue(value.toString());
-              // features.addOtherData(otherData);
-              features.getOtherData().add(otherData);
-            }
-          }
-        }
-
-        // jseq.addFeatures(features);
-        jseq.getFeatures().add(features);
-      }
-
-      if (jdatasq.getAllPDBEntries() != null)
-      {
-        Enumeration<PDBEntry> en = jdatasq.getAllPDBEntries().elements();
-        while (en.hasMoreElements())
-        {
-          Pdbids pdb = new Pdbids();
-          jalview.datamodel.PDBEntry entry = en.nextElement();
-
-          String pdbId = entry.getId();
-          pdb.setId(pdbId);
-          pdb.setType(entry.getType());
-
-          /*
-           * Store any structure views associated with this sequence. This
-           * section copes with duplicate entries in the project, so a dataset
-           * only view *should* be coped with sensibly.
-           */
-          // This must have been loaded, is it still visible?
-          JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-          String matchedFile = null;
-          for (int f = frames.length - 1; f > -1; f--)
-          {
-            if (frames[f] instanceof StructureViewerBase)
-            {
-              StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
-              matchedFile = saveStructureViewer(ap, jds, pdb, entry,
-                      viewIds, matchedFile, viewFrame);
-              /*
-               * Only store each structure viewer's state once in the project
-               * jar. First time through only (storeDS==false)
-               */
-              String viewId = viewFrame.getViewId();
-              String viewerType = viewFrame.getViewerType().toString();
-              if (!storeDS && !viewIds.contains(viewId))
-              {
-                viewIds.add(viewId);
-                File viewerState = viewFrame.saveSession();
-                if (viewerState != null)
-                {
-                  copyFileToJar(jout, viewerState.getPath(),
-                          getViewerJarEntryName(viewId), viewerType);
-                }
-                else
-                {
-                  Console.error(
-                          "Failed to save viewer state for " + viewerType);
-                }
-              }
-            }
-          }
-
-          if (matchedFile != null || entry.getFile() != null)
-          {
-            if (entry.getFile() != null)
-            {
-              // use entry's file
-              matchedFile = entry.getFile();
-            }
-            pdb.setFile(matchedFile); // entry.getFile());
-            if (pdbfiles == null)
-            {
-              pdbfiles = new ArrayList<>();
-            }
-
-            if (!pdbfiles.contains(pdbId))
-            {
-              pdbfiles.add(pdbId);
-              copyFileToJar(jout, matchedFile, pdbId, pdbId);
-            }
-          }
-
-          Enumeration<String> props = entry.getProperties();
-          if (props.hasMoreElements())
-          {
-            // PdbentryItem item = new PdbentryItem();
-            while (props.hasMoreElements())
-            {
-              Property prop = new Property();
-              String key = props.nextElement();
-              prop.setName(key);
-              prop.setValue(entry.getProperty(key).toString());
-              // item.addProperty(prop);
-              pdb.getProperty().add(prop);
-            }
-            // pdb.addPdbentryItem(item);
-          }
-
-          // jseq.addPdbids(pdb);
-          jseq.getPdbids().add(pdb);
-        }
-      }
-
-      saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS);
-
-      // jms.addJSeq(jseq);
-      object.getJSeq().add(jseq);
-    }
-
-    if (!storeDS && av.hasHiddenRows())
-    {
-      jal = av.getAlignment();
-    }
-    // SAVE MAPPINGS
-    // FOR DATASET
-    if (storeDS && jal.getCodonFrames() != null)
-    {
-      List<AlignedCodonFrame> jac = jal.getCodonFrames();
-      for (AlignedCodonFrame acf : jac)
-      {
-        AlcodonFrame alc = new AlcodonFrame();
-        if (acf.getProtMappings() != null
-                && acf.getProtMappings().length > 0)
-        {
-          boolean hasMap = false;
-          SequenceI[] dnas = acf.getdnaSeqs();
-          jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
-          for (int m = 0; m < pmaps.length; m++)
-          {
-            AlcodMap alcmap = new AlcodMap();
-            alcmap.setDnasq(seqHash(dnas[m]));
-            alcmap.setMapping(
-                    createVamsasMapping(pmaps[m], dnas[m], null, false));
-            // alc.addAlcodMap(alcmap);
-            alc.getAlcodMap().add(alcmap);
-            hasMap = true;
-          }
-          if (hasMap)
-          {
-            // vamsasSet.addAlcodonFrame(alc);
-            vamsasSet.getAlcodonFrame().add(alc);
-          }
-        }
-        // TODO: delete this ? dead code from 2.8.3->2.9 ?
-        // {
-        // AlcodonFrame alc = new AlcodonFrame();
-        // vamsasSet.addAlcodonFrame(alc);
-        // for (int p = 0; p < acf.aaWidth; p++)
-        // {
-        // Alcodon cmap = new Alcodon();
-        // if (acf.codons[p] != null)
-        // {
-        // // Null codons indicate a gapped column in the translated peptide
-        // // alignment.
-        // cmap.setPos1(acf.codons[p][0]);
-        // cmap.setPos2(acf.codons[p][1]);
-        // cmap.setPos3(acf.codons[p][2]);
-        // }
-        // alc.addAlcodon(cmap);
-        // }
-        // if (acf.getProtMappings() != null
-        // && acf.getProtMappings().length > 0)
-        // {
-        // SequenceI[] dnas = acf.getdnaSeqs();
-        // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
-        // for (int m = 0; m < pmaps.length; m++)
-        // {
-        // AlcodMap alcmap = new AlcodMap();
-        // alcmap.setDnasq(seqHash(dnas[m]));
-        // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
-        // false));
-        // alc.addAlcodMap(alcmap);
-        // }
-        // }
-      }
-    }
-
-    // SAVE TREES
-    // /////////////////////////////////
-    if (!storeDS && av.getCurrentTree() != null)
-    {
-      // FIND ANY ASSOCIATED TREES
-      // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
-      if (Desktop.desktop != null)
-      {
-        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-
-        for (int t = 0; t < frames.length; t++)
-        {
-          if (frames[t] instanceof TreePanel)
-          {
-            TreePanel tp = (TreePanel) frames[t];
-
-            if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
-            {
-              JalviewModel.Tree tree = new JalviewModel.Tree();
-              tree.setTitle(tp.getTitle());
-              tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
-              tree.setNewick(tp.getTree().print());
-              tree.setThreshold(tp.getTreeCanvas().getThreshold());
-
-              tree.setFitToWindow(tp.fitToWindow.getState());
-              tree.setFontName(tp.getTreeFont().getName());
-              tree.setFontSize(tp.getTreeFont().getSize());
-              tree.setFontStyle(tp.getTreeFont().getStyle());
-              tree.setMarkUnlinked(tp.placeholdersMenu.getState());
-
-              tree.setShowBootstrap(tp.bootstrapMenu.getState());
-              tree.setShowDistances(tp.distanceMenu.getState());
-
-              tree.setHeight(tp.getHeight());
-              tree.setWidth(tp.getWidth());
-              tree.setXpos(tp.getX());
-              tree.setYpos(tp.getY());
-              tree.setId(makeHashCode(tp, null));
-              tree.setLinkToAllViews(
-                      tp.getTreeCanvas().isApplyToAllViews());
-
-              // jms.addTree(tree);
-              object.getTree().add(tree);
-            }
-          }
-        }
-      }
-    }
-
-    /*
-     * save PCA viewers
-     */
-    if (!storeDS && Desktop.desktop != null)
-    {
-      for (JInternalFrame frame : Desktop.desktop.getAllFrames())
-      {
-        if (frame instanceof PCAPanel)
-        {
-          PCAPanel panel = (PCAPanel) frame;
-          if (panel.getAlignViewport().getAlignment() == jal)
-          {
-            savePCA(panel, object);
-          }
-        }
-      }
-    }
-
-    // SAVE ANNOTATIONS
-    /**
-     * store forward refs from an annotationRow to any groups
-     */
-    IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<>();
-    if (storeDS)
-    {
-      for (SequenceI sq : jal.getSequences())
-      {
-        // Store annotation on dataset sequences only
-        AlignmentAnnotation[] aa = sq.getAnnotation();
-        if (aa != null && aa.length > 0)
-        {
-          storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
-                  vamsasSet);
-        }
-      }
-    }
-    else
-    {
-      if (jal.getAlignmentAnnotation() != null)
-      {
-        // Store the annotation shown on the alignment.
-        AlignmentAnnotation[] aa = jal.getAlignmentAnnotation();
-        storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
-                vamsasSet);
-      }
-    }
-    // SAVE GROUPS
-    if (jal.getGroups() != null)
-    {
-      JGroup[] groups = new JGroup[jal.getGroups().size()];
-      int i = -1;
-      for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
-      {
-        JGroup jGroup = new JGroup();
-        groups[++i] = jGroup;
-
-        jGroup.setStart(sg.getStartRes());
-        jGroup.setEnd(sg.getEndRes());
-        jGroup.setName(sg.getName());
-        if (groupRefs.containsKey(sg))
-        {
-          // group has references so set its ID field
-          jGroup.setId(groupRefs.get(sg));
-        }
-        ColourSchemeI colourScheme = sg.getColourScheme();
-        if (colourScheme != null)
-        {
-          ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
-          if (groupColourScheme.conservationApplied())
-          {
-            jGroup.setConsThreshold(groupColourScheme.getConservationInc());
-
-            if (colourScheme instanceof jalview.schemes.UserColourScheme)
-            {
-              jGroup.setColour(setUserColourScheme(colourScheme,
-                      userColours, object));
-            }
-            else
-            {
-              jGroup.setColour(colourScheme.getSchemeName());
-            }
-          }
-          else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
-          {
-            jGroup.setColour("AnnotationColourGradient");
-            jGroup.setAnnotationColours(constructAnnotationColours(
-                    (jalview.schemes.AnnotationColourGradient) colourScheme,
-                    userColours, object));
-          }
-          else if (colourScheme instanceof jalview.schemes.UserColourScheme)
-          {
-            jGroup.setColour(
-                    setUserColourScheme(colourScheme, userColours, object));
-          }
-          else
-          {
-            jGroup.setColour(colourScheme.getSchemeName());
-          }
-
-          jGroup.setPidThreshold(groupColourScheme.getThreshold());
-        }
-
-        jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
-        jGroup.setDisplayBoxes(sg.getDisplayBoxes());
-        jGroup.setDisplayText(sg.getDisplayText());
-        jGroup.setColourText(sg.getColourText());
-        jGroup.setTextCol1(sg.textColour.getRGB());
-        jGroup.setTextCol2(sg.textColour2.getRGB());
-        jGroup.setTextColThreshold(sg.thresholdTextColour);
-        jGroup.setShowUnconserved(sg.getShowNonconserved());
-        jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
-        jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram());
-        jGroup.setShowSequenceLogo(sg.isShowSequenceLogo());
-        jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
-        for (SequenceI seq : sg.getSequences())
-        {
-          // jGroup.addSeq(seqHash(seq));
-          jGroup.getSeq().add(seqHash(seq));
-        }
-      }
-
-      // jms.setJGroup(groups);
-      Object group;
-      for (JGroup grp : groups)
-      {
-        object.getJGroup().add(grp);
-      }
-    }
-    if (!storeDS)
-    {
-      // /////////SAVE VIEWPORT
-      Viewport view = new Viewport();
-      view.setTitle(ap.alignFrame.getTitle());
-      view.setSequenceSetId(
-              makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
-      view.setId(av.getViewId());
-      if (av.getCodingComplement() != null)
-      {
-        view.setComplementId(av.getCodingComplement().getViewId());
-      }
-      view.setViewName(av.getViewName());
-      view.setGatheredViews(av.isGatherViewsHere());
-
-      Rectangle size = ap.av.getExplodedGeometry();
-      Rectangle position = size;
-      if (size == null)
-      {
-        size = ap.alignFrame.getBounds();
-        if (av.getCodingComplement() != null)
-        {
-          position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
-                  .getBounds();
-        }
-        else
-        {
-          position = size;
-        }
-      }
-      view.setXpos(position.x);
-      view.setYpos(position.y);
-
-      view.setWidth(size.width);
-      view.setHeight(size.height);
-
-      view.setStartRes(vpRanges.getStartRes());
-      view.setStartSeq(vpRanges.getStartSeq());
-
-      if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
-      {
-        view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
-                userColours, object));
-      }
-      else if (av
-              .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
-      {
-        AnnotationColourScheme ac = constructAnnotationColours(
-                (jalview.schemes.AnnotationColourGradient) av
-                        .getGlobalColourScheme(),
-                userColours, object);
-
-        view.setAnnotationColours(ac);
-        view.setBgColour("AnnotationColourGradient");
-      }
-      else
-      {
-        view.setBgColour(ColourSchemeProperty
-                .getColourName(av.getGlobalColourScheme()));
-      }
-
-      ResidueShaderI vcs = av.getResidueShading();
-      ColourSchemeI cs = av.getGlobalColourScheme();
-
-      if (cs != null)
-      {
-        if (vcs.conservationApplied())
-        {
-          view.setConsThreshold(vcs.getConservationInc());
-          if (cs instanceof jalview.schemes.UserColourScheme)
-          {
-            view.setBgColour(setUserColourScheme(cs, userColours, object));
-          }
-        }
-        view.setPidThreshold(vcs.getThreshold());
-      }
-
-      view.setConservationSelected(av.getConservationSelected());
-      view.setPidSelected(av.getAbovePIDThreshold());
-      final Font font = av.getFont();
-      view.setFontName(font.getName());
-      view.setFontSize(font.getSize());
-      view.setFontStyle(font.getStyle());
-      view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
-      view.setRenderGaps(av.isRenderGaps());
-      view.setShowAnnotation(av.isShowAnnotation());
-      view.setShowBoxes(av.getShowBoxes());
-      view.setShowColourText(av.getColourText());
-      view.setShowFullId(av.getShowJVSuffix());
-      view.setRightAlignIds(av.isRightAlignIds());
-      view.setShowSequenceFeatures(av.isShowSequenceFeatures());
-      view.setShowText(av.getShowText());
-      view.setShowUnconserved(av.getShowUnconserved());
-      view.setWrapAlignment(av.getWrapAlignment());
-      view.setTextCol1(av.getTextColour().getRGB());
-      view.setTextCol2(av.getTextColour2().getRGB());
-      view.setTextColThreshold(av.getThresholdTextColour());
-      view.setShowConsensusHistogram(av.isShowConsensusHistogram());
-      view.setShowSequenceLogo(av.isShowSequenceLogo());
-      view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
-      view.setShowGroupConsensus(av.isShowGroupConsensus());
-      view.setShowGroupConservation(av.isShowGroupConservation());
-      view.setShowNPfeatureTooltip(av.isShowNPFeats());
-      view.setShowDbRefTooltip(av.isShowDBRefs());
-      view.setFollowHighlight(av.isFollowHighlight());
-      view.setFollowSelection(av.followSelection);
-      view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
-      view.setShowComplementFeatures(av.isShowComplementFeatures());
-      view.setShowComplementFeaturesOnTop(
-              av.isShowComplementFeaturesOnTop());
-      if (av.getFeaturesDisplayed() != null)
-      {
-        FeatureSettings fs = new FeatureSettings();
-
-        FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
-                .getFeatureRenderer();
-        String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
-
-        Vector<String> settingsAdded = new Vector<>();
-        if (renderOrder != null)
-        {
-          for (String featureType : renderOrder)
-          {
-            FeatureSettings.Setting setting = new FeatureSettings.Setting();
-            setting.setType(featureType);
-
-            /*
-             * save any filter for the feature type
-             */
-            FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
-            if (filter != null)
-            {
-              Iterator<FeatureMatcherI> filters = filter.getMatchers()
-                      .iterator();
-              FeatureMatcherI firstFilter = filters.next();
-              setting.setMatcherSet(Jalview2XML.marshalFilter(firstFilter,
-                      filters, filter.isAnded()));
-            }
-
-            /*
-             * save colour scheme for the feature type
-             */
-            FeatureColourI fcol = fr.getFeatureStyle(featureType);
-            if (!fcol.isSimpleColour())
-            {
-              setting.setColour(fcol.getMaxColour().getRGB());
-              setting.setMincolour(fcol.getMinColour().getRGB());
-              setting.setMin(fcol.getMin());
-              setting.setMax(fcol.getMax());
-              setting.setColourByLabel(fcol.isColourByLabel());
-              if (fcol.isColourByAttribute())
-              {
-                String[] attName = fcol.getAttributeName();
-                setting.getAttributeName().add(attName[0]);
-                if (attName.length > 1)
-                {
-                  setting.getAttributeName().add(attName[1]);
-                }
-              }
-              setting.setAutoScale(fcol.isAutoScaled());
-              setting.setThreshold(fcol.getThreshold());
-              Color noColour = fcol.getNoColour();
-              if (noColour == null)
-              {
-                setting.setNoValueColour(NoValueColour.NONE);
-              }
-              else if (noColour.equals(fcol.getMaxColour()))
-              {
-                setting.setNoValueColour(NoValueColour.MAX);
-              }
-              else
-              {
-                setting.setNoValueColour(NoValueColour.MIN);
-              }
-              // -1 = No threshold, 0 = Below, 1 = Above
-              setting.setThreshstate(fcol.isAboveThreshold() ? 1
-                      : (fcol.isBelowThreshold() ? 0 : -1));
-            }
-            else
-            {
-              setting.setColour(fcol.getColour().getRGB());
-            }
-
-            setting.setDisplay(
-                    av.getFeaturesDisplayed().isVisible(featureType));
-            float rorder = fr.getOrder(featureType);
-            if (rorder > -1)
-            {
-              setting.setOrder(rorder);
-            }
-            /// fs.addSetting(setting);
-            fs.getSetting().add(setting);
-            settingsAdded.addElement(featureType);
-          }
-        }
-
-        // is groups actually supposed to be a map here ?
-        Iterator<String> en = fr.getFeatureGroups().iterator();
-        Vector<String> groupsAdded = new Vector<>();
-        while (en.hasNext())
-        {
-          String grp = en.next();
-          if (groupsAdded.contains(grp))
-          {
-            continue;
-          }
-          Group g = new Group();
-          g.setName(grp);
-          g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
-                  .booleanValue());
-          // fs.addGroup(g);
-          fs.getGroup().add(g);
-          groupsAdded.addElement(grp);
-        }
-        // jms.setFeatureSettings(fs);
-        object.setFeatureSettings(fs);
-      }
-
-      if (av.hasHiddenColumns())
-      {
-        jalview.datamodel.HiddenColumns hidden = av.getAlignment()
-                .getHiddenColumns();
-        if (hidden == null)
-        {
-          Console.warn(
-                  "REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
-        }
-        else
-        {
-          Iterator<int[]> hiddenRegions = hidden.iterator();
-          while (hiddenRegions.hasNext())
-          {
-            int[] region = hiddenRegions.next();
-            HiddenColumns hc = new HiddenColumns();
-            hc.setStart(region[0]);
-            hc.setEnd(region[1]);
-            // view.addHiddenColumns(hc);
-            view.getHiddenColumns().add(hc);
-          }
-        }
-      }
-      if (calcIdSet.size() > 0)
-      {
-        for (String calcId : calcIdSet)
-        {
-          if (calcId.trim().length() > 0)
-          {
-            CalcIdParam cidp = createCalcIdParam(calcId, av);
-            // Some calcIds have no parameters.
-            if (cidp != null)
-            {
-              // view.addCalcIdParam(cidp);
-              view.getCalcIdParam().add(cidp);
-            }
-          }
-        }
-      }
-
-      // jms.addViewport(view);
-      object.getViewport().add(view);
-    }
-    // object.setJalviewModelSequence(jms);
-    // object.getVamsasModel().addSequenceSet(vamsasSet);
-    object.getVamsasModel().getSequenceSet().add(vamsasSet);
-
-    if (jout != null && fileName != null)
-    {
-      // We may not want to write the object to disk,
-      // eg we can copy the alignViewport to a new view object
-      // using save and then load
-      try
-      {
-        fileName = fileName.replace('\\', '/');
-        System.out.println("Writing jar entry " + fileName);
-        JarEntry entry = new JarEntry(fileName);
-        jout.putNextEntry(entry);
-        PrintWriter pout = new PrintWriter(
-                new OutputStreamWriter(jout, UTF_8));
-        JAXBContext jaxbContext = JAXBContext
-                .newInstance(JalviewModel.class);
-        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
-
-        // output pretty printed
-        // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
-        jaxbMarshaller.marshal(
-                new ObjectFactory().createJalviewModel(object), pout);
-
-        // jaxbMarshaller.marshal(object, pout);
-        // marshaller.marshal(object);
-        pout.flush();
-        jout.closeEntry();
-      } catch (Exception ex)
-      {
-        // TODO: raise error in GUI if marshalling failed.
-        System.err.println("Error writing Jalview project");
-        ex.printStackTrace();
-      }
-    }
-    return object;
-  }
-
-  /**
-   * Writes PCA viewer attributes and computed values to an XML model object and
-   * adds it to the JalviewModel. Any exceptions are reported by logging.
-   */
-  protected void savePCA(PCAPanel panel, JalviewModel object)
-  {
-    try
-    {
-      PcaViewer viewer = new PcaViewer();
-      viewer.setHeight(panel.getHeight());
-      viewer.setWidth(panel.getWidth());
-      viewer.setXpos(panel.getX());
-      viewer.setYpos(panel.getY());
-      viewer.setTitle(panel.getTitle());
-      PCAModel pcaModel = panel.getPcaModel();
-      viewer.setScoreModelName(pcaModel.getScoreModelName());
-      viewer.setXDim(panel.getSelectedDimensionIndex(X));
-      viewer.setYDim(panel.getSelectedDimensionIndex(Y));
-      viewer.setZDim(panel.getSelectedDimensionIndex(Z));
-      viewer.setBgColour(
-              panel.getRotatableCanvas().getBackgroundColour().getRGB());
-      viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
-      float[] spMin = panel.getRotatableCanvas().getSeqMin();
-      SeqPointMin spmin = new SeqPointMin();
-      spmin.setXPos(spMin[0]);
-      spmin.setYPos(spMin[1]);
-      spmin.setZPos(spMin[2]);
-      viewer.setSeqPointMin(spmin);
-      float[] spMax = panel.getRotatableCanvas().getSeqMax();
-      SeqPointMax spmax = new SeqPointMax();
-      spmax.setXPos(spMax[0]);
-      spmax.setYPos(spMax[1]);
-      spmax.setZPos(spMax[2]);
-      viewer.setSeqPointMax(spmax);
-      viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
-      viewer.setLinkToAllViews(
-              panel.getRotatableCanvas().isApplyToAllViews());
-      SimilarityParamsI sp = pcaModel.getSimilarityParameters();
-      viewer.setIncludeGaps(sp.includeGaps());
-      viewer.setMatchGaps(sp.matchGaps());
-      viewer.setIncludeGappedColumns(sp.includeGappedColumns());
-      viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
-
-      /*
-       * sequence points on display
-       */
-      for (jalview.datamodel.SequencePoint spt : pcaModel
-              .getSequencePoints())
-      {
-        SequencePoint point = new SequencePoint();
-        point.setSequenceRef(seqHash(spt.getSequence()));
-        point.setXPos(spt.coord.x);
-        point.setYPos(spt.coord.y);
-        point.setZPos(spt.coord.z);
-        viewer.getSequencePoint().add(point);
-      }
-
-      /*
-       * (end points of) axes on display
-       */
-      for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
-      {
-
-        Axis axis = new Axis();
-        axis.setXPos(p.x);
-        axis.setYPos(p.y);
-        axis.setZPos(p.z);
-        viewer.getAxis().add(axis);
-      }
-
-      /*
-       * raw PCA data (note we are not restoring PCA inputs here -
-       * alignment view, score model, similarity parameters)
-       */
-      PcaDataType data = new PcaDataType();
-      viewer.setPcaData(data);
-      PCA pca = pcaModel.getPcaData();
-
-      DoubleMatrix pm = new DoubleMatrix();
-      saveDoubleMatrix(pca.getPairwiseScores(), pm);
-      data.setPairwiseMatrix(pm);
-
-      DoubleMatrix tm = new DoubleMatrix();
-      saveDoubleMatrix(pca.getTridiagonal(), tm);
-      data.setTridiagonalMatrix(tm);
-
-      DoubleMatrix eigenMatrix = new DoubleMatrix();
-      data.setEigenMatrix(eigenMatrix);
-      saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
-
-      object.getPcaViewer().add(viewer);
-    } catch (Throwable t)
-    {
-      Console.error("Error saving PCA: " + t.getMessage());
-    }
-  }
-
-  /**
-   * Stores values from a matrix into an XML element, including (if present) the
-   * D or E vectors
-   * 
-   * @param m
-   * @param xmlMatrix
-   * @see #loadDoubleMatrix(DoubleMatrix)
-   */
-  protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
-  {
-    xmlMatrix.setRows(m.height());
-    xmlMatrix.setColumns(m.width());
-    for (int i = 0; i < m.height(); i++)
-    {
-      DoubleVector row = new DoubleVector();
-      for (int j = 0; j < m.width(); j++)
-      {
-        row.getV().add(m.getValue(i, j));
-      }
-      xmlMatrix.getRow().add(row);
-    }
-    if (m.getD() != null)
-    {
-      DoubleVector dVector = new DoubleVector();
-      for (double d : m.getD())
-      {
-        dVector.getV().add(d);
-      }
-      xmlMatrix.setD(dVector);
-    }
-    if (m.getE() != null)
-    {
-      DoubleVector eVector = new DoubleVector();
-      for (double e : m.getE())
-      {
-        eVector.getV().add(e);
-      }
-      xmlMatrix.setE(eVector);
-    }
-  }
-
-  /**
-   * Loads XML matrix data into a new Matrix object, including the D and/or E
-   * vectors (if present)
-   * 
-   * @param mData
-   * @return
-   * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
-   */
-  protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
-  {
-    int rows = mData.getRows();
-    double[][] vals = new double[rows][];
-
-    for (int i = 0; i < rows; i++)
-    {
-      List<Double> dVector = mData.getRow().get(i).getV();
-      vals[i] = new double[dVector.size()];
-      int dvi = 0;
-      for (Double d : dVector)
-      {
-        vals[i][dvi++] = d;
-      }
-    }
-
-    MatrixI m = new Matrix(vals);
-
-    if (mData.getD() != null)
-    {
-      List<Double> dVector = mData.getD().getV();
-      double[] vec = new double[dVector.size()];
-      int dvi = 0;
-      for (Double d : dVector)
-      {
-        vec[dvi++] = d;
-      }
-      m.setD(vec);
-    }
-    if (mData.getE() != null)
-    {
-      List<Double> dVector = mData.getE().getV();
-      double[] vec = new double[dVector.size()];
-      int dvi = 0;
-      for (Double d : dVector)
-      {
-        vec[dvi++] = d;
-      }
-      m.setE(vec);
-    }
-
-    return m;
-  }
-
-  /**
-   * Save any Varna viewers linked to this sequence. Writes an rnaViewer element
-   * for each viewer, with
-   * <ul>
-   * <li>viewer geometry (position, size, split pane divider location)</li>
-   * <li>index of the selected structure in the viewer (currently shows gapped
-   * or ungapped)</li>
-   * <li>the id of the annotation holding RNA secondary structure</li>
-   * <li>(currently only one SS is shown per viewer, may be more in future)</li>
-   * </ul>
-   * Varna viewer state is also written out (in native Varna XML) to separate
-   * project jar entries. A separate entry is written for each RNA structure
-   * displayed, with the naming convention
-   * <ul>
-   * <li>rna_viewId_sequenceId_annotationId_[gapped|trimmed]</li>
-   * </ul>
-   * 
-   * @param jout
-   * @param jseq
-   * @param jds
-   * @param viewIds
-   * @param ap
-   * @param storeDataset
-   */
-  protected void saveRnaViewers(JarOutputStream jout, JSeq jseq,
-          final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
-          boolean storeDataset)
-  {
-    if (Desktop.desktop == null)
-    {
-      return;
-    }
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-    for (int f = frames.length - 1; f > -1; f--)
-    {
-      if (frames[f] instanceof AppVarna)
-      {
-        AppVarna varna = (AppVarna) frames[f];
-        /*
-         * link the sequence to every viewer that is showing it and is linked to
-         * its alignment panel
-         */
-        if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel())
-        {
-          String viewId = varna.getViewId();
-          RnaViewer rna = new RnaViewer();
-          rna.setViewId(viewId);
-          rna.setTitle(varna.getTitle());
-          rna.setXpos(varna.getX());
-          rna.setYpos(varna.getY());
-          rna.setWidth(varna.getWidth());
-          rna.setHeight(varna.getHeight());
-          rna.setDividerLocation(varna.getDividerLocation());
-          rna.setSelectedRna(varna.getSelectedIndex());
-          // jseq.addRnaViewer(rna);
-          jseq.getRnaViewer().add(rna);
-
-          /*
-           * Store each Varna panel's state once in the project per sequence.
-           * First time through only (storeDataset==false)
-           */
-          // boolean storeSessions = false;
-          // String sequenceViewId = viewId + seqsToIds.get(jds);
-          // if (!storeDataset && !viewIds.contains(sequenceViewId))
-          // {
-          // viewIds.add(sequenceViewId);
-          // storeSessions = true;
-          // }
-          for (RnaModel model : varna.getModels())
-          {
-            if (model.seq == jds)
-            {
-              /*
-               * VARNA saves each view (sequence or alignment secondary
-               * structure, gapped or trimmed) as a separate XML file
-               */
-              String jarEntryName = rnaSessions.get(model);
-              if (jarEntryName == null)
-              {
-
-                String varnaStateFile = varna.getStateInfo(model.rna);
-                jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
-                copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna");
-                rnaSessions.put(model, jarEntryName);
-              }
-              SecondaryStructure ss = new SecondaryStructure();
-              String annotationId = varna.getAnnotation(jds).annotationId;
-              ss.setAnnotationId(annotationId);
-              ss.setViewerState(jarEntryName);
-              ss.setGapped(model.gapped);
-              ss.setTitle(model.title);
-              // rna.addSecondaryStructure(ss);
-              rna.getSecondaryStructure().add(ss);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * Copy the contents of a file to a new entry added to the output jar
-   * 
-   * @param jout
-   * @param infilePath
-   * @param jarEntryName
-   * @param msg
-   *          additional identifying info to log to the console
-   */
-  protected void copyFileToJar(JarOutputStream jout, String infilePath,
-          String jarEntryName, String msg)
-  {
-    try (InputStream is = new FileInputStream(infilePath))
-    {
-      File file = new File(infilePath);
-      if (file.exists() && jout != null)
-      {
-        System.out.println(
-                "Writing jar entry " + jarEntryName + " (" + msg + ")");
-        jout.putNextEntry(new JarEntry(jarEntryName));
-        copyAll(is, jout);
-        jout.closeEntry();
-        // dis = new DataInputStream(new FileInputStream(file));
-        // byte[] data = new byte[(int) file.length()];
-        // dis.readFully(data);
-        // writeJarEntry(jout, jarEntryName, data);
-      }
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
-  }
-
-  /**
-   * Copies input to output, in 4K buffers; handles any data (text or binary)
-   * 
-   * @param in
-   * @param out
-   * @throws IOException
-   */
-  protected void copyAll(InputStream in, OutputStream out)
-          throws IOException
-  {
-    byte[] buffer = new byte[4096];
-    int bytesRead = 0;
-    while ((bytesRead = in.read(buffer)) != -1)
-    {
-      out.write(buffer, 0, bytesRead);
-    }
-  }
-
-  /**
-   * Save the state of a structure viewer
-   * 
-   * @param ap
-   * @param jds
-   * @param pdb
-   *          the archive XML element under which to save the state
-   * @param entry
-   * @param viewIds
-   * @param matchedFile
-   * @param viewFrame
-   * @return
-   */
-  protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
-          Pdbids pdb, PDBEntry entry, List<String> viewIds,
-          String matchedFile, StructureViewerBase viewFrame)
-  {
-    final AAStructureBindingModel bindingModel = viewFrame.getBinding();
-
-    /*
-     * Look for any bindings for this viewer to the PDB file of interest
-     * (including part matches excluding chain id)
-     */
-    for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
-    {
-      final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
-      final String pdbId = pdbentry.getId();
-      if (!pdbId.equals(entry.getId()) && !(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
-         */
-        continue;
-      }
-      if (matchedFile == null)
-      {
-        matchedFile = pdbentry.getFile();
-      }
-      else if (!matchedFile.equals(pdbentry.getFile()))
-      {
-        Console.warn(
-                "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
-                        + pdbentry.getFile());
-      }
-      // record the
-      // file so we
-      // can get at it if the ID
-      // match is ambiguous (e.g.
-      // 1QIP==1qipA)
-
-      for (int smap = 0; smap < viewFrame.getBinding()
-              .getSequence()[peid].length; smap++)
-      {
-        // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
-        if (jds == viewFrame.getBinding().getSequence()[peid][smap])
-        {
-          StructureState state = new StructureState();
-          state.setVisible(true);
-          state.setXpos(viewFrame.getX());
-          state.setYpos(viewFrame.getY());
-          state.setWidth(viewFrame.getWidth());
-          state.setHeight(viewFrame.getHeight());
-          final String viewId = viewFrame.getViewId();
-          state.setViewId(viewId);
-          state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
-          state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap));
-          state.setColourByJmol(viewFrame.isColouredByViewer());
-          state.setType(viewFrame.getViewerType().toString());
-          // pdb.addStructureState(state);
-          pdb.getStructureState().add(state);
-        }
-      }
-    }
-    return matchedFile;
-  }
-
-  /**
-   * Populates the AnnotationColourScheme xml for save. This captures the
-   * settings of the options in the 'Colour by Annotation' dialog.
-   * 
-   * @param acg
-   * @param userColours
-   * @param jm
-   * @return
-   */
-  private AnnotationColourScheme constructAnnotationColours(
-          AnnotationColourGradient acg, List<UserColourScheme> userColours,
-          JalviewModel jm)
-  {
-    AnnotationColourScheme ac = new AnnotationColourScheme();
-    ac.setAboveThreshold(acg.getAboveThreshold());
-    ac.setThreshold(acg.getAnnotationThreshold());
-    // 2.10.2 save annotationId (unique) not annotation label
-    ac.setAnnotation(acg.getAnnotation().annotationId);
-    if (acg.getBaseColour() instanceof UserColourScheme)
-    {
-      ac.setColourScheme(
-              setUserColourScheme(acg.getBaseColour(), userColours, jm));
-    }
-    else
-    {
-      ac.setColourScheme(
-              ColourSchemeProperty.getColourName(acg.getBaseColour()));
-    }
-
-    ac.setMaxColour(acg.getMaxColour().getRGB());
-    ac.setMinColour(acg.getMinColour().getRGB());
-    ac.setPerSequence(acg.isSeqAssociated());
-    ac.setPredefinedColours(acg.isPredefinedColours());
-    return ac;
-  }
-
-  private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
-          IdentityHashMap<SequenceGroup, String> groupRefs,
-          AlignmentViewport av, Set<String> calcIdSet, boolean storeDS,
-          SequenceSet vamsasSet)
-  {
-
-    for (int i = 0; i < aa.length; i++)
-    {
-      Annotation an = new Annotation();
-
-      AlignmentAnnotation annotation = aa[i];
-      if (annotation.annotationId != null)
-      {
-        annotationIds.put(annotation.annotationId, annotation);
-      }
-
-      an.setId(annotation.annotationId);
-
-      an.setVisible(annotation.visible);
-
-      an.setDescription(annotation.description);
-
-      if (annotation.sequenceRef != null)
-      {
-        // 2.9 JAL-1781 xref on sequence id rather than name
-        an.setSequenceRef(seqsToIds.get(annotation.sequenceRef));
-      }
-      if (annotation.groupRef != null)
-      {
-        String groupIdr = groupRefs.get(annotation.groupRef);
-        if (groupIdr == null)
-        {
-          // make a locally unique String
-          groupRefs.put(annotation.groupRef,
-                  groupIdr = ("" + System.currentTimeMillis()
-                          + annotation.groupRef.getName()
-                          + groupRefs.size()));
-        }
-        an.setGroupRef(groupIdr.toString());
-      }
-
-      // store all visualization attributes for annotation
-      an.setGraphHeight(annotation.graphHeight);
-      an.setCentreColLabels(annotation.centreColLabels);
-      an.setScaleColLabels(annotation.scaleColLabel);
-      an.setShowAllColLabels(annotation.showAllColLabels);
-      an.setBelowAlignment(annotation.belowAlignment);
-
-      if (annotation.graph > 0)
-      {
-        an.setGraph(true);
-        an.setGraphType(annotation.graph);
-        an.setGraphGroup(annotation.graphGroup);
-        if (annotation.getThreshold() != null)
-        {
-          ThresholdLine line = new ThresholdLine();
-          line.setLabel(annotation.getThreshold().label);
-          line.setValue(annotation.getThreshold().value);
-          line.setColour(annotation.getThreshold().colour.getRGB());
-          an.setThresholdLine(line);
-        }
-      }
-      else
-      {
-        an.setGraph(false);
-      }
-
-      an.setLabel(annotation.label);
-
-      if (annotation == av.getAlignmentQualityAnnot()
-              || annotation == av.getAlignmentConservationAnnotation()
-              || annotation == av.getAlignmentConsensusAnnotation()
-              || annotation.autoCalculated)
-      {
-        // new way of indicating autocalculated annotation -
-        an.setAutoCalculated(annotation.autoCalculated);
-      }
-      if (annotation.hasScore())
-      {
-        an.setScore(annotation.getScore());
-      }
-
-      if (annotation.getCalcId() != null)
-      {
-        calcIdSet.add(annotation.getCalcId());
-        an.setCalcId(annotation.getCalcId());
-      }
-      if (annotation.hasProperties())
-      {
-        for (String pr : annotation.getProperties())
-        {
-          jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property();
-          prop.setName(pr);
-          prop.setValue(annotation.getProperty(pr));
-          // an.addProperty(prop);
-          an.getProperty().add(prop);
-        }
-      }
-
-      AnnotationElement ae;
-      if (annotation.annotations != null)
-      {
-        an.setScoreOnly(false);
-        for (int a = 0; a < annotation.annotations.length; a++)
-        {
-          if ((annotation == null) || (annotation.annotations[a] == null))
-          {
-            continue;
-          }
-
-          ae = new AnnotationElement();
-          if (annotation.annotations[a].description != null)
-          {
-            ae.setDescription(annotation.annotations[a].description);
-          }
-          if (annotation.annotations[a].displayCharacter != null)
-          {
-            ae.setDisplayCharacter(
-                    annotation.annotations[a].displayCharacter);
-          }
-
-          if (!Float.isNaN(annotation.annotations[a].value))
-          {
-            ae.setValue(annotation.annotations[a].value);
-          }
-
-          ae.setPosition(a);
-          if (annotation.annotations[a].secondaryStructure > ' ')
-          {
-            ae.setSecondaryStructure(
-                    annotation.annotations[a].secondaryStructure + "");
-          }
-
-          if (annotation.annotations[a].colour != null
-                  && annotation.annotations[a].colour != java.awt.Color.black)
-          {
-            ae.setColour(annotation.annotations[a].colour.getRGB());
-          }
-
-          // an.addAnnotationElement(ae);
-          an.getAnnotationElement().add(ae);
-          if (annotation.autoCalculated)
-          {
-            // only write one non-null entry into the annotation row -
-            // sufficient to get the visualization attributes necessary to
-            // display data
-            continue;
-          }
-        }
-      }
-      else
-      {
-        an.setScoreOnly(true);
-      }
-      if (!storeDS || (storeDS && !annotation.autoCalculated))
-      {
-        // skip autocalculated annotation - these are only provided for
-        // alignments
-        // vamsasSet.addAnnotation(an);
-        vamsasSet.getAnnotation().add(an);
-      }
-    }
-
-  }
-
-  private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
-  {
-    AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
-    if (settings != null)
-    {
-      CalcIdParam vCalcIdParam = new CalcIdParam();
-      vCalcIdParam.setCalcId(calcId);
-      // vCalcIdParam.addServiceURL(settings.getServiceURI());
-      vCalcIdParam.getServiceURL().add(settings.getServiceURI());
-      // generic URI allowing a third party to resolve another instance of the
-      // service used for this calculation
-      for (String url : settings.getServiceURLs())
-      {
-        // vCalcIdParam.addServiceURL(urls);
-        vCalcIdParam.getServiceURL().add(url);
-      }
-      vCalcIdParam.setVersion("1.0");
-      if (settings.getPreset() != null)
-      {
-        WsParamSetI setting = settings.getPreset();
-        vCalcIdParam.setName(setting.getName());
-        vCalcIdParam.setDescription(setting.getDescription());
-      }
-      else
-      {
-        vCalcIdParam.setName("");
-        vCalcIdParam.setDescription("Last used parameters");
-      }
-      // need to be able to recover 1) settings 2) user-defined presets or
-      // recreate settings from preset 3) predefined settings provided by
-      // service - or settings that can be transferred (or discarded)
-      vCalcIdParam.setParameters(
-              settings.getWsParamFile().replace("\n", "|\\n|"));
-      vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
-      // todo - decide if updateImmediately is needed for any projects.
-
-      return vCalcIdParam;
-    }
-    return null;
-  }
-
-  private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
-          AlignViewport av)
-  {
-    if (calcIdParam.getVersion().equals("1.0"))
-    {
-      final String[] calcIds = calcIdParam.getServiceURL()
-              .toArray(new String[0]);
-      Jws2Instance service = Jws2Discoverer.getDiscoverer()
-              .getPreferredServiceFor(calcIds);
-      if (service != null)
-      {
-        WsParamSetI parmSet = null;
-        try
-        {
-          parmSet = service.getParamStore().parseServiceParameterFile(
-                  calcIdParam.getName(), calcIdParam.getDescription(),
-                  calcIds,
-                  calcIdParam.getParameters().replace("|\\n|", "\n"));
-        } catch (IOException x)
-        {
-          Console.warn("Couldn't parse parameter data for "
-                  + calcIdParam.getCalcId(), x);
-          return false;
-        }
-        List<ArgumentI> argList = null;
-        if (calcIdParam.getName().length() > 0)
-        {
-          parmSet = service.getParamStore()
-                  .getPreset(calcIdParam.getName());
-          if (parmSet != null)
-          {
-            // TODO : check we have a good match with settings in AACon -
-            // otherwise we'll need to create a new preset
-          }
-        }
-        else
-        {
-          argList = parmSet.getArguments();
-          parmSet = null;
-        }
-        AAConSettings settings = new AAConSettings(
-                calcIdParam.isAutoUpdate(), service, parmSet, argList);
-        av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
-                calcIdParam.isNeedsUpdate());
-        return true;
-      }
-      else
-      {
-        Console.warn(
-                "Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
-        return false;
-      }
-    }
-    throw new Error(MessageManager.formatMessage(
-            "error.unsupported_version_calcIdparam", new Object[]
-            { calcIdParam.toString() }));
-  }
-
-  /**
-   * External mapping between jalview objects and objects yielding a valid and
-   * unique object ID string. This is null for normal Jalview project IO, but
-   * non-null when a jalview project is being read or written as part of a
-   * vamsas session.
-   */
-  IdentityHashMap jv2vobj = null;
-
-  /**
-   * Construct a unique ID for jvobj using either existing bindings or if none
-   * exist, the result of the hashcode call for the object.
-   * 
-   * @param jvobj
-   *          jalview data object
-   * @return unique ID for referring to jvobj
-   */
-  private String makeHashCode(Object jvobj, String altCode)
-  {
-    if (jv2vobj != null)
-    {
-      Object id = jv2vobj.get(jvobj);
-      if (id != null)
-      {
-        return id.toString();
-      }
-      // check string ID mappings
-      if (jvids2vobj != null && jvobj instanceof String)
-      {
-        id = jvids2vobj.get(jvobj);
-      }
-      if (id != null)
-      {
-        return id.toString();
-      }
-      // give up and warn that something has gone wrong
-      Console.warn(
-              "Cannot find ID for object in external mapping : " + jvobj);
-    }
-    return altCode;
-  }
-
-  /**
-   * return local jalview object mapped to ID, if it exists
-   * 
-   * @param idcode
-   *          (may be null)
-   * @return null or object bound to idcode
-   */
-  private Object retrieveExistingObj(String idcode)
-  {
-    if (idcode != null && vobj2jv != null)
-    {
-      return vobj2jv.get(idcode);
-    }
-    return null;
-  }
-
-  /**
-   * binding from ID strings from external mapping table to jalview data model
-   * objects.
-   */
-  private Hashtable vobj2jv;
-
-  private Sequence createVamsasSequence(String id, SequenceI jds)
-  {
-    return createVamsasSequence(true, id, jds, null);
-  }
-
-  private Sequence createVamsasSequence(boolean recurse, String id,
-          SequenceI jds, SequenceI parentseq)
-  {
-    Sequence vamsasSeq = new Sequence();
-    vamsasSeq.setId(id);
-    vamsasSeq.setName(jds.getName());
-    vamsasSeq.setSequence(jds.getSequenceAsString());
-    vamsasSeq.setDescription(jds.getDescription());
-    List<DBRefEntry> dbrefs = null;
-    if (jds.getDatasetSequence() != null)
-    {
-      vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
-    }
-    else
-    {
-      // seqId==dsseqid so we can tell which sequences really are
-      // dataset sequences only
-      vamsasSeq.setDsseqid(id);
-      dbrefs = jds.getDBRefs();
-      if (parentseq == null)
-      {
-        parentseq = jds;
-      }
-    }
-
-    /*
-     * save any dbrefs; special subclass GeneLocus is flagged as 'locus'
-     */
-    if (dbrefs != null)
-    {
-      for (int d = 0, nd = dbrefs.size(); d < nd; d++)
-      {
-        DBRef dbref = new DBRef();
-        DBRefEntry ref = dbrefs.get(d);
-        dbref.setSource(ref.getSource());
-        dbref.setVersion(ref.getVersion());
-        dbref.setAccessionId(ref.getAccessionId());
-        dbref.setCanonical(ref.isCanonical());
-        if (ref instanceof GeneLocus)
-        {
-          dbref.setLocus(true);
-        }
-        if (ref.hasMap())
-        {
-          Mapping mp = createVamsasMapping(ref.getMap(), parentseq, jds,
-                  recurse);
-          dbref.setMapping(mp);
-        }
-        vamsasSeq.getDBRef().add(dbref);
-      }
-    }
-    return vamsasSeq;
-  }
-
-  private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
-          SequenceI parentseq, SequenceI jds, boolean recurse)
-  {
-    Mapping mp = null;
-    if (jmp.getMap() != null)
-    {
-      mp = new Mapping();
-
-      jalview.util.MapList mlst = jmp.getMap();
-      List<int[]> r = mlst.getFromRanges();
-      for (int[] range : r)
-      {
-        MapListFrom mfrom = new MapListFrom();
-        mfrom.setStart(range[0]);
-        mfrom.setEnd(range[1]);
-        // mp.addMapListFrom(mfrom);
-        mp.getMapListFrom().add(mfrom);
-      }
-      r = mlst.getToRanges();
-      for (int[] range : r)
-      {
-        MapListTo mto = new MapListTo();
-        mto.setStart(range[0]);
-        mto.setEnd(range[1]);
-        // mp.addMapListTo(mto);
-        mp.getMapListTo().add(mto);
-      }
-      mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
-      mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
-      if (jmp.getTo() != null)
-      {
-        // MappingChoice mpc = new MappingChoice();
-
-        // check/create ID for the sequence referenced by getTo()
-
-        String jmpid = "";
-        SequenceI ps = null;
-        if (parentseq != jmp.getTo()
-                && parentseq.getDatasetSequence() != jmp.getTo())
-        {
-          // chaining dbref rather than a handshaking one
-          jmpid = seqHash(ps = jmp.getTo());
-        }
-        else
-        {
-          jmpid = seqHash(ps = parentseq);
-        }
-        // mpc.setDseqFor(jmpid);
-        mp.setDseqFor(jmpid);
-        if (!seqRefIds.containsKey(jmpid))
-        {
-          Console.debug("creatign new DseqFor ID");
-          seqRefIds.put(jmpid, ps);
-        }
-        else
-        {
-          Console.debug("reusing DseqFor ID");
-        }
-
-        // mp.setMappingChoice(mpc);
-      }
-    }
-    return mp;
-  }
-
-  String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
-          List<UserColourScheme> userColours, JalviewModel jm)
-  {
-    String id = null;
-    jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
-    boolean newucs = false;
-    if (!userColours.contains(ucs))
-    {
-      userColours.add(ucs);
-      newucs = true;
-    }
-    id = "ucs" + userColours.indexOf(ucs);
-    if (newucs)
-    {
-      // actually create the scheme's entry in the XML model
-      java.awt.Color[] colours = ucs.getColours();
-      UserColours uc = new UserColours();
-      // UserColourScheme jbucs = new UserColourScheme();
-      JalviewUserColours jbucs = new JalviewUserColours();
-
-      for (int i = 0; i < colours.length; i++)
-      {
-        Colour col = new Colour();
-        col.setName(ResidueProperties.aa[i]);
-        col.setRGB(jalview.util.Format.getHexString(colours[i]));
-        // jbucs.addColour(col);
-        jbucs.getColour().add(col);
-      }
-      if (ucs.getLowerCaseColours() != null)
-      {
-        colours = ucs.getLowerCaseColours();
-        for (int i = 0; i < colours.length; i++)
-        {
-          Colour col = new Colour();
-          col.setName(ResidueProperties.aa[i].toLowerCase(Locale.ROOT));
-          col.setRGB(jalview.util.Format.getHexString(colours[i]));
-          // jbucs.addColour(col);
-          jbucs.getColour().add(col);
-        }
-      }
-
-      uc.setId(id);
-      uc.setUserColourScheme(jbucs);
-      // jm.addUserColours(uc);
-      jm.getUserColours().add(uc);
-    }
-
-    return id;
-  }
-
-  jalview.schemes.UserColourScheme getUserColourScheme(JalviewModel jm,
-          String id)
-  {
-    List<UserColours> uc = jm.getUserColours();
-    UserColours colours = null;
-    /*
-    for (int i = 0; i < uc.length; i++)
-    {
-      if (uc[i].getId().equals(id))
-      {
-        colours = uc[i];
-        break;
-      }
-    }
-    */
-    for (UserColours c : uc)
-    {
-      if (c.getId().equals(id))
-      {
-        colours = c;
-        break;
-      }
-    }
-
-    java.awt.Color[] newColours = new java.awt.Color[24];
-
-    for (int i = 0; i < 24; i++)
-    {
-      newColours[i] = new java.awt.Color(Integer.parseInt(
-              // colours.getUserColourScheme().getColour(i).getRGB(), 16));
-              colours.getUserColourScheme().getColour().get(i).getRGB(),
-              16));
-    }
-
-    jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
-            newColours);
-
-    if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24)
-    {
-      newColours = new java.awt.Color[23];
-      for (int i = 0; i < 23; i++)
-      {
-        newColours[i] = new java.awt.Color(
-                Integer.parseInt(colours.getUserColourScheme().getColour()
-                        .get(i + 24).getRGB(), 16));
-      }
-      ucs.setLowerCaseColours(newColours);
-    }
-
-    return ucs;
-  }
-
-  /**
-   * contains last error message (if any) encountered by XML loader.
-   */
-  String errorMessage = null;
-
-  /**
-   * flag to control whether the Jalview2XML_V1 parser should be deferred to if
-   * exceptions are raised during project XML parsing
-   */
-  public boolean attemptversion1parse = false;
-
-  /**
-   * Load a jalview project archive from a jar file
-   * 
-   * @param file
-   *          - HTTP URL or filename
-   */
-  public AlignFrame loadJalviewAlign(final Object file)
-  {
-
-    jalview.gui.AlignFrame af = null;
-
-    try
-    {
-      // create list to store references for any new Jmol viewers created
-      newStructureViewers = new Vector<>();
-      // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
-      // Workaround is to make sure caller implements the JarInputStreamProvider
-      // interface
-      // so we can re-open the jar input stream for each entry.
-
-      jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
-      af = loadJalviewAlign(jprovider);
-      if (af != null)
-      {
-        af.setMenusForViewport();
-      }
-    } catch (MalformedURLException e)
-    {
-      errorMessage = "Invalid URL format for '" + file + "'";
-      reportErrors();
-    } finally
-    {
-      try
-      {
-        SwingUtilities.invokeAndWait(new Runnable()
-        {
-          @Override
-          public void run()
-          {
-            setLoadingFinishedForNewStructureViewers();
-          }
-        });
-      } catch (Exception x)
-      {
-        System.err.println("Error loading alignment: " + x.getMessage());
-      }
-    }
-    return af;
-  }
-
-  @SuppressWarnings("unused")
-  private jarInputStreamProvider createjarInputStreamProvider(
-          final Object ofile) throws MalformedURLException
-  {
-
-    // BH 2018 allow for bytes already attached to File object
-    try
-    {
-      String file = (ofile instanceof File
-              ? ((File) ofile).getCanonicalPath()
-              : ofile.toString());
-      byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
-              : null;
-      URL url = null;
-      errorMessage = null;
-      uniqueSetSuffix = null;
-      seqRefIds = null;
-      viewportsAdded.clear();
-      frefedSequence = null;
-
-      if (HttpUtils.startsWithHttpOrHttps(file))
-      {
-        url = new URL(file);
-      }
-      final URL _url = url;
-      return new jarInputStreamProvider()
-      {
-
-        @Override
-        public JarInputStream getJarInputStream() throws IOException
-        {
-          if (bytes != null)
-          {
-            // System.out.println("Jalview2XML: opening byte jarInputStream for
-            // bytes.length=" + bytes.length);
-            return new JarInputStream(new ByteArrayInputStream(bytes));
-          }
-          if (_url != null)
-          {
-            // System.out.println("Jalview2XML: opening url jarInputStream for "
-            // + _url);
-            return new JarInputStream(_url.openStream());
-          }
-          else
-          {
-            // System.out.println("Jalview2XML: opening file jarInputStream for
-            // " + file);
-            return new JarInputStream(new FileInputStream(file));
-          }
-        }
-
-        @Override
-        public String getFilename()
-        {
-          return file;
-        }
-      };
-    } catch (IOException e)
-    {
-      e.printStackTrace();
-      return null;
-    }
-  }
-
-  /**
-   * Recover jalview session from a jalview project archive. Caller may
-   * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
-   * themselves. Any null fields will be initialised with default values,
-   * non-null fields are left alone.
-   * 
-   * @param jprovider
-   * @return
-   */
-  public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
-  {
-    errorMessage = null;
-    if (uniqueSetSuffix == null)
-    {
-      uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
-    }
-    if (seqRefIds == null)
-    {
-      initSeqRefs();
-    }
-    AlignFrame af = null, _af = null;
-    IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
-    Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
-    final String file = jprovider.getFilename();
-    try
-    {
-      JarInputStream jin = null;
-      JarEntry jarentry = null;
-      int entryCount = 1;
-
-      do
-      {
-        jin = jprovider.getJarInputStream();
-        for (int i = 0; i < entryCount; i++)
-        {
-          jarentry = jin.getNextJarEntry();
-        }
-
-        if (jarentry != null && jarentry.getName().endsWith(".xml"))
-        {
-          JAXBContext jc = JAXBContext
-                  .newInstance("jalview.xml.binding.jalview");
-          XMLStreamReader streamReader = XMLInputFactory.newInstance()
-                  .createXMLStreamReader(jin);
-          javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
-          JAXBElement<JalviewModel> jbe = um.unmarshal(streamReader,
-                  JalviewModel.class);
-          JalviewModel object = jbe.getValue();
-
-          if (true) // !skipViewport(object))
-          {
-            _af = loadFromObject(object, file, true, jprovider);
-            if (_af != null && object.getViewport().size() > 0)
-            // getJalviewModelSequence().getViewportCount() > 0)
-            {
-              if (af == null)
-              {
-                // store a reference to the first view
-                af = _af;
-              }
-              if (_af.getViewport().isGatherViewsHere())
-              {
-                // if this is a gathered view, keep its reference since
-                // after gathering views, only this frame will remain
-                af = _af;
-                gatherToThisFrame.put(_af.getViewport().getSequenceSetId(),
-                        _af);
-              }
-              // Save dataset to register mappings once all resolved
-              importedDatasets.put(
-                      af.getViewport().getAlignment().getDataset(),
-                      af.getViewport().getAlignment().getDataset());
-            }
-          }
-          entryCount++;
-        }
-        else if (jarentry != null)
-        {
-          // Some other file here.
-          entryCount++;
-        }
-      } while (jarentry != null);
-      jin.close();
-      resolveFrefedSequences();
-    } catch (IOException ex)
-    {
-      ex.printStackTrace();
-      errorMessage = "Couldn't locate Jalview XML file : " + file;
-      System.err.println(
-              "Exception whilst loading jalview XML file : " + ex + "\n");
-    } catch (Exception ex)
-    {
-      System.err.println("Parsing as Jalview Version 2 file failed.");
-      ex.printStackTrace(System.err);
-      if (attemptversion1parse)
-      {
-        // used to attempt to parse as V1 castor-generated xml
-      }
-      if (Desktop.instance != null)
-      {
-        Desktop.instance.stopLoading();
-      }
-      if (af != null)
-      {
-        System.out.println("Successfully loaded archive file");
-        return af;
-      }
-      ex.printStackTrace();
-
-      System.err.println(
-              "Exception whilst loading jalview XML file : " + ex + "\n");
-    } catch (OutOfMemoryError e)
-    {
-      // Don't use the OOM Window here
-      errorMessage = "Out of memory loading jalview XML file";
-      System.err.println("Out of memory whilst loading jalview XML file");
-      e.printStackTrace();
-    }
-
-    /*
-     * Regather multiple views (with the same sequence set id) to the frame (if
-     * any) that is flagged as the one to gather to, i.e. convert them to tabbed
-     * views instead of separate frames. Note this doesn't restore a state where
-     * some expanded views in turn have tabbed views - the last "first tab" read
-     * in will play the role of gatherer for all.
-     */
-    for (AlignFrame fr : gatherToThisFrame.values())
-    {
-      Desktop.instance.gatherViews(fr);
-    }
-
-    restoreSplitFrames();
-    for (AlignmentI ds : importedDatasets.keySet())
-    {
-      if (ds.getCodonFrames() != null)
-      {
-        StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance)
-                .registerMappings(ds.getCodonFrames());
-      }
-    }
-    if (errorMessage != null)
-    {
-      reportErrors();
-    }
-
-    if (Desktop.instance != null)
-    {
-      Desktop.instance.stopLoading();
-    }
-
-    return af;
-  }
-
-  /**
-   * Try to reconstruct and display SplitFrame windows, where each contains
-   * complementary dna and protein alignments. Done by pairing up AlignFrame
-   * objects (created earlier) which have complementary viewport ids associated.
-   */
-  protected void restoreSplitFrames()
-  {
-    List<SplitFrame> gatherTo = new ArrayList<>();
-    List<AlignFrame> addedToSplitFrames = new ArrayList<>();
-    Map<String, AlignFrame> dna = new HashMap<>();
-
-    /*
-     * Identify the DNA alignments
-     */
-    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
-            .entrySet())
-    {
-      AlignFrame af = candidate.getValue();
-      if (af.getViewport().getAlignment().isNucleotide())
-      {
-        dna.put(candidate.getKey().getId(), af);
-      }
-    }
-
-    /*
-     * Try to match up the protein complements
-     */
-    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
-            .entrySet())
-    {
-      AlignFrame af = candidate.getValue();
-      if (!af.getViewport().getAlignment().isNucleotide())
-      {
-        String complementId = candidate.getKey().getComplementId();
-        // only non-null complements should be in the Map
-        if (complementId != null && dna.containsKey(complementId))
-        {
-          final AlignFrame dnaFrame = dna.get(complementId);
-          SplitFrame sf = createSplitFrame(dnaFrame, af);
-          addedToSplitFrames.add(dnaFrame);
-          addedToSplitFrames.add(af);
-          dnaFrame.setMenusForViewport();
-          af.setMenusForViewport();
-          if (af.getViewport().isGatherViewsHere())
-          {
-            gatherTo.add(sf);
-          }
-        }
-      }
-    }
-
-    /*
-     * Open any that we failed to pair up (which shouldn't happen!) as
-     * standalone AlignFrame's.
-     */
-    for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
-            .entrySet())
-    {
-      AlignFrame af = candidate.getValue();
-      if (!addedToSplitFrames.contains(af))
-      {
-        Viewport view = candidate.getKey();
-        Desktop.addInternalFrame(af, view.getTitle(),
-                safeInt(view.getWidth()), safeInt(view.getHeight()));
-        af.setMenusForViewport();
-        System.err.println("Failed to restore view " + view.getTitle()
-                + " to split frame");
-      }
-    }
-
-    /*
-     * Gather back into tabbed views as flagged.
-     */
-    for (SplitFrame sf : gatherTo)
-    {
-      Desktop.instance.gatherViews(sf);
-    }
-
-    splitFrameCandidates.clear();
-  }
-
-  /**
-   * Construct and display one SplitFrame holding DNA and protein alignments.
-   * 
-   * @param dnaFrame
-   * @param proteinFrame
-   * @return
-   */
-  protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
-          AlignFrame proteinFrame)
-  {
-    SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
-    String title = MessageManager.getString("label.linked_view_title");
-    int width = (int) dnaFrame.getBounds().getWidth();
-    int height = (int) (dnaFrame.getBounds().getHeight()
-            + proteinFrame.getBounds().getHeight() + 50);
-
-    /*
-     * SplitFrame location is saved to both enclosed frames
-     */
-    splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
-    Desktop.addInternalFrame(splitFrame, title, width, height);
-
-    /*
-     * And compute cDNA consensus (couldn't do earlier with consensus as
-     * mappings were not yet present)
-     */
-    proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel);
-
-    return splitFrame;
-  }
-
-  /**
-   * check errorMessage for a valid error message and raise an error box in the
-   * GUI or write the current errorMessage to stderr and then clear the error
-   * state.
-   */
-  protected void reportErrors()
-  {
-    reportErrors(false);
-  }
-
-  protected void reportErrors(final boolean saving)
-  {
-    if (errorMessage != null)
-    {
-      final String finalErrorMessage = errorMessage;
-      if (raiseGUI)
-      {
-        javax.swing.SwingUtilities.invokeLater(new Runnable()
-        {
-          @Override
-          public void run()
-          {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                    finalErrorMessage,
-                    "Error " + (saving ? "saving" : "loading")
-                            + " Jalview file",
-                    JvOptionPane.WARNING_MESSAGE);
-          }
-        });
-      }
-      else
-      {
-        System.err.println("Problem loading Jalview file: " + errorMessage);
-      }
-    }
-    errorMessage = null;
-  }
-
-  Map<String, String> alreadyLoadedPDB = new HashMap<>();
-
-  /**
-   * when set, local views will be updated from view stored in JalviewXML
-   * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
-   * sync if this is set to true.
-   */
-  private final boolean updateLocalViews = false;
-
-  /**
-   * Returns the path to a temporary file holding the PDB file for the given PDB
-   * id. The first time of asking, searches for a file of that name in the
-   * Jalview project jar, and copies it to a new temporary file. Any repeat
-   * requests just return the path to the file previously created.
-   * 
-   * @param jprovider
-   * @param pdbId
-   * @return
-   */
-  String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
-          String origFile)
-  {
-    if (alreadyLoadedPDB.containsKey(pdbId))
-    {
-      return alreadyLoadedPDB.get(pdbId).toString();
-    }
-
-    String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
-            origFile);
-    if (tempFile != null)
-    {
-      alreadyLoadedPDB.put(pdbId, tempFile);
-    }
-    return tempFile;
-  }
-
-  /**
-   * Copies the jar entry of given name to a new temporary file and returns the
-   * path to the file, or null if the entry is not found.
-   * 
-   * @param jprovider
-   * @param jarEntryName
-   * @param prefix
-   *          a prefix for the temporary file name, must be at least three
-   *          characters long
-   * @param suffixModel
-   *          null or original file - so new file can be given the same suffix
-   *          as the old one
-   * @return
-   */
-  protected String copyJarEntry(jarInputStreamProvider jprovider,
-          String jarEntryName, String prefix, String suffixModel)
-  {
-    String suffix = ".tmp";
-    if (suffixModel == null)
-    {
-      suffixModel = jarEntryName;
-    }
-    int sfpos = suffixModel.lastIndexOf(".");
-    if (sfpos > -1 && sfpos < (suffixModel.length() - 1))
-    {
-      suffix = "." + suffixModel.substring(sfpos + 1);
-    }
-
-    try (JarInputStream jin = jprovider.getJarInputStream())
-    {
-      JarEntry entry = null;
-      do
-      {
-        entry = jin.getNextJarEntry();
-      } while (entry != null && !entry.getName().equals(jarEntryName));
-
-      if (entry != null)
-      {
-        // in = new BufferedReader(new InputStreamReader(jin, UTF_8));
-        File outFile = File.createTempFile(prefix, suffix);
-        outFile.deleteOnExit();
-        try (OutputStream os = new FileOutputStream(outFile))
-        {
-          copyAll(jin, os);
-        }
-        String t = outFile.getAbsolutePath();
-        return t;
-      }
-      else
-      {
-        Console.warn(
-                "Couldn't find entry in Jalview Jar for " + jarEntryName);
-      }
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
-
-    return null;
-  }
-
-  private class JvAnnotRow
-  {
-    public JvAnnotRow(int i, AlignmentAnnotation jaa)
-    {
-      order = i;
-      template = jaa;
-    }
-
-    /**
-     * persisted version of annotation row from which to take vis properties
-     */
-    public jalview.datamodel.AlignmentAnnotation template;
-
-    /**
-     * original position of the annotation row in the alignment
-     */
-    public int order;
-  }
-
-  /**
-   * Load alignment frame from jalview XML DOM object
-   * 
-   * @param jalviewModel
-   *          DOM
-   * @param file
-   *          filename source string
-   * @param loadTreesAndStructures
-   *          when false only create Viewport
-   * @param jprovider
-   *          data source provider
-   * @return alignment frame created from view stored in DOM
-   */
-  AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
-          boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
-  {
-    SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet()
-            .get(0);
-    List<Sequence> vamsasSeqs = vamsasSet.getSequence();
-
-    // JalviewModelSequence jms = object.getJalviewModelSequence();
-
-    // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
-    // : null;
-    Viewport view = (jalviewModel.getViewport().size() > 0)
-            ? jalviewModel.getViewport().get(0)
-            : null;
-
-    // ////////////////////////////////
-    // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
-    //
-    //
-    // If we just load in the same jar file again, the sequenceSetId
-    // will be the same, and we end up with multiple references
-    // to the same sequenceSet. We must modify this id on load
-    // so that each load of the file gives a unique id
-
-    /**
-     * used to resolve correct alignment dataset for alignments with multiple
-     * views
-     */
-    String uniqueSeqSetId = null;
-    String viewId = null;
-    if (view != null)
-    {
-      uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
-      viewId = (view.getId() == null ? null
-              : view.getId() + uniqueSetSuffix);
-    }
-
-    // ////////////////////////////////
-    // LOAD SEQUENCES
-
-    List<SequenceI> hiddenSeqs = null;
-
-    List<SequenceI> tmpseqs = new ArrayList<>();
-
-    boolean multipleView = false;
-    SequenceI referenceseqForView = null;
-    // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
-    List<JSeq> jseqs = jalviewModel.getJSeq();
-    int vi = 0; // counter in vamsasSeq array
-    for (int i = 0; i < jseqs.size(); i++)
-    {
-      JSeq jseq = jseqs.get(i);
-      String seqId = jseq.getId();
-
-      SequenceI tmpSeq = seqRefIds.get(seqId);
-      if (tmpSeq != null)
-      {
-        if (!incompleteSeqs.containsKey(seqId))
-        {
-          // may not need this check, but keep it for at least 2.9,1 release
-          if (tmpSeq.getStart() != jseq.getStart()
-                  || tmpSeq.getEnd() != jseq.getEnd())
-          {
-            System.err.println(String.format(
-                    "Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
-                    tmpSeq.getName(), tmpSeq.getStart(), tmpSeq.getEnd(),
-                    jseq.getStart(), jseq.getEnd()));
-          }
-        }
-        else
-        {
-          incompleteSeqs.remove(seqId);
-        }
-        if (vamsasSeqs.size() > vi
-                && vamsasSeqs.get(vi).getId().equals(seqId))
-        {
-          // most likely we are reading a dataset XML document so
-          // update from vamsasSeq section of XML for this sequence
-          tmpSeq.setName(vamsasSeqs.get(vi).getName());
-          tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription());
-          tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence());
-          vi++;
-        }
-        else
-        {
-          // reading multiple views, so vamsasSeq set is a subset of JSeq
-          multipleView = true;
-        }
-        tmpSeq.setStart(jseq.getStart());
-        tmpSeq.setEnd(jseq.getEnd());
-        tmpseqs.add(tmpSeq);
-      }
-      else
-      {
-        Sequence vamsasSeq = vamsasSeqs.get(vi);
-        tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(),
-                vamsasSeq.getSequence());
-        tmpSeq.setDescription(vamsasSeq.getDescription());
-        tmpSeq.setStart(jseq.getStart());
-        tmpSeq.setEnd(jseq.getEnd());
-        tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
-        seqRefIds.put(vamsasSeq.getId(), tmpSeq);
-        tmpseqs.add(tmpSeq);
-        vi++;
-      }
-
-      if (safeBoolean(jseq.isViewreference()))
-      {
-        referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
-      }
-
-      if (jseq.isHidden() != null && jseq.isHidden().booleanValue())
-      {
-        if (hiddenSeqs == null)
-        {
-          hiddenSeqs = new ArrayList<>();
-        }
 
-        hiddenSeqs.add(tmpSeq);
-      }
-    }
-
-    // /
-    // Create the alignment object from the sequence set
-    // ///////////////////////////////
-    SequenceI[] orderedSeqs = tmpseqs
-            .toArray(new SequenceI[tmpseqs.size()]);
-
-    AlignmentI al = null;
-    // so we must create or recover the dataset alignment before going further
-    // ///////////////////////////////
-    if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
-    {
-      // older jalview projects do not have a dataset - so creat alignment and
-      // dataset
-      al = new Alignment(orderedSeqs);
-      al.setDataset(null);
-    }
-    else
-    {
-      boolean isdsal = jalviewModel.getViewport().isEmpty();
-      if (isdsal)
-      {
-        // we are importing a dataset record, so
-        // recover reference to an alignment already materialsed as dataset
-        al = getDatasetFor(vamsasSet.getDatasetId());
-      }
-      if (al == null)
-      {
-        // materialse the alignment
-        al = new Alignment(orderedSeqs);
-      }
-      if (isdsal)
-      {
-        addDatasetRef(vamsasSet.getDatasetId(), al);
-      }
-
-      // finally, verify all data in vamsasSet is actually present in al
-      // passing on flag indicating if it is actually a stored dataset
-      recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
-    }
-
-    if (referenceseqForView != null)
-    {
-      al.setSeqrep(referenceseqForView);
-    }
-    // / Add the alignment properties
-    for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++)
-    {
-      SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties()
-              .get(i);
-      al.setProperty(ssp.getKey(), ssp.getValue());
-    }
-
-    // ///////////////////////////////
-
-    Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
-    if (!multipleView)
-    {
-      // load sequence features, database references and any associated PDB
-      // structures for the alignment
-      //
-      // prior to 2.10, this part would only be executed the first time a
-      // sequence was encountered, but not afterwards.
-      // now, for 2.10 projects, this is also done if the xml doc includes
-      // dataset sequences not actually present in any particular view.
-      //
-      for (int i = 0; i < vamsasSeqs.size(); i++)
-      {
-        JSeq jseq = jseqs.get(i);
-        if (jseq.getFeatures().size() > 0)
-        {
-          List<Feature> features = jseq.getFeatures();
-          for (int f = 0; f < features.size(); f++)
-          {
-            Feature feat = features.get(f);
-            SequenceFeature sf = new SequenceFeature(feat.getType(),
-                    feat.getDescription(), feat.getBegin(), feat.getEnd(),
-                    safeFloat(feat.getScore()), feat.getFeatureGroup());
-            sf.setStatus(feat.getStatus());
-
-            /*
-             * load any feature attributes - include map-valued attributes
-             */
-            Map<String, Map<String, String>> mapAttributes = new HashMap<>();
-            for (int od = 0; od < feat.getOtherData().size(); od++)
-            {
-              OtherData keyValue = feat.getOtherData().get(od);
-              String attributeName = keyValue.getKey();
-              String attributeValue = keyValue.getValue();
-              if (attributeName.startsWith("LINK"))
-              {
-                sf.addLink(attributeValue);
-              }
-              else
-              {
-                String subAttribute = keyValue.getKey2();
-                if (subAttribute == null)
-                {
-                  // simple string-valued attribute
-                  sf.setValue(attributeName, attributeValue);
-                }
-                else
-                {
-                  // attribute 'key' has sub-attribute 'key2'
-                  if (!mapAttributes.containsKey(attributeName))
-                  {
-                    mapAttributes.put(attributeName, new HashMap<>());
-                  }
-                  mapAttributes.get(attributeName).put(subAttribute,
-                          attributeValue);
-                }
-              }
-            }
-            for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
-                    .entrySet())
-            {
-              sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
-            }
-
-            // adds feature to datasequence's feature set (since Jalview 2.10)
-            al.getSequenceAt(i).addSequenceFeature(sf);
-          }
-        }
-        if (vamsasSeqs.get(i).getDBRef().size() > 0)
-        {
-          // adds dbrefs to datasequence's set (since Jalview 2.10)
-          addDBRefs(
-                  al.getSequenceAt(i).getDatasetSequence() == null
-                          ? al.getSequenceAt(i)
-                          : al.getSequenceAt(i).getDatasetSequence(),
-                  vamsasSeqs.get(i));
-        }
-        if (jseq.getPdbids().size() > 0)
-        {
-          List<Pdbids> ids = jseq.getPdbids();
-          for (int p = 0; p < ids.size(); p++)
-          {
-            Pdbids pdbid = ids.get(p);
-            jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
-            entry.setId(pdbid.getId());
-            if (pdbid.getType() != null)
-            {
-              if (PDBEntry.Type.getType(pdbid.getType()) != null)
-              {
-                entry.setType(PDBEntry.Type.getType(pdbid.getType()));
-              }
-              else
-              {
-                entry.setType(PDBEntry.Type.FILE);
-              }
-            }
-            // jprovider is null when executing 'New View'
-            if (pdbid.getFile() != null && jprovider != null)
-            {
-              if (!pdbloaded.containsKey(pdbid.getFile()))
-              {
-                entry.setFile(loadPDBFile(jprovider, pdbid.getId(),
-                        pdbid.getFile()));
-              }
-              else
-              {
-                entry.setFile(pdbloaded.get(pdbid.getId()).toString());
-              }
-            }
-            /*
-            if (pdbid.getPdbentryItem() != null)
-            {
-              for (PdbentryItem item : pdbid.getPdbentryItem())
-              {
-                for (Property pr : item.getProperty())
-                {
-                  entry.setProperty(pr.getName(), pr.getValue());
-                }
-              }
-            }
-            */
-            for (Property prop : pdbid.getProperty())
-            {
-              entry.setProperty(prop.getName(), prop.getValue());
-            }
-            StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance)
-                    .registerPDBEntry(entry);
-            // adds PDBEntry to datasequence's set (since Jalview 2.10)
-            if (al.getSequenceAt(i).getDatasetSequence() != null)
-            {
-              al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
-            }
-            else
-            {
-              al.getSequenceAt(i).addPDBId(entry);
-            }
-          }
-        }
-      }
-    } // end !multipleview
-
-    // ///////////////////////////////
-    // LOAD SEQUENCE MAPPINGS
-
-    if (vamsasSet.getAlcodonFrame().size() > 0)
-    {
-      // TODO Potentially this should only be done once for all views of an
-      // alignment
-      List<AlcodonFrame> alc = vamsasSet.getAlcodonFrame();
-      for (int i = 0; i < alc.size(); i++)
-      {
-        AlignedCodonFrame cf = new AlignedCodonFrame();
-        if (alc.get(i).getAlcodMap().size() > 0)
-        {
-          List<AlcodMap> maps = alc.get(i).getAlcodMap();
-          for (int m = 0; m < maps.size(); m++)
-          {
-            AlcodMap map = maps.get(m);
-            SequenceI dnaseq = seqRefIds.get(map.getDnasq());
-            // Load Mapping
-            jalview.datamodel.Mapping mapping = null;
-            // attach to dna sequence reference.
-            if (map.getMapping() != null)
-            {
-              mapping = addMapping(map.getMapping());
-              if (dnaseq != null && mapping.getTo() != null)
-              {
-                cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
-              }
-              else
-              {
-                // defer to later
-                frefedSequence
-                        .add(newAlcodMapRef(map.getDnasq(), cf, mapping));
-              }
-            }
-          }
-          al.addCodonFrame(cf);
-        }
-      }
-    }
-
-    // ////////////////////////////////
-    // LOAD ANNOTATIONS
-    List<JvAnnotRow> autoAlan = new ArrayList<>();
-
-    /*
-     * store any annotations which forward reference a group's ID
+    /**
+     * Write the data to a new entry of given name in the output jar file
+     * 
+     * @param jout
+     * @param jarEntryName
+     * @param data
+     * @throws IOException
      */
-    Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<>();
-
-    if (vamsasSet.getAnnotation().size()/*Count()*/ > 0)
-    {
-      List<Annotation> an = vamsasSet.getAnnotation();
-
-      for (int i = 0; i < an.size(); i++)
-      {
-        Annotation annotation = an.get(i);
-
-        /**
-         * test if annotation is automatically calculated for this view only
-         */
-        boolean autoForView = false;
-        if (annotation.getLabel().equals("Quality")
-                || annotation.getLabel().equals("Conservation")
-                || annotation.getLabel().equals("Consensus"))
-        {
-          // Kludge for pre 2.5 projects which lacked the autocalculated flag
-          autoForView = true;
-          // JAXB has no has() test; schema defaults value to false
-          // if (!annotation.hasAutoCalculated())
-          // {
-          // annotation.setAutoCalculated(true);
-          // }
-        }
-        if (autoForView || annotation.isAutoCalculated())
-        {
-          // remove ID - we don't recover annotation from other views for
-          // view-specific annotation
-          annotation.setId(null);
-        }
-
-        // set visibility for other annotation in this view
-        String annotationId = annotation.getId();
-        if (annotationId != null && annotationIds.containsKey(annotationId))
-        {
-          AlignmentAnnotation jda = annotationIds.get(annotationId);
-          // in principle Visible should always be true for annotation displayed
-          // in multiple views
-          if (annotation.isVisible() != null)
-          {
-            jda.visible = annotation.isVisible();
-          }
-
-          al.addAnnotation(jda);
-
-          continue;
-        }
-        // Construct new annotation from model.
-        List<AnnotationElement> ae = annotation.getAnnotationElement();
-        jalview.datamodel.Annotation[] anot = null;
-        java.awt.Color firstColour = null;
-        int anpos;
-        if (!annotation.isScoreOnly())
-        {
-          anot = new jalview.datamodel.Annotation[al.getWidth()];
-          for (int aa = 0; aa < ae.size() && aa < anot.length; aa++)
-          {
-            AnnotationElement annElement = ae.get(aa);
-            anpos = annElement.getPosition();
-
-            if (anpos >= anot.length)
-            {
-              continue;
-            }
-
-            float value = safeFloat(annElement.getValue());
-            anot[anpos] = new jalview.datamodel.Annotation(
-                    annElement.getDisplayCharacter(),
-                    annElement.getDescription(),
-                    (annElement.getSecondaryStructure() == null
-                            || annElement.getSecondaryStructure()
-                                    .length() == 0)
-                                            ? ' '
-                                            : annElement
-                                                    .getSecondaryStructure()
-                                                    .charAt(0),
-                    value);
-            anot[anpos].colour = new Color(safeInt(annElement.getColour()));
-            if (firstColour == null)
-            {
-              firstColour = anot[anpos].colour;
-            }
-          }
-        }
-        jalview.datamodel.AlignmentAnnotation jaa = null;
-
-        if (annotation.isGraph())
-        {
-          float llim = 0, hlim = 0;
-          // if (autoForView || an[i].isAutoCalculated()) {
-          // hlim=11f;
-          // }
-          jaa = new jalview.datamodel.AlignmentAnnotation(
-                  annotation.getLabel(), annotation.getDescription(), anot,
-                  llim, hlim, safeInt(annotation.getGraphType()));
-
-          jaa.graphGroup = safeInt(annotation.getGraphGroup());
-          jaa._linecolour = firstColour;
-          if (annotation.getThresholdLine() != null)
-          {
-            jaa.setThreshold(new jalview.datamodel.GraphLine(
-                    safeFloat(annotation.getThresholdLine().getValue()),
-                    annotation.getThresholdLine().getLabel(),
-                    new java.awt.Color(safeInt(
-                            annotation.getThresholdLine().getColour()))));
-          }
-          if (autoForView || annotation.isAutoCalculated())
-          {
-            // Hardwire the symbol display line to ensure that labels for
-            // histograms are displayed
-            jaa.hasText = true;
-          }
-        }
-        else
-        {
-          jaa = new jalview.datamodel.AlignmentAnnotation(
-                  annotation.getLabel(), annotation.getDescription(), anot);
-          jaa._linecolour = firstColour;
-        }
-        // register new annotation
-        if (annotation.getId() != null)
-        {
-          annotationIds.put(annotation.getId(), jaa);
-          jaa.annotationId = annotation.getId();
-        }
-        // recover sequence association
-        String sequenceRef = annotation.getSequenceRef();
-        if (sequenceRef != null)
-        {
-          // from 2.9 sequenceRef is to sequence id (JAL-1781)
-          SequenceI sequence = seqRefIds.get(sequenceRef);
-          if (sequence == null)
-          {
-            // in pre-2.9 projects sequence ref is to sequence name
-            sequence = al.findName(sequenceRef);
-          }
-          if (sequence != null)
-          {
-            jaa.createSequenceMapping(sequence, 1, true);
-            sequence.addAlignmentAnnotation(jaa);
-          }
-        }
-        // and make a note of any group association
-        if (annotation.getGroupRef() != null
-                && annotation.getGroupRef().length() > 0)
-        {
-          List<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
-                  .get(annotation.getGroupRef());
-          if (aal == null)
-          {
-            aal = new ArrayList<>();
-            groupAnnotRefs.put(annotation.getGroupRef(), aal);
-          }
-          aal.add(jaa);
-        }
-
-        if (annotation.getScore() != null)
-        {
-          jaa.setScore(annotation.getScore().doubleValue());
-        }
-        if (annotation.isVisible() != null)
-        {
-          jaa.visible = annotation.isVisible().booleanValue();
-        }
-
-        if (annotation.isCentreColLabels() != null)
-        {
-          jaa.centreColLabels = annotation.isCentreColLabels()
-                  .booleanValue();
-        }
-
-        if (annotation.isScaleColLabels() != null)
-        {
-          jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue();
-        }
-        if (annotation.isAutoCalculated())
-        {
-          // newer files have an 'autoCalculated' flag and store calculation
-          // state in viewport properties
-          jaa.autoCalculated = true; // means annotation will be marked for
-          // update at end of load.
-        }
-        if (annotation.getGraphHeight() != null)
-        {
-          jaa.graphHeight = annotation.getGraphHeight().intValue();
-        }
-        jaa.belowAlignment = annotation.isBelowAlignment();
-        jaa.setCalcId(annotation.getCalcId());
-        if (annotation.getProperty().size() > 0)
-        {
-          for (Annotation.Property prop : annotation.getProperty())
-          {
-            jaa.setProperty(prop.getName(), prop.getValue());
-          }
-        }
-        if (jaa.autoCalculated)
-        {
-          autoAlan.add(new JvAnnotRow(i, jaa));
-        }
-        else
-        // if (!autoForView)
-        {
-          // add autocalculated group annotation and any user created annotation
-          // for the view
-          al.addAnnotation(jaa);
-        }
-      }
-    }
-    // ///////////////////////
-    // LOAD GROUPS
-    // Create alignment markup and styles for this view
-    if (jalviewModel.getJGroup().size() > 0)
-    {
-      List<JGroup> groups = jalviewModel.getJGroup();
-      boolean addAnnotSchemeGroup = false;
-      for (int i = 0; i < groups.size(); i++)
-      {
-        JGroup jGroup = groups.get(i);
-        ColourSchemeI cs = null;
-        if (jGroup.getColour() != null)
-        {
-          if (jGroup.getColour().startsWith("ucs"))
-          {
-            cs = getUserColourScheme(jalviewModel, jGroup.getColour());
-          }
-          else if (jGroup.getColour().equals("AnnotationColourGradient")
-                  && jGroup.getAnnotationColours() != null)
-          {
-            addAnnotSchemeGroup = true;
-          }
-          else
-          {
-            cs = ColourSchemeProperty.getColourScheme(null, al,
-                    jGroup.getColour());
-          }
-        }
-        int pidThreshold = safeInt(jGroup.getPidThreshold());
-
-        Vector<SequenceI> seqs = new Vector<>();
-
-        for (int s = 0; s < jGroup.getSeq().size(); s++)
-        {
-          String seqId = jGroup.getSeq().get(s);
-          SequenceI ts = seqRefIds.get(seqId);
-
-          if (ts != null)
-          {
-            seqs.addElement(ts);
-          }
-        }
-
-        if (seqs.size() < 1)
-        {
-          continue;
-        }
-
-        SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
-                safeBoolean(jGroup.isDisplayBoxes()),
-                safeBoolean(jGroup.isDisplayText()),
-                safeBoolean(jGroup.isColourText()),
-                safeInt(jGroup.getStart()), safeInt(jGroup.getEnd()));
-        sg.getGroupColourScheme().setThreshold(pidThreshold, true);
-        sg.getGroupColourScheme()
-                .setConservationInc(safeInt(jGroup.getConsThreshold()));
-        sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour())));
-
-        sg.textColour = new Color(safeInt(jGroup.getTextCol1()));
-        sg.textColour2 = new Color(safeInt(jGroup.getTextCol2()));
-        sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved()));
-        sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold());
-        // attributes with a default in the schema are never null
-        sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
-        sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
-        sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
-        sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus());
-        if (jGroup.getConsThreshold() != null
-                && jGroup.getConsThreshold().intValue() != 0)
-        {
-          Conservation c = new Conservation("All", sg.getSequences(null), 0,
-                  sg.getWidth() - 1);
-          c.calculate();
-          c.verdict(false, 25);
-          sg.cs.setConservation(c);
-        }
-
-        if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
-        {
-          // re-instate unique group/annotation row reference
-          List<AlignmentAnnotation> jaal = groupAnnotRefs
-                  .get(jGroup.getId());
-          if (jaal != null)
-          {
-            for (AlignmentAnnotation jaa : jaal)
-            {
-              jaa.groupRef = sg;
-              if (jaa.autoCalculated)
-              {
-                // match up and try to set group autocalc alignment row for this
-                // annotation
-                if (jaa.label.startsWith("Consensus for "))
-                {
-                  sg.setConsensus(jaa);
-                }
-                // match up and try to set group autocalc alignment row for this
-                // annotation
-                if (jaa.label.startsWith("Conservation for "))
-                {
-                  sg.setConservationRow(jaa);
-                }
-              }
-            }
-          }
-        }
-        al.addGroup(sg);
-        if (addAnnotSchemeGroup)
-        {
-          // reconstruct the annotation colourscheme
-          sg.setColourScheme(
-                  constructAnnotationColour(jGroup.getAnnotationColours(),
-                          null, al, jalviewModel, false));
-        }
-      }
-    }
-    if (view == null)
+    protected void writeJarEntry(JarOutputStream jout, String jarEntryName,
+                                byte[] data) throws IOException
     {
-      // only dataset in this model, so just return.
-      return null;
+       if (jout != null)
+           {
+               jarEntryName = jarEntryName.replace('\\','/');
+               System.out.println("Writing jar entry " + jarEntryName);
+               jout.putNextEntry(new JarEntry(jarEntryName));
+               DataOutputStream dout = new DataOutputStream(jout);
+               dout.write(data, 0, data.length);
+               dout.flush();
+               jout.closeEntry();
+           }
     }
-    // ///////////////////////////////
-    // LOAD VIEWPORT
-
-    AlignFrame af = null;
-    AlignViewport av = null;
-    // now check to see if we really need to create a new viewport.
-    if (multipleView && viewportsAdded.size() == 0)
-    {
-      // We recovered an alignment for which a viewport already exists.
-      // TODO: fix up any settings necessary for overlaying stored state onto
-      // state recovered from another document. (may not be necessary).
-      // we may need a binding from a viewport in memory to one recovered from
-      // XML.
-      // and then recover its containing af to allow the settings to be applied.
-      // TODO: fix for vamsas demo
-      System.err.println(
-              "About to recover a viewport for existing alignment: Sequence set ID is "
-                      + uniqueSeqSetId);
-      Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
-      if (seqsetobj != null)
-      {
-        if (seqsetobj instanceof String)
-        {
-          uniqueSeqSetId = (String) seqsetobj;
-          System.err.println(
-                  "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
-                          + uniqueSeqSetId);
-        }
-        else
-        {
-          System.err.println(
-                  "Warning : Collision between sequence set ID string and existing jalview object mapping.");
-        }
 
-      }
-    }
     /**
-     * indicate that annotation colours are applied across all groups (pre
-     * Jalview 2.8.1 behaviour)
+     * Copies input to output, in 4K buffers; handles any data (text or binary)
+     * 
+     * @param in
+     * @param out
+     * @throws IOException
      */
-    boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
-            jalviewModel.getVersion());
-
-    AlignmentPanel ap = null;
-    boolean isnewview = true;
-    if (viewId != null)
-    {
-      // Check to see if this alignment already has a view id == viewId
-      jalview.gui.AlignmentPanel views[] = Desktop
-              .getAlignmentPanels(uniqueSeqSetId);
-      if (views != null && views.length > 0)
-      {
-        for (int v = 0; v < views.length; v++)
-        {
-          if (views[v].av.getViewId().equalsIgnoreCase(viewId))
-          {
-            // recover the existing alignpanel, alignframe, viewport
-            af = views[v].alignFrame;
-            av = views[v].av;
-            ap = views[v];
-            // TODO: could even skip resetting view settings if we don't want to
-            // change the local settings from other jalview processes
-            isnewview = false;
-          }
-        }
-      }
-    }
-
-    if (isnewview)
+    protected void copyAll(InputStream in, OutputStream out)
+       throws IOException
     {
-      af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
-              uniqueSeqSetId, viewId, autoAlan);
-      av = af.getViewport();
-      ap = af.alignPanel;
+       byte[] buffer = new byte[4096];
+       int bytesRead = 0;
+       while ((bytesRead = in.read(buffer)) != -1)
+           {
+               out.write(buffer, 0, bytesRead);
+           }
     }
 
-    /*
-     * Load any trees, PDB structures and viewers
+    /**
+     * Save the state of a structure viewer
      * 
-     * Not done if flag is false (when this method is used for New View)
+     * @param ap
+     * @param jds
+     * @param pdb
+     *          the archive XML element under which to save the state
+     * @param entry
+     * @param viewIds
+     * @param matchedFile
+     * @param viewFrame
+     * @return
      */
-    if (loadTreesAndStructures)
-    {
-      loadTrees(jalviewModel, view, af, av, ap);
-      loadPCAViewers(jalviewModel, ap);
-      loadPDBStructures(jprovider, jseqs, af, ap);
-      loadRnaViewers(jprovider, jseqs, ap);
+    protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
+                                        Pdbids pdb, PDBEntry entry, List<String> viewIds,
+                                        String matchedFile, StructureViewerBase viewFrame)
+    {
+       final AAStructureBindingModel bindingModel = viewFrame.getBinding();
+
+       /*
+        * Look for any bindings for this viewer to the PDB file of interest
+        * (including part matches excluding chain id)
+        */
+       for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
+           {
+               final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
+               final String pdbId = pdbentry.getId();
+               if (!pdbId.equals(entry.getId())
+                   && !(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
+                        */
+                       continue;
+                   }
+               if (matchedFile == null)
+                   {
+                       matchedFile = pdbentry.getFile();
+                   }
+               else if (!matchedFile.equals(pdbentry.getFile()))
+                   {
+                       Console.warn(
+                                    "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
+                                    + pdbentry.getFile());
+                   }
+               // record the
+               // file so we
+               // can get at it if the ID
+               // match is ambiguous (e.g.
+               // 1QIP==1qipA)
+
+               for (int smap = 0; smap < viewFrame.getBinding()
+                        .getSequence()[peid].length; smap++)
+                   {
+                       // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+                       if (jds == viewFrame.getBinding().getSequence()[peid][smap])
+                           {
+                               StructureState state = new StructureState();
+                               state.setVisible(true);
+                               state.setXpos(viewFrame.getX());
+                               state.setYpos(viewFrame.getY());
+                               state.setWidth(viewFrame.getWidth());
+                               state.setHeight(viewFrame.getHeight());
+                               final String viewId = viewFrame.getViewId();
+                               state.setViewId(viewId);
+                               state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
+                               state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap));
+                               state.setColourByJmol(viewFrame.isColouredByViewer());
+                               state.setType(viewFrame.getViewerType().toString());
+                               // pdb.addStructureState(state);
+                               pdb.getStructureState().add(state);
+                           }
+                   }
+           }
+       return matchedFile;
     }
-    // and finally return.
-    return af;
-  }
 
-  /**
-   * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna
-   * panel is restored from separate jar entries, two (gapped and trimmed) per
-   * sequence and secondary structure.
-   * 
-   * Currently each viewer shows just one sequence and structure (gapped and
-   * trimmed), however this method is designed to support multiple sequences or
-   * structures in viewers if wanted in future.
-   * 
-   * @param jprovider
-   * @param jseqs
-   * @param ap
-   */
-  private void loadRnaViewers(jarInputStreamProvider jprovider,
-          List<JSeq> jseqs, AlignmentPanel ap)
-  {
-    /*
-     * scan the sequences for references to viewers; create each one the first
-     * time it is referenced, add Rna models to existing viewers
+    /**
+     * Populates the AnnotationColourScheme xml for save. This captures the
+     * settings of the options in the 'Colour by Annotation' dialog.
+     * 
+     * @param acg
+     * @param userColours
+     * @param jm
+     * @return
      */
-    for (JSeq jseq : jseqs)
-    {
-      for (int i = 0; i < jseq.getRnaViewer().size(); i++)
-      {
-        RnaViewer viewer = jseq.getRnaViewer().get(i);
-        AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
-                ap);
+    private AnnotationColourScheme constructAnnotationColours(
+                                                             AnnotationColourGradient acg, List<UserColourScheme> userColours,
+                                                             JalviewModel jm)
+    {
+       AnnotationColourScheme ac = new AnnotationColourScheme();
+       ac.setAboveThreshold(acg.getAboveThreshold());
+       ac.setThreshold(acg.getAnnotationThreshold());
+       // 2.10.2 save annotationId (unique) not annotation label
+       ac.setAnnotation(acg.getAnnotation().annotationId);
+       if (acg.getBaseColour() instanceof UserColourScheme)
+           {
+               ac.setColourScheme(
+                                  setUserColourScheme(acg.getBaseColour(), userColours, jm));
+           }
+       else
+           {
+               ac.setColourScheme(
+                                  ColourSchemeProperty.getColourName(acg.getBaseColour()));
+           }
+
+       ac.setMaxColour(acg.getMaxColour().getRGB());
+       ac.setMinColour(acg.getMinColour().getRGB());
+       ac.setPerSequence(acg.isSeqAssociated());
+       ac.setPredefinedColours(acg.isPredefinedColours());
+       return ac;
+    }
+
+    private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
+                                         IdentityHashMap<SequenceGroup, String> groupRefs,
+                                         AlignmentViewport av, Set<String> calcIdSet, boolean storeDS,
+                                         SequenceSet vamsasSet)
+    {
+
+       for (int i = 0; i < aa.length; i++)
+           {
+               Annotation an = new Annotation();
+
+               AlignmentAnnotation annotation = aa[i];
+               if (annotation.annotationId != null)
+                   {
+                       annotationIds.put(annotation.annotationId, annotation);
+                   }
+
+               an.setId(annotation.annotationId);
+
+               an.setVisible(annotation.visible);
+
+               an.setDescription(annotation.description);
+
+               if (annotation.sequenceRef != null)
+                   {
+                       // 2.9 JAL-1781 xref on sequence id rather than name
+                       an.setSequenceRef(seqsToIds.get(annotation.sequenceRef));
+                   }
+               if (annotation.groupRef != null)
+                   {
+                       String groupIdr = groupRefs.get(annotation.groupRef);
+                       if (groupIdr == null)
+                           {
+                               // make a locally unique String
+                               groupRefs.put(annotation.groupRef,
+                                             groupIdr = ("" + System.currentTimeMillis()
+                                                         + annotation.groupRef.getName()
+                                                         + groupRefs.size()));
+                           }
+                       an.setGroupRef(groupIdr.toString());
+                   }
+
+               // store all visualization attributes for annotation
+               an.setGraphHeight(annotation.graphHeight);
+               an.setCentreColLabels(annotation.centreColLabels);
+               an.setScaleColLabels(annotation.scaleColLabel);
+               an.setShowAllColLabels(annotation.showAllColLabels);
+               an.setBelowAlignment(annotation.belowAlignment);
+
+               if (annotation.graph > 0)
+                   {
+                       an.setGraph(true);
+                       an.setGraphType(annotation.graph);
+                       an.setGraphGroup(annotation.graphGroup);
+                       if (annotation.getThreshold() != null)
+                           {
+                               ThresholdLine line = new ThresholdLine();
+                               line.setLabel(annotation.getThreshold().label);
+                               line.setValue(annotation.getThreshold().value);
+                               line.setColour(annotation.getThreshold().colour.getRGB());
+                               an.setThresholdLine(line);
+                           }
+                   }
+               else
+                   {
+                       an.setGraph(false);
+                   }
+
+               an.setLabel(annotation.label);
+
+               if (annotation == av.getAlignmentQualityAnnot()
+                   || annotation == av.getAlignmentConservationAnnotation()
+                   || annotation == av.getAlignmentConsensusAnnotation()
+                   || annotation.autoCalculated)
+                   {
+                       // new way of indicating autocalculated annotation -
+                       an.setAutoCalculated(annotation.autoCalculated);
+                   }
+               if (annotation.hasScore())
+                   {
+                       an.setScore(annotation.getScore());
+                   }
+
+               if (annotation.getCalcId() != null)
+                   {
+                       calcIdSet.add(annotation.getCalcId());
+                       an.setCalcId(annotation.getCalcId());
+                   }
+               if (annotation.hasProperties())
+                   {
+                       for (String pr : annotation.getProperties())
+                           {
+                               jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property();
+                               prop.setName(pr);
+                               prop.setValue(annotation.getProperty(pr));
+                               // an.addProperty(prop);
+                               an.getProperty().add(prop);
+                           }
+                   }
+
+               AnnotationElement ae;
+               if (annotation.annotations != null)
+                   {
+                       an.setScoreOnly(false);
+                       for (int a = 0; a < annotation.annotations.length; a++)
+                           {
+                               if ((annotation == null) || (annotation.annotations[a] == null))
+                                   {
+                                       continue;
+                                   }
+
+                               ae = new AnnotationElement();
+                               if (annotation.annotations[a].description != null)
+                                   {
+                                       ae.setDescription(annotation.annotations[a].description);
+                                   }
+                               if (annotation.annotations[a].displayCharacter != null)
+                                   {
+                                       ae.setDisplayCharacter(
+                                                              annotation.annotations[a].displayCharacter);
+                                   }
+
+                               if (!Float.isNaN(annotation.annotations[a].value))
+                                   {
+                                       ae.setValue(annotation.annotations[a].value);
+                                   }
+
+                               ae.setPosition(a);
+                               if (annotation.annotations[a].secondaryStructure > ' ')
+                                   {
+                                       ae.setSecondaryStructure(
+                                                                annotation.annotations[a].secondaryStructure + "");
+                                   }
+
+                               if (annotation.annotations[a].colour != null
+                                   && annotation.annotations[a].colour != java.awt.Color.black)
+                                   {
+                                       ae.setColour(annotation.annotations[a].colour.getRGB());
+                                   }
+
+                               // an.addAnnotationElement(ae);
+                               an.getAnnotationElement().add(ae);
+                               if (annotation.autoCalculated)
+                                   {
+                                       // only write one non-null entry into the annotation row -
+                                       // sufficient to get the visualization attributes necessary to
+                                       // display data
+                                       continue;
+                                   }
+                           }
+                   }
+               else
+                   {
+                       an.setScoreOnly(true);
+                   }
+               if (!storeDS || (storeDS && !annotation.autoCalculated))
+                   {
+                       // skip autocalculated annotation - these are only provided for
+                       // alignments
+                       // vamsasSet.addAnnotation(an);
+                       vamsasSet.getAnnotation().add(an);
+                   }
+           }
+
+    }
+
+    private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
+    {
+       AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
+       if (settings != null)
+           {
+               CalcIdParam vCalcIdParam = new CalcIdParam();
+               vCalcIdParam.setCalcId(calcId);
+               // vCalcIdParam.addServiceURL(settings.getServiceURI());
+               vCalcIdParam.getServiceURL().add(settings.getServiceURI());
+               // generic URI allowing a third party to resolve another instance of the
+               // service used for this calculation
+               for (String url : settings.getServiceURLs())
+                   {
+                       // vCalcIdParam.addServiceURL(urls);
+                       vCalcIdParam.getServiceURL().add(url);
+                   }
+               vCalcIdParam.setVersion("1.0");
+               if (settings.getPreset() != null)
+                   {
+                       WsParamSetI setting = settings.getPreset();
+                       vCalcIdParam.setName(setting.getName());
+                       vCalcIdParam.setDescription(setting.getDescription());
+                   }
+               else
+                   {
+                       vCalcIdParam.setName("");
+                       vCalcIdParam.setDescription("Last used parameters");
+                   }
+               // need to be able to recover 1) settings 2) user-defined presets or
+               // recreate settings from preset 3) predefined settings provided by
+               // service - or settings that can be transferred (or discarded)
+               vCalcIdParam.setParameters(
+                                          settings.getWsParamFile().replace("\n", "|\\n|"));
+               vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
+               // todo - decide if updateImmediately is needed for any projects.
+
+               return vCalcIdParam;
+           }
+       return null;
+    }
+
+    private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
+                                      AlignViewport av)
+    {
+       if (calcIdParam.getVersion().equals("1.0"))
+           {
+               final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]);
+               ServiceWithParameters service = PreferredServiceRegistry.getRegistry()
+                   .getPreferredServiceFor(calcIds);
+               if (service != null)
+                   {
+                       WsParamSetI parmSet = null;
+                       try
+                           {
+                               parmSet = service.getParamStore().parseServiceParameterFile(
+                                                                                           calcIdParam.getName(), calcIdParam.getDescription(),
+                                                                                           calcIds,
+                                                                                           calcIdParam.getParameters().replace("|\\n|", "\n"));
+                           } catch (IOException x)
+                           {
+                               Console.warn("Couldn't parse parameter data for "
+                                            + calcIdParam.getCalcId(), x);
+                               return false;
+                           }
+                       List<ArgumentI> argList = null;
+                       if (calcIdParam.getName().length() > 0)
+                           {
+                               parmSet = service.getParamStore()
+                                   .getPreset(calcIdParam.getName());
+                               if (parmSet != null)
+                                   {
+                                       // TODO : check we have a good match with settings in AACon -
+                                       // otherwise we'll need to create a new preset
+                                   }
+                           }
+                       else
+                           {
+                               argList = parmSet.getArguments();
+                               parmSet = null;
+                           }
+                       AutoCalcSetting settings = new AAConSettings(
+                                                                    calcIdParam.isAutoUpdate(), service, parmSet, argList);
+                       av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
+                                               calcIdParam.isNeedsUpdate());
+                       return true;
+                   }
+               else
+                   {
+                       Console.warn(
+                                    "Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
+                       return false;
+                   }
+           }
+       throw new Error(MessageManager.formatMessage(
+                                                    "error.unsupported_version_calcIdparam", new Object[]
+                                                    { calcIdParam.toString() }));
+    }
 
-        for (int j = 0; j < viewer.getSecondaryStructure().size(); j++)
-        {
-          SecondaryStructure ss = viewer.getSecondaryStructure().get(j);
-          SequenceI seq = seqRefIds.get(jseq.getId());
-          AlignmentAnnotation ann = this.annotationIds
-                  .get(ss.getAnnotationId());
+    /**
+     * External mapping between jalview objects and objects yielding a valid and
+     * unique object ID string. This is null for normal Jalview project IO, but
+     * non-null when a jalview project is being read or written as part of a
+     * vamsas session.
+     */
+    IdentityHashMap jv2vobj = null;
 
-          /*
-           * add the structure to the Varna display (with session state copied
-           * from the jar to a temporary file)
-           */
-          boolean gapped = safeBoolean(ss.isGapped());
-          String rnaTitle = ss.getTitle();
-          String sessionState = ss.getViewerState();
-          String tempStateFile = copyJarEntry(jprovider, sessionState,
-                  "varna", null);
-          RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
-          appVarna.addModelSession(rna, rnaTitle, tempStateFile);
-        }
-        appVarna.setInitialSelection(safeInt(viewer.getSelectedRna()));
-      }
+    /**
+     * Construct a unique ID for jvobj using either existing bindings or if none
+     * exist, the result of the hashcode call for the object.
+     * 
+     * @param jvobj
+     *          jalview data object
+     * @return unique ID for referring to jvobj
+     */
+    private String makeHashCode(Object jvobj, String altCode)
+    {
+       if (jv2vobj != null)
+           {
+               Object id = jv2vobj.get(jvobj);
+               if (id != null)
+                   {
+                       return id.toString();
+                   }
+               // check string ID mappings
+               if (jvids2vobj != null && jvobj instanceof String)
+                   {
+                       id = jvids2vobj.get(jvobj);
+                   }
+               if (id != null)
+                   {
+                       return id.toString();
+                   }
+               // give up and warn that something has gone wrong
+               Console.warn(
+                            "Cannot find ID for object in external mapping : " + jvobj);
+           }
+       return altCode;
     }
-  }
 
-  /**
-   * Locate and return an already instantiated matching AppVarna, or create one
-   * if not found
-   * 
-   * @param viewer
-   * @param viewIdSuffix
-   * @param ap
-   * @return
-   */
-  protected AppVarna findOrCreateVarnaViewer(RnaViewer viewer,
-          String viewIdSuffix, AlignmentPanel ap)
-  {
-    /*
-     * on each load a suffix is appended to the saved viewId, to avoid conflicts
-     * if load is repeated
+    /**
+     * return local jalview object mapped to ID, if it exists
+     * 
+     * @param idcode
+     *          (may be null)
+     * @return null or object bound to idcode
      */
-    String postLoadId = viewer.getViewId() + viewIdSuffix;
-    for (JInternalFrame frame : getAllFrames())
+    private Object retrieveExistingObj(String idcode)
     {
-      if (frame instanceof AppVarna)
-      {
-        AppVarna varna = (AppVarna) frame;
-        if (postLoadId.equals(varna.getViewId()))
-        {
-          // this viewer is already instantiated
-          // could in future here add ap as another 'parent' of the
-          // AppVarna window; currently just 1-to-many
-          return varna;
-        }
-      }
+       if (idcode != null && vobj2jv != null)
+           {
+               return vobj2jv.get(idcode);
+           }
+       return null;
     }
 
-    /*
-     * viewer not found - make it
+    /**
+     * binding from ID strings from external mapping table to jalview data model
+     * objects.
      */
-    RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(),
-            safeInt(viewer.getXpos()), safeInt(viewer.getYpos()),
-            safeInt(viewer.getWidth()), safeInt(viewer.getHeight()),
-            safeInt(viewer.getDividerLocation()));
-    AppVarna varna = new AppVarna(model, ap);
+    private Hashtable vobj2jv;
+
+    private Sequence createVamsasSequence(String id, SequenceI jds)
+    {
+       return createVamsasSequence(true, id, jds, null);
+    }
+
+    private Sequence createVamsasSequence(boolean recurse, String id,
+                                         SequenceI jds, SequenceI parentseq)
+    {
+       Sequence vamsasSeq = new Sequence();
+       vamsasSeq.setId(id);
+       vamsasSeq.setName(jds.getName());
+       vamsasSeq.setSequence(jds.getSequenceAsString());
+       vamsasSeq.setDescription(jds.getDescription());
+       List<DBRefEntry> dbrefs = null;
+       if (jds.getDatasetSequence() != null)
+           {
+               vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
+           }
+       else
+           {
+               // seqId==dsseqid so we can tell which sequences really are
+               // dataset sequences only
+               vamsasSeq.setDsseqid(id);
+               dbrefs = jds.getDBRefs();
+               if (parentseq == null)
+                   {
+                       parentseq = jds;
+                   }
+           }
+
+       /*
+        * save any dbrefs; special subclass GeneLocus is flagged as 'locus'
+        */
+       if (dbrefs != null)
+           {
+               for (int d = 0, nd = dbrefs.size(); d < nd; d++)
+                   {
+                       DBRef dbref = new DBRef();
+                       DBRefEntry ref = dbrefs.get(d);
+                       dbref.setSource(ref.getSource());
+                       dbref.setVersion(ref.getVersion());
+                       dbref.setAccessionId(ref.getAccessionId());
+                       dbref.setCanonical(ref.isCanonical());
+                       if (ref instanceof GeneLocus)
+                           {
+                               dbref.setLocus(true);
+                           }
+                       if (ref.hasMap())
+                           {
+                               Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
+                                                                jds, recurse);
+                               dbref.setMapping(mp);
+                           }
+                       vamsasSeq.getDBRef().add(dbref);
+                   }
+           }
+       return vamsasSeq;
+    }
+
+    private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
+                                       SequenceI parentseq, SequenceI jds, boolean recurse)
+    {
+       Mapping mp = null;
+       if (jmp.getMap() != null)
+           {
+               mp = new Mapping();
+
+               jalview.util.MapList mlst = jmp.getMap();
+               List<int[]> r = mlst.getFromRanges();
+               for (int[] range : r)
+                   {
+                       MapListFrom mfrom = new MapListFrom();
+                       mfrom.setStart(range[0]);
+                       mfrom.setEnd(range[1]);
+                       // mp.addMapListFrom(mfrom);
+                       mp.getMapListFrom().add(mfrom);
+                   }
+               r = mlst.getToRanges();
+               for (int[] range : r)
+                   {
+                       MapListTo mto = new MapListTo();
+                       mto.setStart(range[0]);
+                       mto.setEnd(range[1]);
+                       // mp.addMapListTo(mto);
+                       mp.getMapListTo().add(mto);
+                   }
+               mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
+               mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
+               if (jmp.getTo() != null)
+                   {
+                       // MappingChoice mpc = new MappingChoice();
+
+                       // check/create ID for the sequence referenced by getTo()
+
+                       String jmpid = "";
+                       SequenceI ps = null;
+                       if (parentseq != jmp.getTo()
+                           && parentseq.getDatasetSequence() != jmp.getTo())
+                           {
+                               // chaining dbref rather than a handshaking one
+                               jmpid = seqHash(ps = jmp.getTo());
+                           }
+                       else
+                           {
+                               jmpid = seqHash(ps = parentseq);
+                           }
+                       // mpc.setDseqFor(jmpid);
+                       mp.setDseqFor(jmpid);
+                       if (!seqRefIds.containsKey(jmpid))
+                           {
+                               Console.debug("creatign new DseqFor ID");
+                               seqRefIds.put(jmpid, ps);
+                           }
+                       else
+                           {
+                               Console.debug("reusing DseqFor ID");
+                           }
+
+                       // mp.setMappingChoice(mpc);
+                   }
+           }
+       return mp;
+    }
+
+    String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
+                              List<UserColourScheme> userColours, JalviewModel jm)
+    {
+       String id = null;
+       jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
+       boolean newucs = false;
+       if (!userColours.contains(ucs))
+           {
+               userColours.add(ucs);
+               newucs = true;
+           }
+       id = "ucs" + userColours.indexOf(ucs);
+       if (newucs)
+           {
+               // actually create the scheme's entry in the XML model
+               java.awt.Color[] colours = ucs.getColours();
+               UserColours uc = new UserColours();
+               // UserColourScheme jbucs = new UserColourScheme();
+               JalviewUserColours jbucs = new JalviewUserColours();
+
+               for (int i = 0; i < colours.length; i++)
+                   {
+                       Colour col = new Colour();
+                       col.setName(ResidueProperties.aa[i]);
+                       col.setRGB(jalview.util.Format.getHexString(colours[i]));
+                       // jbucs.addColour(col);
+                       jbucs.getColour().add(col);
+                   }
+               if (ucs.getLowerCaseColours() != null)
+                   {
+                       colours = ucs.getLowerCaseColours();
+                       for (int i = 0; i < colours.length; i++)
+                           {
+                               Colour col = new Colour();
+                               col.setName(ResidueProperties.aa[i].toLowerCase(Locale.ROOT));
+                               col.setRGB(jalview.util.Format.getHexString(colours[i]));
+                               // jbucs.addColour(col);
+                               jbucs.getColour().add(col);
+                           }
+                   }
+
+               uc.setId(id);
+               uc.setUserColourScheme(jbucs);
+               // jm.addUserColours(uc);
+               jm.getUserColours().add(uc);
+           }
+
+       return id;
+    }
+
+    jalview.schemes.UserColourScheme getUserColourScheme(JalviewModel jm,
+                                                        String id)
+    {
+       List<UserColours> uc = jm.getUserColours();
+       UserColours colours = null;
+       /*
+         for (int i = 0; i < uc.length; i++)
+         {
+         if (uc[i].getId().equals(id))
+         {
+         colours = uc[i];
+         break;
+         }
+         }
+       */
+       for (UserColours c : uc)
+           {
+               if (c.getId().equals(id))
+                   {
+                       colours = c;
+                       break;
+                   }
+           }
+
+       java.awt.Color[] newColours = new java.awt.Color[24];
+
+       for (int i = 0; i < 24; i++)
+           {
+               newColours[i] = new java.awt.Color(Integer.parseInt(
+                                                                   // colours.getUserColourScheme().getColour(i).getRGB(), 16));
+                                                                   colours.getUserColourScheme().getColour().get(i).getRGB(),
+                                                                   16));
+           }
+
+       jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
+                                                                                   newColours);
+
+       if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24)
+           {
+               newColours = new java.awt.Color[23];
+               for (int i = 0; i < 23; i++)
+                   {
+                       newColours[i] = new java.awt.Color(
+                                                          Integer.parseInt(colours.getUserColourScheme().getColour()
+                                                                           .get(i + 24).getRGB(), 16));
+                   }
+               ucs.setLowerCaseColours(newColours);
+           }
+
+       return ucs;
+    }
 
-    return varna;
-  }
+    /**
+     * Load a jalview project archive from a jar file
+     * 
+     * @param file
+     *          - HTTP URL or filename
+     */
+    public AlignFrame loadJalviewAlign(final Object file)
+    {
+
+       jalview.gui.AlignFrame af = null;
+
+       try
+           {
+               // create list to store references for any new Jmol viewers created
+               newStructureViewers = new Vector<>();
+               // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
+               // Workaround is to make sure caller implements the JarInputStreamProvider
+               // interface
+               // so we can re-open the jar input stream for each entry.
+
+               jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
+               af = loadJalviewAlign(jprovider);
+               if (af != null)
+                   {
+                       af.setMenusForViewport();
+                   }
+           } catch (MalformedURLException e)
+           {
+               errorMessage = "Invalid URL format for '" + file + "'";
+               reportErrors();
+           } finally
+           {
+               try
+                   {
+                       // was invokeAndWait
+         
+                       // BH 2019 -- can't wait
+                       SwingUtilities.invokeLater(new Runnable()
+                           {
+                               @Override
+                               public void run()
+                               {
+                                   setLoadingFinishedForNewStructureViewers();
+                               }
+                           });
+                   } catch (Exception x)
+                   {
+                       System.err.println("Error loading alignment: " + x.getMessage());
+                   }
+           }
+       this.jarFile = null;
+       return af;
+    }
+
+    @SuppressWarnings("unused")
+    private jarInputStreamProvider createjarInputStreamProvider(
+                                                               final Object ofile) throws MalformedURLException
+    {
+
+       try
+           {
+               String file = (ofile instanceof File
+                              ? ((File) ofile).getCanonicalPath()
+                              : ofile.toString());
+               byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
+                   : null;
+               if (bytes != null)
+                   {
+                       this.jarFile = (File) ofile;
+                   }
+               URL url;
+               errorMessage = null;
+               uniqueSetSuffix = null;
+               seqRefIds = null;
+               viewportsAdded.clear();
+               frefedSequence = null;
+
+               if (HttpUtils.startsWithHttpOrHttps(file))
+                   {
+                       url = new URL(file);
+                   } else {
+                       url = null;
+               }
+               return new jarInputStreamProvider()
+                   {
+
+                       @Override
+                       public JarInputStream getJarInputStream() throws IOException
+                       {
+                           InputStream is = bytes != null ? new ByteArrayInputStream(bytes)
+                               : (url != null ? url.openStream()
+                                  : new FileInputStream(file));
+                           return new JarInputStream(is);
+                       }
+
+                       @Override
+                       public File getFile()
+                       {
+                           return jarFile;
+                       }
+
+                       @Override
+                       public String getFilename()
+                       {
+                           return file;
+                       }
+               };
+           } catch (IOException e)
+           {
+               e.printStackTrace();
+               return null;
+           }
+    }
 
-  /**
-   * Load any saved trees
-   * 
-   * @param jm
-   * @param view
-   * @param af
-   * @param av
-   * @param ap
-   */
-  protected void loadTrees(JalviewModel jm, Viewport view, AlignFrame af,
-          AlignViewport av, AlignmentPanel ap)
-  {
-    // TODO result of automated refactoring - are all these parameters needed?
-    try
-    {
-      for (int t = 0; t < jm.getTree().size(); t++)
-      {
+    /**
+     * Recover jalview session from a jalview project archive. Caller may
+     * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
+     * themselves. Any null fields will be initialised with default values,
+     * non-null fields are left alone.
+     * 
+     * @param jprovider
+     * @return
+     */
+    public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
+    {
+       errorMessage = null;
+       if (uniqueSetSuffix == null)
+           {
+               uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
+           }
+       if (seqRefIds == null)
+           {
+               initSeqRefs();
+           }
+       AlignFrame af = null, _af = null;
+       IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
+       Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
+       String fileName = jprovider.getFilename();
+       File file = jprovider.getFile();
+       List<AlignFrame> alignFrames = new ArrayList<>();
+       try
+           {
+               JarInputStream jin = null;
+               JarEntry jarentry = null;
+               int entryCount = 1;
+
+               // Look for all the entry names ending with ".xml"
+               // This includes all panels and at least one frame.
+               //      Platform.timeCheck(null, Platform.TIME_MARK);
+               do
+                   {
+                       jin = jprovider.getJarInputStream();
+                       for (int i = 0; i < entryCount; i++)
+                           {
+                               jarentry = jin.getNextJarEntry();
+                           }
+                       String name = (jarentry == null ? null : jarentry.getName());
+
+                       //        System.out.println("Jalview2XML opening " + name);
+                       if (name != null && name.endsWith(".xml"))
+                           {
+                               // DataSet for.... is read last.
+          
+          
+                               // The question here is what to do with the two
+                               // .xml files in the jvp file.
+                               // Some number of them, "...Dataset for...", will be the
+                               // Only AlignPanels and will have Viewport.
+                               // One or more will be the source data, with the DBRefs.
+                               //
+                               // JVP file writing (above) ensures tha the AlignPanels are written
+                               // first, then all relevant datasets (which are
+                               // Jalview.datamodel.Alignment).
+                               //
+
+                               //          Platform.timeCheck("Jalview2XML JAXB " + name, Platform.TIME_MARK);
+                               JAXBContext jc = JAXBContext
+                                   .newInstance("jalview.xml.binding.jalview");
+                               XMLStreamReader streamReader = XMLInputFactory.newInstance()
+                                   .createXMLStreamReader(jin);
+                               javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+                               JAXBElement<JalviewModel> jbe = um
+                                   .unmarshal(streamReader, JalviewModel.class);
+                               JalviewModel model = jbe.getValue();
+
+                               if (true) // !skipViewport(object))
+                                   {
+                                       // Q: Do we have to load from the model, even if it
+                                       // does not have a viewport, could we discover that early on?
+                                       // Q: Do we need to load this object?
+                                       _af = loadFromObject(model, fileName, file, true, jprovider);
+                                       //            Platform.timeCheck("Jalview2XML.loadFromObject",
+                                       // Platform.TIME_MARK);
+
+                                       if (_af != null)
+                                           {
+                                               alignFrames.add(_af);
+                                           }
+                                       if (_af != null && model.getViewport().size() > 0)
+                                           {
+
+                                               // That is, this is one of the AlignmentPanel models
+                                               if (af == null)
+                                                   {
+                                                       // store a reference to the first view
+                                                       af = _af;
+                                                   }
+                                               if (_af.getViewport().isGatherViewsHere())
+                                                   {
+                                                       // if this is a gathered view, keep its reference since
+                                                       // after gathering views, only this frame will remain
+                                                       af = _af;
+                                                       gatherToThisFrame.put(_af.getViewport().getSequenceSetId(),
+                                                                             _af);
+                                                   }
+                                               // Save dataset to register mappings once all resolved
+                                               importedDatasets.put(
+                                                                    af.getViewport().getAlignment().getDataset(),
+                                                                    af.getViewport().getAlignment().getDataset());
+                                           }
+                                   }
+                               entryCount++;
+                           }
+                       else if (jarentry != null)
+                           {
+                               // Some other file here.
+                               entryCount++;
+                           }
+                   } while (jarentry != null);
+               jin.close();
+               resolveFrefedSequences();
+           } catch (IOException ex)
+           {
+               ex.printStackTrace();
+               errorMessage = "Couldn't locate Jalview XML file : " + fileName;
+               System.err.println(
+                                  "Exception whilst loading jalview XML file : " + ex + "\n");
+           } catch (Exception ex)
+           {
+               System.err.println("Parsing as Jalview Version 2 file failed.");
+               ex.printStackTrace(System.err);
+               if (attemptversion1parse)
+                   {
+                       // used to attempt to parse as V1 castor-generated xml
+                   }
+               if (Desktop.getInstance() != null)
+                   {
+                       Desktop.getInstance().stopLoading();
+                   }
+               if (af != null)
+                   {
+                       System.out.println("Successfully loaded archive file");
+                       return af;
+                   }
+               ex.printStackTrace();
+
+               System.err.println(
+                                  "Exception whilst loading jalview XML file : " + ex + "\n");
+           } catch (OutOfMemoryError e)
+           {
+               // Don't use the OOM Window here
+               errorMessage = "Out of memory loading jalview XML file";
+               System.err.println("Out of memory whilst loading jalview XML file");
+               e.printStackTrace();
+           } finally
+           {
+               for (AlignFrame alf : alignFrames)
+                   {
+                       alf.alignPanel.setHoldRepaint(false);
+                   }
+           }
+
+       /*
+        * Regather multiple views (with the same sequence set id) to the frame (if
+        * any) that is flagged as the one to gather to, i.e. convert them to tabbed
+        * views instead of separate frames. Note this doesn't restore a state where
+        * some expanded views in turn have tabbed views - the last "first tab" read
+        * in will play the role of gatherer for all.
+        */
+       for (AlignFrame fr : gatherToThisFrame.values())
+           {
+               Desktop.getInstance().gatherViews(fr);
+           }
+
+       restoreSplitFrames();
+       for (AlignmentI ds : importedDatasets.keySet())
+           {
+               if (ds.getCodonFrames() != null)
+                   {
+                       Desktop.getStructureSelectionManager()
+                           .registerMappings(ds.getCodonFrames());
+                   }
+           }
+       if (errorMessage != null)
+           {
+               reportErrors();
+           }
+
+       if (Desktop.getInstance() != null)
+           {
+               Desktop.getInstance().stopLoading();
+           }
+
+       return af;
+    }
 
-        Tree tree = jm.getTree().get(t);
+    /**
+     * Try to reconstruct and display SplitFrame windows, where each contains
+     * complementary dna and protein alignments. Done by pairing up AlignFrame
+     * objects (created earlier) which have complementary viewport ids associated.
+     */
+    protected void restoreSplitFrames()
+    {
+       List<SplitFrame> gatherTo = new ArrayList<>();
+       List<AlignFrame> addedToSplitFrames = new ArrayList<>();
+       Map<String, AlignFrame> dna = new HashMap<>();
+
+       /*
+        * Identify the DNA alignments
+        */
+       for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+                .entrySet())
+           {
+               AlignFrame af = candidate.getValue();
+               if (af.getViewport().getAlignment().isNucleotide())
+                   {
+                       dna.put(candidate.getKey().getId(), af);
+                   }
+           }
+
+       /*
+        * Try to match up the protein complements
+        */
+       for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+                .entrySet())
+           {
+               AlignFrame af = candidate.getValue();
+               if (!af.getViewport().getAlignment().isNucleotide())
+                   {
+                       String complementId = candidate.getKey().getComplementId();
+                       // only non-null complements should be in the Map
+                       if (complementId != null && dna.containsKey(complementId))
+                           {
+                               final AlignFrame dnaFrame = dna.get(complementId);
+                               SplitFrame sf = createSplitFrame(dnaFrame, af);
+                               addedToSplitFrames.add(dnaFrame);
+                               addedToSplitFrames.add(af);
+                               dnaFrame.setMenusForViewport();
+                               af.setMenusForViewport();
+                               if (af.getViewport().isGatherViewsHere())
+                                   {
+                                       gatherTo.add(sf);
+                                   }
+                           }
+                   }
+           }
+
+       /*
+        * Open any that we failed to pair up (which shouldn't happen!) as
+        * standalone AlignFrame's.
+        */
+       for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
+                .entrySet())
+           {
+               AlignFrame af = candidate.getValue();
+               if (!addedToSplitFrames.contains(af))
+                   {
+                       Viewport view = candidate.getKey();
+                       Desktop.addInternalFrame(af, view.getTitle(),
+                                                safeInt(view.getWidth()), safeInt(view.getHeight()));
+                       af.setMenusForViewport();
+                       System.err.println("Failed to restore view " + view.getTitle()
+                                          + " to split frame");
+                   }
+           }
+
+       /*
+        * Gather back into tabbed views as flagged.
+        */
+       for (SplitFrame sf : gatherTo)
+           {
+               Desktop.getInstance().gatherViews(sf);
+           }
+
+       splitFrameCandidates.clear();
+    }
 
-        TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
-        if (tp == null)
-        {
-          tp = af.showNewickTree(new NewickFile(tree.getNewick()),
-                  tree.getTitle(), safeInt(tree.getWidth()),
-                  safeInt(tree.getHeight()), safeInt(tree.getXpos()),
-                  safeInt(tree.getYpos()));
-          if (tree.getId() != null)
-          {
-            // perhaps bind the tree id to something ?
-          }
-        }
-        else
-        {
-          // update local tree attributes ?
-          // TODO: should check if tp has been manipulated by user - if so its
-          // settings shouldn't be modified
-          tp.setTitle(tree.getTitle());
-          tp.setBounds(new Rectangle(safeInt(tree.getXpos()),
-                  safeInt(tree.getYpos()), safeInt(tree.getWidth()),
-                  safeInt(tree.getHeight())));
-          tp.setViewport(av); // af.viewport;
-          // TODO: verify 'associate with all views' works still
-          tp.getTreeCanvas().setViewport(av); // af.viewport;
-          tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
-        }
-        tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
-        if (tp == null)
-        {
-          Console.warn(
-                  "There was a problem recovering stored Newick tree: \n"
-                          + tree.getNewick());
-          continue;
-        }
+    /**
+     * Construct and display one SplitFrame holding DNA and protein alignments.
+     * 
+     * @param dnaFrame
+     * @param proteinFrame
+     * @return
+     */
+    protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
+                                         AlignFrame proteinFrame)
+    {
+       SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
+       String title = MessageManager.getString("label.linked_view_title");
+       int width = (int) dnaFrame.getBounds().getWidth();
+       int height = (int) (dnaFrame.getBounds().getHeight()
+                           + proteinFrame.getBounds().getHeight() + 50);
 
-        tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow()));
-        tp.fitToWindow_actionPerformed(null);
+       /*
+        * SplitFrame location is saved to both enclosed frames
+        */
+       splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
+       Desktop.addInternalFrame(splitFrame, title, width, height);
 
-        if (tree.getFontName() != null)
-        {
-          tp.setTreeFont(
-                  new Font(tree.getFontName(), safeInt(tree.getFontStyle()),
-                          safeInt(tree.getFontSize())));
-        }
-        else
-        {
-          tp.setTreeFont(
-                  new Font(view.getFontName(), safeInt(view.getFontStyle()),
-                          safeInt(view.getFontSize())));
-        }
+       /*
+        * And compute cDNA consensus (couldn't do earlier with consensus as
+        * mappings were not yet present)
+        */
+       proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel);
 
-        tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked()));
-        tp.showBootstrap(safeBoolean(tree.isShowBootstrap()));
-        tp.showDistances(safeBoolean(tree.isShowDistances()));
+       return splitFrame;
+    }
 
-        tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold()));
+    /**
+     * check errorMessage for a valid error message and raise an error box in the
+     * GUI or write the current errorMessage to stderr and then clear the error
+     * state.
+     */
+    protected void reportErrors()
+    {
+       reportErrors(false);
+    }
+
+    protected void reportErrors(final boolean saving)
+    {
+       if (errorMessage != null)
+           {
+               final String finalErrorMessage = errorMessage;
+               if (raiseGUI)
+                   {
+                       javax.swing.SwingUtilities.invokeLater(new Runnable()
+                           {
+                               @Override
+                               public void run()
+                               {
+                                   JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
+                                                                          finalErrorMessage,
+                                                                          "Error " + (saving ? "saving" : "loading")
+                                                                          + " Jalview file",
+                                                                          JvOptionPane.WARNING_MESSAGE);
+                               }
+                           });
+                   }
+               else
+                   {
+                       System.err.println("Problem loading Jalview file: " + errorMessage);
+                   }
+           }
+       errorMessage = null;
+    }
+
+    Map<String, String> alreadyLoadedPDB = new HashMap<>();
 
-        if (safeBoolean(tree.isCurrentTree()))
-        {
-          af.getViewport().setCurrentTree(tp.getTree());
-        }
-      }
+    /**
+     * when set, local views will be updated from view stored in JalviewXML
+     * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
+     * sync if this is set to true.
+     */
+    private final boolean updateLocalViews = false;
 
-    } catch (Exception ex)
+    /**
+     * Returns the path to a temporary file holding the PDB file for the given PDB
+     * id. The first time of asking, searches for a file of that name in the
+     * Jalview project jar, and copies it to a new temporary file. Any repeat
+     * requests just return the path to the file previously created.
+     * 
+     * @param jprovider
+     * @param pdbId
+     * @return
+     */
+    String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
+                      String origFile)
     {
-      ex.printStackTrace();
+       if (alreadyLoadedPDB.containsKey(pdbId))
+           {
+               return alreadyLoadedPDB.get(pdbId).toString();
+           }
+
+       String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
+                                      origFile);
+       if (tempFile != null)
+           {
+               alreadyLoadedPDB.put(pdbId, tempFile);
+           }
+       return tempFile;
     }
-  }
 
-  /**
-   * Load and link any saved structure viewers.
-   * 
-   * @param jprovider
-   * @param jseqs
-   * @param af
-   * @param ap
-   */
-  protected void loadPDBStructures(jarInputStreamProvider jprovider,
-          List<JSeq> jseqs, AlignFrame af, AlignmentPanel ap)
-  {
-    /*
-     * Run through all PDB ids on the alignment, and collect mappings between
-     * distinct view ids and all sequences referring to that view.
+    /**
+     * Copies the jar entry of given name to a new temporary file and returns the
+     * path to the file, or null if the entry is not found.
+     * 
+     * @param jprovider
+     * @param jarEntryName
+     * @param prefix
+     *          a prefix for the temporary file name, must be at least three
+     *          characters long
+     * @param suffixModel
+     *          null or original file - so new file can be given the same suffix
+     *          as the old one
+     * @return
      */
-    Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<>();
+    protected String copyJarEntry(jarInputStreamProvider jprovider,
+                                 String jarEntryName, String prefix, String suffixModel)
+    {
+       BufferedReader in = null;
+       PrintWriter out = null;
+       String suffix = ".tmp";
+       if (suffixModel == null)
+           {
+               suffixModel = jarEntryName;
+           }
+       int sfpos = suffixModel.lastIndexOf(".");
+       if (sfpos > -1 && sfpos < (suffixModel.length() - 1))
+           {
+               suffix = "." + suffixModel.substring(sfpos + 1);
+           }
+
+       try (JarInputStream jin = jprovider.getJarInputStream())
+           {
+               JarEntry entry = null;
+               do
+                   {
+                       entry = jin.getNextJarEntry();
+                   } while (entry != null && !entry.getName().equals(jarEntryName));
+
+               if (entry != null)
+                   {
+                       // in = new BufferedReader(new InputStreamReader(jin, UTF_8));
+                       File outFile = File.createTempFile(prefix, suffix);
+                       outFile.deleteOnExit();
+                       try (OutputStream os = new FileOutputStream(outFile))
+                           {
+                               copyAll(jin, os);
+                           }
+                       String t = outFile.getAbsolutePath();
+                       return t;
+                   }
+               else
+                   {
+                       Console.warn(
+                                    "Couldn't find entry in Jalview Jar for " + jarEntryName);
+                   }
+           } catch (Exception ex)
+           {
+               ex.printStackTrace();
+           }
+
+       return null;
+    }
+
+    private class JvAnnotRow
+    {
+       public JvAnnotRow(int i, AlignmentAnnotation jaa)
+       {
+           order = i;
+           template = jaa;
+       }
+
+       /**
+        * persisted version of annotation row from which to take vis properties
+        */
+       public jalview.datamodel.AlignmentAnnotation template;
+
+       /**
+        * original position of the annotation row in the alignment
+        */
+       public int order;
+    }
 
-    for (int i = 0; i < jseqs.size(); i++)
+    /**
+     * Load alignment frame from jalview XML DOM object. For a DOM object that
+     * includes one or more Viewport elements (one with a title that does NOT
+     * contain "Dataset for"), create the frame.
+     * 
+     * @param jalviewModel
+     *          DOM
+     * @param fileName
+     *          filename source string
+     * @param file 
+     * @param loadTreesAndStructures
+     *          when false only create Viewport
+     * @param jprovider
+     *          data source provider
+     * @return alignment frame created from view stored in DOM
+     */
+    AlignFrame loadFromObject(JalviewModel jalviewModel, String fileName,
+                             File file, boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
     {
-      JSeq jseq = jseqs.get(i);
-      if (jseq.getPdbids().size() > 0)
-      {
-        List<Pdbids> ids = jseq.getPdbids();
-        for (int p = 0; p < ids.size(); p++)
-        {
-          Pdbids pdbid = ids.get(p);
-          final int structureStateCount = pdbid.getStructureState().size();
-          for (int s = 0; s < structureStateCount; s++)
-          {
-            // check to see if we haven't already created this structure view
-            final StructureState structureState = pdbid.getStructureState()
-                    .get(s);
-            String sviewid = (structureState.getViewId() == null) ? null
-                    : structureState.getViewId() + uniqueSetSuffix;
-            jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
-            // Originally : pdbid.getFile()
-            // : TODO: verify external PDB file recovery still works in normal
-            // jalview project load
-            jpdb.setFile(
-                    loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile()));
-            jpdb.setId(pdbid.getId());
-
-            int x = safeInt(structureState.getXpos());
-            int y = safeInt(structureState.getYpos());
-            int width = safeInt(structureState.getWidth());
-            int height = safeInt(structureState.getHeight());
-
-            // Probably don't need to do this anymore...
-            // Desktop.desktop.getComponentAt(x, y);
-            // TODO: NOW: check that this recovers the PDB file correctly.
-            String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
-                    pdbid.getFile());
-            jalview.datamodel.SequenceI seq = seqRefIds
-                    .get(jseq.getId() + "");
-            if (sviewid == null)
-            {
-              sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + ","
-                      + height;
-            }
-            if (!structureViewers.containsKey(sviewid))
-            {
-              String viewerType = structureState.getType();
-              if (viewerType == null) // pre Jalview 2.9
-              {
-                viewerType = ViewerType.JMOL.toString();
-              }
-              structureViewers.put(sviewid,
-                      new StructureViewerModel(x, y, width, height, false,
-                              false, true, structureState.getViewId(),
-                              viewerType));
-              // Legacy pre-2.7 conversion JAL-823 :
-              // do not assume any view has to be linked for colour by
-              // sequence
-            }
+       SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
+       List<Sequence> vamsasSeqs = vamsasSet.getSequence();
 
-            // assemble String[] { pdb files }, String[] { id for each
-            // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
-            // seqs_file 2}, boolean[] {
-            // linkAlignPanel,superposeWithAlignpanel}} from hash
-            StructureViewerModel jmoldat = structureViewers.get(sviewid);
-            jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
-                    || structureState.isAlignwithAlignPanel());
-
-            /*
-             * Default colour by linked panel to false if not specified (e.g.
-             * for pre-2.7 projects)
-             */
-            boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
-            colourWithAlignPanel |= structureState.isColourwithAlignPanel();
-            jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
-
-            /*
-             * Default colour by viewer to true if not specified (e.g. for
-             * pre-2.7 projects)
-             */
-            boolean colourByViewer = jmoldat.isColourByViewer();
-            colourByViewer &= structureState.isColourByJmol();
-            jmoldat.setColourByViewer(colourByViewer);
-
-            if (jmoldat.getStateData().length() < structureState.getValue()
-                    /*Content()*/.length())
-            {
-              jmoldat.setStateData(structureState.getValue());// Content());
-            }
-            if (pdbid.getFile() != null)
-            {
-              File mapkey = new File(pdbid.getFile());
-              StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
-              if (seqstrmaps == null)
-              {
-                jmoldat.getFileData().put(mapkey,
-                        seqstrmaps = jmoldat.new StructureData(pdbFile,
-                                pdbid.getId()));
-              }
-              if (!seqstrmaps.getSeqList().contains(seq))
-              {
-                seqstrmaps.getSeqList().add(seq);
-                // TODO and chains?
-              }
-            }
-            else
-            {
-              errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
-              Console.warn(errorMessage);
-            }
-          }
-        }
-      }
-    }
-    // Instantiate the associated structure views
-    for (Entry<String, StructureViewerModel> entry : structureViewers
-            .entrySet())
-    {
-      try
-      {
-        createOrLinkStructureViewer(entry, af, ap, jprovider);
-      } catch (Exception e)
-      {
-        System.err.println(
-                "Error loading structure viewer: " + e.getMessage());
-        // failed - try the next one
-      }
-    }
-  }
+       // JalviewModelSequence jms = object.getJalviewModelSequence();
 
-  /**
-   * 
-   * @param viewerData
-   * @param af
-   * @param ap
-   * @param jprovider
-   */
-  protected void createOrLinkStructureViewer(
-          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
-          AlignmentPanel ap, jarInputStreamProvider jprovider)
-  {
-    final StructureViewerModel stateData = viewerData.getValue();
+       // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
+       // : null;
+       Viewport view = (jalviewModel.getViewport().size() > 0)
+            ? jalviewModel.getViewport().get(0)
+            : null;
 
-    /*
-     * Search for any viewer windows already open from other alignment views
-     * that exactly match the stored structure state
-     */
-    StructureViewerBase comp = findMatchingViewer(viewerData);
+       // ////////////////////////////////
+       // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
+       //
+       //
+       // If we just load in the same jar file again, the sequenceSetId
+       // will be the same, and we end up with multiple references
+       // to the same sequenceSet. We must modify this id on load
+       // so that each load of the file gives a unique id
+
+       /**
+        * used to resolve correct alignment dataset for alignments with multiple
+        * views
+        */
+       String uniqueSeqSetId = null;
+       String viewId = null;
+       if (view != null)
+           {
+               uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
+               viewId = (view.getId() == null ? null
+                         : view.getId() + uniqueSetSuffix);
+           }
+
+       // ////////////////////////////////
+       // LOAD SEQUENCES
+
+       List<SequenceI> hiddenSeqs = null;
+
+       List<SequenceI> tmpseqs = new ArrayList<>();
+
+       boolean multipleView = false;
+       SequenceI referenceseqForView = null;
+       // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
+       List<JSeq> jseqs = jalviewModel.getJSeq();
+       int vi = 0; // counter in vamsasSeq array
+       for (int i = 0; i < jseqs.size(); i++)
+           {
+               JSeq jseq = jseqs.get(i);
+               String seqId = jseq.getId();
+
+               SequenceI tmpSeq = seqRefIds.get(seqId);
+               if (tmpSeq != null)
+                   {
+                       if (!incompleteSeqs.containsKey(seqId))
+                           {
+                               // may not need this check, but keep it for at least 2.9,1 release
+                               if (tmpSeq.getStart() != jseq.getStart()
+                                   || tmpSeq.getEnd() != jseq.getEnd())
+                                   {
+                                       Console.warn(
+                                                    String.format("Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
+                                                                  tmpSeq.getName(), tmpSeq.getStart(),
+                                                                  tmpSeq.getEnd(), jseq.getStart(),
+                                                                  jseq.getEnd()));
+                                   }
+                           }
+                       else
+                           {
+                               incompleteSeqs.remove(seqId);
+                           }
+                       if (vamsasSeqs.size() > vi
+                           && vamsasSeqs.get(vi).getId().equals(seqId))
+                           {
+                               // most likely we are reading a dataset XML document so
+                               // update from vamsasSeq section of XML for this sequence
+                               tmpSeq.setName(vamsasSeqs.get(vi).getName());
+                               tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription());
+                               tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence());
+                               vi++;
+                           }
+                       else
+                           {
+                               // reading multiple views, so vamsasSeq set is a subset of JSeq
+                               multipleView = true;
+                           }
+                       tmpSeq.setStart(jseq.getStart());
+                       tmpSeq.setEnd(jseq.getEnd());
+                       tmpseqs.add(tmpSeq);
+                   }
+               else
+                   {
+                       Sequence vamsasSeq = vamsasSeqs.get(vi);
+                       tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(),
+                                                               vamsasSeq.getSequence());
+                       tmpSeq.setDescription(vamsasSeq.getDescription());
+                       tmpSeq.setStart(jseq.getStart());
+                       tmpSeq.setEnd(jseq.getEnd());
+                       tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
+                       seqRefIds.put(vamsasSeq.getId(), tmpSeq);
+                       tmpseqs.add(tmpSeq);
+                       vi++;
+                   }
+
+               if (safeBoolean(jseq.isViewreference()))
+                   {
+                       referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
+                   }
+
+               if (jseq.isHidden() != null && jseq.isHidden().booleanValue())
+                   {
+                       if (hiddenSeqs == null)
+                           {
+                               hiddenSeqs = new ArrayList<>();
+                           }
+
+                       hiddenSeqs.add(tmpSeq);
+                   }
+           }
+
+       // /
+       // Create the alignment object from the sequence set
+       // ///////////////////////////////
+       SequenceI[] orderedSeqs = tmpseqs
+            .toArray(new SequenceI[tmpseqs.size()]);
 
-    if (comp != null)
-    {
-      linkStructureViewer(ap, comp, stateData);
-      return;
+       AlignmentI al = null;
+       // so we must create or recover the dataset alignment before going further
+       // ///////////////////////////////
+       if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
+           {
+               // older jalview projects do not have a dataset - so creat alignment and
+               // dataset
+               al = new Alignment(orderedSeqs);
+               al.setDataset(null);
+           }
+       else
+           {
+               boolean isdsal = jalviewModel.getViewport().isEmpty();
+               if (isdsal)
+                   {
+                       // we are importing a dataset record, so
+                       // recover reference to an alignment already materialsed as dataset
+                       al = getDatasetFor(vamsasSet.getDatasetId());
+                   }
+               if (al == null)
+                   {
+                       // materialse the alignment
+                       al = new Alignment(orderedSeqs);
+                   }
+               if (isdsal)
+                   {
+                       addDatasetRef(vamsasSet.getDatasetId(), al);
+                   }
+
+               // finally, verify all data in vamsasSet is actually present in al
+               // passing on flag indicating if it is actually a stored dataset
+               recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
+           }
+
+       if (referenceseqForView != null)
+           {
+               al.setSeqrep(referenceseqForView);
+           }
+       // / Add the alignment properties
+       for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++)
+           {
+               SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties()
+                   .get(i);
+               al.setProperty(ssp.getKey(), ssp.getValue());
+           }
+  
+       // ///////////////////////////////
+
+       Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
+       if (!multipleView)
+           {
+               // load sequence features, database references and any associated PDB
+               // structures for the alignment
+               //
+               // prior to 2.10, this part would only be executed the first time a
+               // sequence was encountered, but not afterwards.
+               // now, for 2.10 projects, this is also done if the xml doc includes
+               // dataset sequences not actually present in any particular view.
+               //
+               for (int i = 0; i < vamsasSeqs.size(); i++)
+                   {
+                       JSeq jseq = jseqs.get(i);
+                       if (jseq.getFeatures().size() > 0)
+                           {
+                               List<Feature> features = jseq.getFeatures();
+                               for (int f = 0; f < features.size(); f++)
+                                   {
+                                       Feature feat = features.get(f);
+                                       SequenceFeature sf = new SequenceFeature(feat.getType(),
+                                                                                feat.getDescription(), feat.getBegin(), feat.getEnd(),
+                                                                                safeFloat(feat.getScore()), feat.getFeatureGroup());
+                                       sf.setStatus(feat.getStatus());
+
+                                       /*
+                                        * load any feature attributes - include map-valued attributes
+                                        */
+                                       Map<String, Map<String, String>> mapAttributes = new HashMap<>();
+                                       for (int od = 0; od < feat.getOtherData().size(); od++)
+                                           {
+                                               OtherData keyValue = feat.getOtherData().get(od);
+                                               String attributeName = keyValue.getKey();
+                                               String attributeValue = keyValue.getValue();
+                                               if (attributeName.startsWith("LINK"))
+                                                   {
+                                                       sf.addLink(attributeValue);
+                                                   }
+                                               else
+                                                   {
+                                                       String subAttribute = keyValue.getKey2();
+                                                       if (subAttribute == null)
+                                                           {
+                                                               // simple string-valued attribute
+                                                               sf.setValue(attributeName, attributeValue);
+                                                           }
+                                                       else
+                                                           {
+                                                               // attribute 'key' has sub-attribute 'key2'
+                                                               if (!mapAttributes.containsKey(attributeName))
+                                                                   {
+                                                                       mapAttributes.put(attributeName, new HashMap<>());
+                                                                   }
+                                                               mapAttributes.get(attributeName).put(subAttribute,
+                                                                                                    attributeValue);
+                                                           }
+                                                   }
+                                           }
+                                       for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
+                                                .entrySet())
+                                           {
+                                               sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
+                                           }
+
+                                       // adds feature to datasequence's feature set (since Jalview 2.10)
+                                       al.getSequenceAt(i).addSequenceFeature(sf);
+                                   }
+                           }
+                       if (vamsasSeqs.get(i).getDBRef().size() > 0)
+                           {
+                               // adds dbrefs to datasequence's set (since Jalview 2.10)
+                               addDBRefs(
+                                         al.getSequenceAt(i).getDatasetSequence() == null
+                                         ? al.getSequenceAt(i)
+                                         : al.getSequenceAt(i).getDatasetSequence(),
+                                         vamsasSeqs.get(i));
+                           }
+                       if (jseq.getPdbids().size() > 0)
+                           {
+                               List<Pdbids> ids = jseq.getPdbids();
+                               for (int p = 0; p < ids.size(); p++)
+                                   {
+                                       Pdbids pdbid = ids.get(p);
+                                       jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
+                                       entry.setId(pdbid.getId());
+                                       if (pdbid.getType() != null)
+                                           {
+                                               if (PDBEntry.Type.getType(pdbid.getType()) != null)
+                                                   {
+                                                       entry.setType(PDBEntry.Type.getType(pdbid.getType()));
+                                                   }
+                                               else
+                                                   {
+                                                       entry.setType(PDBEntry.Type.FILE);
+                                                   }
+                                           }
+                                       // jprovider is null when executing 'New View'
+                                       if (pdbid.getFile() != null && jprovider != null)
+                                           {
+                                               if (!pdbloaded.containsKey(pdbid.getFile()))
+                                                   {
+                                                       entry.setFile(loadPDBFile(jprovider, pdbid.getId(),
+                                                                                 pdbid.getFile()));
+                                                   }
+                                               else
+                                                   {
+                                                       entry.setFile(pdbloaded.get(pdbid.getId()).toString());
+                                                   }
+                                           }
+                                       /*
+                                         if (pdbid.getPdbentryItem() != null)
+                                         {
+                                         for (PdbentryItem item : pdbid.getPdbentryItem())
+                                         {
+                                         for (Property pr : item.getProperty())
+                                         {
+                                         entry.setProperty(pr.getName(), pr.getValue());
+                                         }
+                                         }
+                                         }
+                                       */
+                                       for (Property prop : pdbid.getProperty())
+                                           {
+                                               entry.setProperty(prop.getName(), prop.getValue());
+                                           }
+                                       Desktop.getStructureSelectionManager()
+                                           .registerPDBEntry(entry);
+                                       // adds PDBEntry to datasequence's set (since Jalview 2.10)
+                                       if (al.getSequenceAt(i).getDatasetSequence() != null)
+                                           {
+                                               al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+                                           }
+                                       else
+                                           {
+                                               al.getSequenceAt(i).addPDBId(entry);
+                                           }
+                                   }
+                           }
+                       /*
+                        * load any HMMER profile
+                        */
+                       // TODO fix this
+
+                       String hmmJarFile = jseqs.get(i).getHmmerProfile();
+                       if (hmmJarFile != null && jprovider != null)
+                           {
+                               loadHmmerProfile(jprovider, hmmJarFile, al.getSequenceAt(i));
+                           }
+                   }
+           } // end !multipleview
+  
+       // ///////////////////////////////
+       // LOAD SEQUENCE MAPPINGS
+
+       if (vamsasSet.getAlcodonFrame().size() > 0)
+           {
+               // TODO Potentially this should only be done once for all views of an
+               // alignment
+               List<AlcodonFrame> alc = vamsasSet.getAlcodonFrame();
+               for (int i = 0; i < alc.size(); i++)
+                   {
+                       AlignedCodonFrame cf = new AlignedCodonFrame();
+                       if (alc.get(i).getAlcodMap().size() > 0)
+                           {
+                               List<AlcodMap> maps = alc.get(i).getAlcodMap();
+                               for (int m = 0; m < maps.size(); m++)
+                                   {
+                                       AlcodMap map = maps.get(m);
+                                       SequenceI dnaseq = seqRefIds.get(map.getDnasq());
+                                       // Load Mapping
+                                       jalview.datamodel.Mapping mapping = null;
+                                       // attach to dna sequence reference.
+                                       if (map.getMapping() != null)
+                                           {
+                                               mapping = addMapping(map.getMapping());
+                                               if (dnaseq != null && mapping.getTo() != null)
+                                                   {
+                                                       cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
+                                                   }
+                                               else
+                                                   {
+                                                       // defer to later
+                                                       frefedSequence.add(
+                                                                          newAlcodMapRef(map.getDnasq(), cf, mapping));
+                                                   }
+                                           }
+                                   }
+                               al.addCodonFrame(cf);
+                           }
+                   }
+           }
+
+       // ////////////////////////////////
+       // LOAD ANNOTATIONS
+       List<JvAnnotRow> autoAlan = new ArrayList<>();
+
+       /*
+        * store any annotations which forward reference a group's ID
+        */
+       Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<>();
+
+       if (vamsasSet.getAnnotation().size()/*Count()*/ > 0)
+           {
+               List<Annotation> an = vamsasSet.getAnnotation();
+
+               for (int i = 0; i < an.size(); i++)
+                   {
+                       Annotation annotation = an.get(i);
+
+                       /**
+                        * test if annotation is automatically calculated for this view only
+                        */
+                       boolean autoForView = false;
+                       if (annotation.getLabel().equals("Quality")
+                           || annotation.getLabel().equals("Conservation")
+                           || annotation.getLabel().equals("Consensus"))
+                           {
+                               // Kludge for pre 2.5 projects which lacked the autocalculated flag
+                               autoForView = true;
+                               // JAXB has no has() test; schema defaults value to false
+                               // if (!annotation.hasAutoCalculated())
+                               // {
+                               // annotation.setAutoCalculated(true);
+                               // }
+                           }
+                       if (autoForView || annotation.isAutoCalculated())
+                           {
+                               // remove ID - we don't recover annotation from other views for
+                               // view-specific annotation
+                               annotation.setId(null);
+                           }
+
+                       // set visibility for other annotation in this view
+                       String annotationId = annotation.getId();
+                       if (annotationId != null && annotationIds.containsKey(annotationId))
+                           {
+                               AlignmentAnnotation jda = annotationIds.get(annotationId);
+                               // in principle Visible should always be true for annotation displayed
+                               // in multiple views
+                               if (annotation.isVisible() != null)
+                                   {
+                                       jda.visible = annotation.isVisible();
+                                   }
+
+                               al.addAnnotation(jda);
+
+                               continue;
+                           }
+                       // Construct new annotation from model.
+                       List<AnnotationElement> ae = annotation.getAnnotationElement();
+                       jalview.datamodel.Annotation[] anot = null;
+                       java.awt.Color firstColour = null;
+                       int anpos;
+                       if (!annotation.isScoreOnly())
+                           {
+                               anot = new jalview.datamodel.Annotation[al.getWidth()];
+                               for (int aa = 0; aa < ae.size() && aa < anot.length; aa++)
+                                   {
+                                       AnnotationElement annElement = ae.get(aa);
+                                       anpos = annElement.getPosition();
+
+                                       if (anpos >= anot.length)
+                                           {
+                                               continue;
+                                           }
+
+                                       float value = safeFloat(annElement.getValue());
+                                       anot[anpos] = new jalview.datamodel.Annotation(
+                                                                                      annElement.getDisplayCharacter(),
+                                                                                      annElement.getDescription(),
+                                                                                      (annElement.getSecondaryStructure() == null
+                                                                                       || annElement.getSecondaryStructure()
+                                                                                       .length() == 0)
+                                                                                      ? ' '
+                                                                                      : annElement
+                                                                                      .getSecondaryStructure()
+                                                                                      .charAt(0),
+                                                                                      value);
+                                       anot[anpos].colour = new Color(safeInt(annElement.getColour()));
+                                       if (firstColour == null)
+                                           {
+                                               firstColour = anot[anpos].colour;
+                                           }
+                                   }
+                           }
+                       // create the new AlignmentAnnotation
+                       jalview.datamodel.AlignmentAnnotation jaa = null;
+
+                       if (annotation.isGraph())
+                           {
+                               float llim = 0, hlim = 0;
+                               // if (autoForView || an[i].isAutoCalculated()) {
+                               // hlim=11f;
+                               // }
+                               jaa = new jalview.datamodel.AlignmentAnnotation(
+                                                                               annotation.getLabel(), annotation.getDescription(), anot,
+                                                                               llim, hlim, safeInt(annotation.getGraphType()));
+
+                               jaa.graphGroup = safeInt(annotation.getGraphGroup());
+                               jaa._linecolour = firstColour;
+                               if (annotation.getThresholdLine() != null)
+                                   {
+                                       jaa.setThreshold(new jalview.datamodel.GraphLine(
+                                                                                        safeFloat(annotation.getThresholdLine().getValue()),
+                                                                                        annotation.getThresholdLine().getLabel(),
+                                                                                        new java.awt.Color(safeInt(
+                                                                                                                   annotation.getThresholdLine().getColour()))));
+                                   }
+                               if (autoForView || annotation.isAutoCalculated())
+                                   {
+                                       // Hardwire the symbol display line to ensure that labels for
+                                       // histograms are displayed
+                                       jaa.hasText = true;
+                                   }
+                           }
+                       else
+                           {
+                               jaa = new jalview.datamodel.AlignmentAnnotation(
+                                                                               annotation.getLabel(), annotation.getDescription(), anot);
+                               jaa._linecolour = firstColour;
+                           }
+                       // register new annotation
+                       // Annotation graphs such as Conservation will not have id.
+                       if (annotation.getId() != null)
+                           {
+                               annotationIds.put(annotation.getId(), jaa);
+                               jaa.annotationId = annotation.getId();
+                           }
+                       // recover sequence association
+                       String sequenceRef = annotation.getSequenceRef();
+                       if (sequenceRef != null)
+                           {
+                               // from 2.9 sequenceRef is to sequence id (JAL-1781)
+                               SequenceI sequence = seqRefIds.get(sequenceRef);
+                               if (sequence == null)
+                                   {
+                                       // in pre-2.9 projects sequence ref is to sequence name
+                                       sequence = al.findName(sequenceRef);
+                                   }
+                               if (sequence != null)
+                                   {
+                                       jaa.createSequenceMapping(sequence, 1, true);
+                                       sequence.addAlignmentAnnotation(jaa);
+                                   }
+                           }
+                       // and make a note of any group association
+                       if (annotation.getGroupRef() != null
+                           && annotation.getGroupRef().length() > 0)
+                           {
+                               List<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
+                                   .get(annotation.getGroupRef());
+                               if (aal == null)
+                                   {
+                                       aal = new ArrayList<>();
+                                       groupAnnotRefs.put(annotation.getGroupRef(), aal);
+                                   }
+                               aal.add(jaa);
+                           }
+
+                       if (annotation.getScore() != null)
+                           {
+                               jaa.setScore(annotation.getScore().doubleValue());
+                           }
+                       if (annotation.isVisible() != null)
+                           {
+                               jaa.visible = annotation.isVisible().booleanValue();
+                           }
+
+                       if (annotation.isCentreColLabels() != null)
+                           {
+                               jaa.centreColLabels = annotation.isCentreColLabels()
+                                   .booleanValue();
+                           }
+
+                       if (annotation.isScaleColLabels() != null)
+                           {
+                               jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue();
+                           }
+                       if (annotation.isAutoCalculated())
+                           {
+                               // newer files have an 'autoCalculated' flag and store calculation
+                               // state in viewport properties
+                               jaa.autoCalculated = true; // means annotation will be marked for
+                               // update at end of load.
+                           }
+                       if (annotation.getGraphHeight() != null)
+                           {
+                               jaa.graphHeight = annotation.getGraphHeight().intValue();
+                           }
+                       jaa.belowAlignment = annotation.isBelowAlignment();
+                       jaa.setCalcId(annotation.getCalcId());
+                       if (annotation.getProperty().size() > 0)
+                           {
+                               for (Annotation.Property prop : annotation
+                                        .getProperty())
+                                   {
+                                       jaa.setProperty(prop.getName(), prop.getValue());
+                                   }
+                           }
+                       if (jaa.autoCalculated)
+                           {
+                               autoAlan.add(new JvAnnotRow(i, jaa));
+                           }
+                       else
+                           // if (!autoForView)
+                           {
+                               // add autocalculated group annotation and any user created annotation
+                               // for the view
+                               al.addAnnotation(jaa);
+                           }
+                   }
+           }
+       // ///////////////////////
+       // LOAD GROUPS
+       // Create alignment markup and styles for this view
+       if (jalviewModel.getJGroup().size() > 0)
+           {
+               List<JGroup> groups = jalviewModel.getJGroup();
+               boolean addAnnotSchemeGroup = false;
+               for (int i = 0; i < groups.size(); i++)
+                   {
+                       JGroup jGroup = groups.get(i);
+                       ColourSchemeI cs = null;
+                       if (jGroup.getColour() != null)
+                           {
+                               if (jGroup.getColour().startsWith("ucs"))
+                                   {
+                                       cs = getUserColourScheme(jalviewModel, jGroup.getColour());
+                                   }
+                               else if (jGroup.getColour().equals("AnnotationColourGradient")
+                                        && jGroup.getAnnotationColours() != null)
+                                   {
+                                       addAnnotSchemeGroup = true;
+                                   }
+                               else
+                                   {
+                                       cs = ColourSchemeProperty.getColourScheme(null, al,
+                                                                                 jGroup.getColour());
+                                   }
+                           }
+                       int pidThreshold = safeInt(jGroup.getPidThreshold());
+
+                       Vector<SequenceI> seqs = new Vector<>();
+
+                       for (int s = 0; s < jGroup.getSeq().size(); s++)
+                           {
+                               String seqId = jGroup.getSeq().get(s);
+                               SequenceI ts = seqRefIds.get(seqId);
+
+                               if (ts != null)
+                                   {
+                                       seqs.addElement(ts);
+                                   }
+                           }
+
+                       if (seqs.size() < 1)
+                           {
+                               continue;
+                           }
+
+                       SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
+                                                            safeBoolean(jGroup.isDisplayBoxes()),
+                                                            safeBoolean(jGroup.isDisplayText()),
+                                                            safeBoolean(jGroup.isColourText()),
+                                                            safeInt(jGroup.getStart()), safeInt(jGroup.getEnd()));
+                       sg.getGroupColourScheme().setThreshold(pidThreshold, true);
+                       sg.getGroupColourScheme()
+                           .setConservationInc(safeInt(jGroup.getConsThreshold()));
+                       sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour())));
+
+                       sg.textColour = new Color(safeInt(jGroup.getTextCol1()));
+                       sg.textColour2 = new Color(safeInt(jGroup.getTextCol2()));
+                       sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved()));
+                       sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold());
+                       // attributes with a default in the schema are never null
+                       sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram());
+                       sg.setshowSequenceLogo(jGroup.isShowSequenceLogo());
+                       sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo());
+                       sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus());
+                       if (jGroup.getConsThreshold() != null
+                           && jGroup.getConsThreshold().intValue() != 0)
+                           {
+                               Conservation c = new Conservation("All", sg.getSequences(null), 0,
+                                                                 sg.getWidth() - 1);
+                               c.calculate();
+                               c.verdict(false, 25);
+                               sg.cs.setConservation(c);
+                           }
+
+                       if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
+                           {
+                               // re-instate unique group/annotation row reference
+                               List<AlignmentAnnotation> jaal = groupAnnotRefs
+                                   .get(jGroup.getId());
+                               if (jaal != null)
+                                   {
+                                       for (AlignmentAnnotation jaa : jaal)
+                                           {
+                                               jaa.groupRef = sg;
+                                               if (jaa.autoCalculated)
+                                                   {
+                                                       // match up and try to set group autocalc alignment row for this
+                                                       // annotation
+                                                       if (jaa.label.startsWith("Consensus for "))
+                                                           {
+                                                               sg.setConsensus(jaa);
+                                                           }
+                                                       // match up and try to set group autocalc alignment row for this
+                                                       // annotation
+                                                       if (jaa.label.startsWith("Conservation for "))
+                                                           {
+                                                               sg.setConservationRow(jaa);
+                                                           }
+                                                   }
+                                           }
+                                   }
+                           }
+                       al.addGroup(sg);
+                       if (addAnnotSchemeGroup)
+                           {
+                               // reconstruct the annotation colourscheme
+                               sg.setColourScheme(
+                                                  constructAnnotationColour(jGroup.getAnnotationColours(),
+                                                                            null, al, jalviewModel, false));
+                           }
+                   }
+           }
+       if (view == null)
+           {
+               // only dataset in this model, so just return.
+               return null;
+           }
+       // ///////////////////////////////
+       // LOAD VIEWPORT
+
+       // now check to see if we really need to create a new viewport.
+       if (multipleView && viewportsAdded.size() == 0)
+           {
+               // We recovered an alignment for which a viewport already exists.
+               // TODO: fix up any settings necessary for overlaying stored state onto
+               // state recovered from another document. (may not be necessary).
+               // we may need a binding from a viewport in memory to one recovered from
+               // XML.
+               // and then recover its containing af to allow the settings to be applied.
+               // TODO: fix for vamsas demo
+               System.err.println(
+                                  "About to recover a viewport for existing alignment: Sequence set ID is "
+                                  + uniqueSeqSetId);
+               Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
+               if (seqsetobj != null)
+                   {
+                       if (seqsetobj instanceof String)
+                           {
+                               uniqueSeqSetId = (String) seqsetobj;
+                               System.err.println(
+                                                  "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
+                                                  + uniqueSeqSetId);
+                           }
+                       else
+                           {
+                               System.err.println(
+                                                  "Warning : Collision between sequence set ID string and existing jalview object mapping.");
+                           }
+
+                   }
+           }
+       /**
+        * indicate that annotation colours are applied across all groups (pre
+        * Jalview 2.8.1 behaviour)
+        */
+       boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
+                                                                       jalviewModel.getVersion());
+
+       AlignFrame af = null;
+       AlignmentPanel ap = null;
+       AlignViewport av = null;
+       if (viewId != null)
+           {
+               // Check to see if this alignment already has a view id == viewId
+               jalview.gui.AlignmentPanel views[] = Desktop
+                   .getAlignmentPanels(uniqueSeqSetId);
+               if (views != null && views.length > 0)
+                   {
+                       for (int v = 0; v < views.length; v++)
+                           {
+                               ap = views[v];
+                               av = ap.av;
+                               if (av.getViewId().equalsIgnoreCase(viewId))
+                                   {
+                                       // recover the existing alignpanel, alignframe, viewport
+                                       af = ap.alignFrame;
+                                       break;
+                                       // TODO: could even skip resetting view settings if we don't want to
+                                       // change the local settings from other jalview processes
+                                   }
+                           }
+                   }
+           }
+
+       if (af == null)
+           {
+               af = loadViewport(fileName, file, jseqs, hiddenSeqs, al, jalviewModel, view,
+                                 uniqueSeqSetId, viewId, autoAlan);
+               av = af.getViewport();
+               // note that this only retrieves the most recently accessed
+               // tab of an AlignFrame.
+               ap = af.alignPanel;
+           }
+
+       /*
+        * Load any trees, PDB structures and viewers
+        * 
+        * Not done if flag is false (when this method is used for New View)
+        */
+       final AlignFrame af0 = af;
+       final AlignViewport av0 = av;
+       final AlignmentPanel ap0 = ap;
+       //    Platform.timeCheck("Jalview2XML.loadFromObject-beforetree",
+       //            Platform.TIME_MARK);
+       if (loadTreesAndStructures)
+           {
+               if (!jalviewModel.getTree().isEmpty())
+                   {
+                       SwingUtilities.invokeLater(new Runnable()
+                           {
+                               @Override
+                               public void run()
+                               {
+                                   //            Platform.timeCheck(null, Platform.TIME_MARK);
+                                   loadTrees(jalviewModel, view, af0, av0, ap0);
+                                   //            Platform.timeCheck("Jalview2XML.loadTrees", Platform.TIME_MARK);
+                               }
+                           });
+                   }
+               if (!jalviewModel.getPcaViewer().isEmpty())
+                   {
+                       SwingUtilities.invokeLater(new Runnable()
+                           {
+                               @Override
+                               public void run()
+                               {
+                                   //            Platform.timeCheck(null, Platform.TIME_MARK);
+                                   loadPCAViewers(jalviewModel, ap0);
+                                   //            Platform.timeCheck("Jalview2XML.loadPCA", Platform.TIME_MARK);
+                               }
+                           });
+                   }
+               SwingUtilities.invokeLater(new Runnable()
+                   {
+                       @Override
+                       public void run()
+                       {
+                           //          Platform.timeCheck(null, Platform.TIME_MARK);
+                           loadPDBStructures(jprovider, jseqs, af0, ap0);
+                           //          Platform.timeCheck("Jalview2XML.loadPDB", Platform.TIME_MARK);
+                       }
+                   });
+               SwingUtilities.invokeLater(new Runnable()
+                   {
+                       @Override
+                       public void run()
+                       {
+                           loadRnaViewers(jprovider, jseqs, ap0);
+                       }
+                   });
+           }
+       // and finally return.
+       // but do not set holdRepaint true just yet, because this could be the
+       // initial frame with just its dataset.
+       return af;
     }
 
-    String type = stateData.getType();
-    try
-    {
-      ViewerType viewerType = ViewerType.valueOf(type);
-      createStructureViewer(viewerType, viewerData, af, jprovider);
-    } catch (IllegalArgumentException | NullPointerException e)
-    {
-      // TODO JAL-3619 show error dialog / offer an alternative viewer
-      Console.error("Invalid structure viewer type: " + type);
+    /**
+     * Loads a HMMER profile from a file stored in the project, and associates it
+     * with the specified sequence
+     * 
+     * @param jprovider
+     * @param hmmJarFile
+     * @param seq
+     */
+    protected void loadHmmerProfile(jarInputStreamProvider jprovider,
+                                   String hmmJarFile, SequenceI seq)
+    {
+       try
+           {
+               String hmmFile = copyJarEntry(jprovider, hmmJarFile, "hmm", null);
+               HMMFile parser = new HMMFile(hmmFile, DataSourceType.FILE);
+               HiddenMarkovModel hmmModel = parser.getHMM();
+               hmmModel = new HiddenMarkovModel(hmmModel, seq);
+               seq.setHMM(hmmModel);
+           } catch (IOException e)
+           {
+               Console.warn("Error loading HMM profile for " + seq.getName() + ": "
+                            + e.getMessage());
+           }
     }
-  }
-
-  /**
-   * Generates a name for the entry in the project jar file to hold state
-   * information for a structure viewer
-   * 
-   * @param viewId
-   * @return
-   */
-  protected String getViewerJarEntryName(String viewId)
-  {
-    return VIEWER_PREFIX + viewId;
-  }
 
-  /**
-   * Returns any open frame that matches given structure viewer data. The match
-   * is based on the unique viewId, or (for older project versions) the frame's
-   * geometry.
-   * 
-   * @param viewerData
-   * @return
-   */
-  protected StructureViewerBase findMatchingViewer(
-          Entry<String, StructureViewerModel> viewerData)
-  {
-    final String sviewid = viewerData.getKey();
-    final StructureViewerModel svattrib = viewerData.getValue();
-    StructureViewerBase comp = null;
-    JInternalFrame[] frames = getAllFrames();
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof StructureViewerBase)
-      {
-        /*
-         * Post jalview 2.4 schema includes structure view id
-         */
-        if (sviewid != null && ((StructureViewerBase) frame).getViewId()
-                .equals(sviewid))
-        {
-          comp = (StructureViewerBase) frame;
-          break; // break added in 2.9
-        }
-        /*
-         * Otherwise test for matching position and size of viewer frame
-         */
-        else if (frame.getX() == svattrib.getX()
-                && frame.getY() == svattrib.getY()
-                && frame.getHeight() == svattrib.getHeight()
-                && frame.getWidth() == svattrib.getWidth())
-        {
-          comp = (StructureViewerBase) frame;
-          // no break in faint hope of an exact match on viewId
-        }
-      }
+    /**
+     * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna
+     * panel is restored from separate jar entries, two (gapped and trimmed) per
+     * sequence and secondary structure.
+     * 
+     * Currently each viewer shows just one sequence and structure (gapped and
+     * trimmed), however this method is designed to support multiple sequences or
+     * structures in viewers if wanted in future.
+     * 
+     * @param jprovider
+     * @param jseqs
+     * @param ap
+     */
+    protected void loadRnaViewers(jarInputStreamProvider jprovider,
+                                 List<JSeq> jseqs, AlignmentPanel ap)
+    {
+       /*
+        * scan the sequences for references to viewers; create each one the first
+        * time it is referenced, add Rna models to existing viewers
+        */
+       for (JSeq jseq : jseqs)
+           {
+               for (int i = 0; i < jseq.getRnaViewer().size(); i++)
+                   {
+                       RnaViewer viewer = jseq.getRnaViewer().get(i);
+                       AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
+                                                                   ap);
+
+                       for (int j = 0; j < viewer.getSecondaryStructure().size(); j++)
+                           {
+                               SecondaryStructure ss = viewer.getSecondaryStructure().get(j);
+                               SequenceI seq = seqRefIds.get(jseq.getId());
+                               AlignmentAnnotation ann = this.annotationIds
+                                   .get(ss.getAnnotationId());
+
+                               /*
+                                * add the structure to the Varna display (with session state copied
+                                * from the jar to a temporary file)
+                                */
+                               boolean gapped = safeBoolean(ss.isGapped());
+                               String rnaTitle = ss.getTitle();
+                               String sessionState = ss.getViewerState();
+                               String tempStateFile = copyJarEntry(jprovider, sessionState,
+                                                                   "varna", null);
+                               RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
+                               appVarna.addModelSession(rna, rnaTitle, tempStateFile);
+                           }
+                       appVarna.setInitialSelection(safeInt(viewer.getSelectedRna()));
+                   }
+           }
     }
-    return comp;
-  }
 
-  /**
-   * Link an AlignmentPanel to an existing structure viewer.
-   * 
-   * @param ap
-   * @param viewer
-   * @param oldFiles
-   * @param useinViewerSuperpos
-   * @param usetoColourbyseq
-   * @param viewerColouring
-   */
-  protected void linkStructureViewer(AlignmentPanel ap,
-          StructureViewerBase viewer, StructureViewerModel stateData)
-  {
-    // NOTE: if the jalview project is part of a shared session then
-    // view synchronization should/could be done here.
+    /**
+     * Locate and return an already instantiated matching AppVarna, or create one
+     * if not found
+     * 
+     * @param viewer
+     * @param viewIdSuffix
+     * @param ap
+     * @return
+     */
+    protected AppVarna findOrCreateVarnaViewer(RnaViewer viewer,
+                                              String viewIdSuffix, AlignmentPanel ap)
+    {
+       /*
+        * on each load a suffix is appended to the saved viewId, to avoid conflicts
+        * if load is repeated
+        */
+       String postLoadId = viewer.getViewId() + viewIdSuffix;
+       for (JInternalFrame frame : getAllFrames())
+           {
+               if (frame instanceof AppVarna)
+                   {
+                       AppVarna varna = (AppVarna) frame;
+                       if (postLoadId.equals(varna.getViewId()))
+                           {
+                               // this viewer is already instantiated
+                               // could in future here add ap as another 'parent' of the
+                               // AppVarna window; currently just 1-to-many
+                               return varna;
+                           }
+                   }
+           }
+  
+       /*
+        * viewer not found - make it
+        */
+       RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(),
+                                                 safeInt(viewer.getXpos()), safeInt(viewer.getYpos()),
+                                                 safeInt(viewer.getWidth()), safeInt(viewer.getHeight()),
+                                                 safeInt(viewer.getDividerLocation()));
+       AppVarna varna = new AppVarna(model, ap);
+
+       return varna;
+    }
 
-    final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
-    final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
-    final boolean viewerColouring = stateData.isColourByViewer();
-    Map<File, StructureData> oldFiles = stateData.getFileData();
+    /**
+     * Load any saved trees
+     * 
+     * @param jm
+     * @param view
+     * @param af
+     * @param av
+     * @param ap
+     */
+    protected void loadTrees(JalviewModel jm, Viewport view,
+                            AlignFrame af, AlignViewport av, AlignmentPanel ap)
+    {
+       // TODO result of automated refactoring - are all these parameters needed?
+       try
+           {
+               for (int t = 0; t < jm.getTree().size(); t++)
+                   {
+
+                       Tree tree = jm.getTree().get(t);
+
+                       TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
+                       if (tp == null)
+                           {
+                               tp = af.showNewickTree(new NewickFile(tree.getNewick()),
+                                                      tree.getTitle(), safeInt(tree.getWidth()),
+                                                      safeInt(tree.getHeight()), safeInt(tree.getXpos()),
+                                                      safeInt(tree.getYpos()));
+                               if (tp == null)
+                                   {
+                                       Console.warn("There was a problem recovering stored Newick tree: \n"
+                                                    + tree.getNewick());
+                                       continue;
+                                   }
+                               if (tree.getId() != null)
+                                   {
+                                       // perhaps bind the tree id to something ?
+                                   }
+                           }
+                       else
+                           {
+                               // update local tree attributes ?
+                               // TODO: should check if tp has been manipulated by user - if so its
+                               // settings shouldn't be modified
+                               tp.setTitle(tree.getTitle());
+                               tp.setBounds(new Rectangle(safeInt(tree.getXpos()),
+                                                          safeInt(tree.getYpos()), safeInt(tree.getWidth()),
+                                                          safeInt(tree.getHeight())));
+                               tp.setViewport(av); // af.viewport;
+                               // TODO: verify 'associate with all views' works still
+                               tp.getTreeCanvas().setViewport(av); // af.viewport;
+                               tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
+                           }
+                       tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
+
+                       tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow()));
+                       tp.fitToWindow_actionPerformed(null);
+
+                       if (tree.getFontName() != null)
+                           {
+                               tp.setTreeFont(
+                                              new Font(tree.getFontName(), safeInt(tree.getFontStyle()),
+                                                       safeInt(tree.getFontSize())));
+                           }
+                       else
+                           {
+                               tp.setTreeFont(
+                                              new Font(view.getFontName(), safeInt(view.getFontStyle()),
+                                                       safeInt(view.getFontSize())));
+                           }
+
+                       tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked()));
+                       tp.showBootstrap(safeBoolean(tree.isShowBootstrap()));
+                       tp.showDistances(safeBoolean(tree.isShowDistances()));
+
+                       tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold()));
+
+                       if (safeBoolean(tree.isCurrentTree()))
+                           {
+                               af.getViewport().setCurrentTree(tp.getTree());
+                           }
+                   }
+
+           } catch (Exception ex)
+           {
+               ex.printStackTrace();
+           }
+    }
 
-    /*
-     * Add mapping for sequences in this view to an already open viewer
+    /**
+     * Load and link any saved structure viewers.
+     * 
+     * @param jprovider
+     * @param jseqs
+     * @param af
+     * @param ap
      */
-    final AAStructureBindingModel binding = viewer.getBinding();
-    for (File id : oldFiles.keySet())
-    {
-      // add this and any other pdb files that should be present in the
-      // viewer
-      StructureData filedat = oldFiles.get(id);
-      String pdbFile = filedat.getFilePath();
-      SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
-      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
-              null);
-      binding.addSequenceForStructFile(pdbFile, seq);
+    protected void loadPDBStructures(jarInputStreamProvider jprovider,
+                                    List<JSeq> jseqs, AlignFrame af, AlignmentPanel ap)
+    {
+       /*
+        * Run through all PDB ids on the alignment, and collect mappings between
+        * distinct view ids and all sequences referring to that view.
+        */
+       Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<>();
+
+       for (int i = 0; i < jseqs.size(); i++)
+           {
+               JSeq jseq = jseqs.get(i);
+               if (jseq.getPdbids().size() > 0)
+                   {
+                       List<Pdbids> ids = jseq.getPdbids();
+                       for (int p = 0; p < ids.size(); p++)
+                           {
+                               Pdbids pdbid = ids.get(p);
+                               final int structureStateCount = pdbid.getStructureState().size();
+                               for (int s = 0; s < structureStateCount; s++)
+                                   {
+                                       // check to see if we haven't already created this structure view
+                                       final StructureState structureState = pdbid
+                                           .getStructureState().get(s);
+                                       String sviewid = (structureState.getViewId() == null) ? null
+                                           : structureState.getViewId() + uniqueSetSuffix;
+                                       jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
+                                       // Originally : pdbid.getFile()
+                                       // : TODO: verify external PDB file recovery still works in normal
+                                       // jalview project load
+                                       jpdb.setFile(
+                                                    loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile()));
+                                       jpdb.setId(pdbid.getId());
+
+                                       int x = safeInt(structureState.getXpos());
+                                       int y = safeInt(structureState.getYpos());
+                                       int width = safeInt(structureState.getWidth());
+                                       int height = safeInt(structureState.getHeight());
+
+                                       // Probably don't need to do this anymore...
+                                       // Desktop.getDesktop().getComponentAt(x, y);
+                                       // TODO: NOW: check that this recovers the PDB file correctly.
+                                       String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
+                                                                    pdbid.getFile());
+                                       jalview.datamodel.SequenceI seq = seqRefIds
+                                           .get(jseq.getId() + "");
+                                       if (sviewid == null)
+                                           {
+                                               sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + ","
+                                                   + height;
+                                           }
+                                       if (!structureViewers.containsKey(sviewid))
+                                           {
+                                               String viewerType = structureState.getType();
+                                               if (viewerType == null) // pre Jalview 2.9
+                                                   {
+                                                       viewerType = ViewerType.JMOL.toString();
+                                                   }
+                                               structureViewers.put(sviewid,
+                                                                    new StructureViewerModel(x, y, width, height, false,
+                                                                                             false, true, structureState.getViewId(),
+                                                                                             viewerType));
+                                               // Legacy pre-2.7 conversion JAL-823 :
+                                               // do not assume any view has to be linked for colour by
+                                               // sequence
+                                           }
+
+                                       // assemble String[] { pdb files }, String[] { id for each
+                                       // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
+                                       // seqs_file 2}, boolean[] {
+                                       // linkAlignPanel,superposeWithAlignpanel}} from hash
+                                       StructureViewerModel jmoldat = structureViewers.get(sviewid);
+                                       jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
+                                                                 || structureState.isAlignwithAlignPanel());
+
+                                       /*
+                                        * Default colour by linked panel to false if not specified (e.g.
+                                        * for pre-2.7 projects)
+                                        */
+                                       boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
+                                       colourWithAlignPanel |= structureState.isColourwithAlignPanel();
+                                       jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
+
+                                       /*
+                                        * Default colour by viewer to true if not specified (e.g. for
+                                        * pre-2.7 projects)
+                                        */
+                                       boolean colourByViewer = jmoldat.isColourByViewer();
+                                       colourByViewer &= structureState.isColourByJmol();
+                                       jmoldat.setColourByViewer(colourByViewer);
+
+                                       if (jmoldat.getStateData().length() < structureState
+                                           .getValue()/*Content()*/.length())
+                                           {
+                                               jmoldat.setStateData(structureState.getValue());// Content());
+                                           }
+                                       if (pdbid.getFile() != null)
+                                           {
+                                               File mapkey = new File(pdbid.getFile());
+                                               StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
+                                               if (seqstrmaps == null)
+                                                   {
+                                                       jmoldat.getFileData().put(mapkey,
+                                                                                 seqstrmaps = jmoldat.new StructureData(pdbFile,
+                                                                                                                        pdbid.getId()));
+                                                   }
+                                               if (!seqstrmaps.getSeqList().contains(seq))
+                                                   {
+                                                       seqstrmaps.getSeqList().add(seq);
+                                                       // TODO and chains?
+                                                   }
+                                           }
+                                       else
+                                           {
+                                               errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
+                                               Console.warn(errorMessage);
+                                           }
+                                   }
+                           }
+                   }
+           }
+       // Instantiate the associated structure views
+       for (Entry<String, StructureViewerModel> entry : structureViewers
+                .entrySet())
+           {
+               try
+                   {
+                       createOrLinkStructureViewer(entry, af, ap, jprovider);
+                   } catch (Exception e)
+                   {
+                       System.err.println(
+                                          "Error loading structure viewer: " + e.getMessage());
+                       // failed - try the next one
+                   }
+           }
     }
-    // and add the AlignmentPanel's reference to the view panel
-    viewer.addAlignmentPanel(ap);
-    if (useinViewerSuperpos)
-    {
-      viewer.useAlignmentPanelForSuperposition(ap);
+
+    /**
+     * 
+     * @param viewerData
+     * @param af
+     * @param ap
+     * @param jprovider
+     */
+    protected void createOrLinkStructureViewer(
+                                              Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+                                              AlignmentPanel ap, jarInputStreamProvider jprovider)
+    {
+       final StructureViewerModel stateData = viewerData.getValue();
+
+       /*
+        * Search for any viewer windows already open from other alignment views
+        * that exactly match the stored structure state
+        */
+       StructureViewerBase comp = findMatchingViewer(viewerData);
+
+       if (comp != null)
+           {
+               linkStructureViewer(ap, comp, stateData);
+               return;
+           }
+
+       String type = stateData.getType();
+       try
+           {
+               ViewerType viewerType = ViewerType.valueOf(type);
+               createStructureViewer(viewerType, viewerData, af, jprovider);
+           } catch (IllegalArgumentException | NullPointerException e)
+           {
+               // TODO JAL-3619 show error dialog / offer an alternative viewer
+               Console.error("Invalid structure viewer type: " + type);
+           }
     }
-    else
+    /**
+     * Generates a name for the entry in the project jar file to hold state
+     * information for a structure viewer
+     * 
+     * @param viewId
+     * @return
+     */
+    protected String getViewerJarEntryName(String viewId)
     {
-      viewer.excludeAlignmentPanelForSuperposition(ap);
+       return VIEWER_PREFIX + viewId;
     }
-    if (usetoColourbyseq)
-    {
-      viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
+
+    /**
+     * Returns any open frame that matches given structure viewer data. The match
+     * is based on the unique viewId, or (for older project versions) the frame's
+     * geometry.
+     * 
+     * @param viewerData
+     * @return
+     */
+    protected StructureViewerBase findMatchingViewer(
+                                                    Entry<String, StructureViewerModel> viewerData)
+    {
+       final String sviewid = viewerData.getKey();
+       final StructureViewerModel svattrib = viewerData.getValue();
+       StructureViewerBase comp = null;
+       JInternalFrame[] frames = getAllFrames();
+       for (JInternalFrame frame : frames)
+           {
+               if (frame instanceof StructureViewerBase)
+                   {
+                       /*
+                        * Post jalview 2.4 schema includes structure view id
+                        */
+                       if (sviewid != null && ((StructureViewerBase) frame).getViewId()
+                           .equals(sviewid))
+                           {
+                               comp = (StructureViewerBase) frame;
+                               break; // break added in 2.9
+                           }
+                       /*
+                        * Otherwise test for matching position and size of viewer frame
+                        */
+                       else if (frame.getX() == svattrib.getX()
+                                && frame.getY() == svattrib.getY()
+                                && frame.getHeight() == svattrib.getHeight()
+                                && frame.getWidth() == svattrib.getWidth())
+                           {
+                               comp = (StructureViewerBase) frame;
+                               // no break in faint hope of an exact match on viewId
+                           }
+                   }
+           }
+       return comp;
     }
-    else
-    {
-      viewer.excludeAlignmentPanelForColourbyseq(ap);
+
+    /**
+     * Link an AlignmentPanel to an existing structure viewer.
+     * 
+     * @param ap
+     * @param viewer
+     * @param oldFiles
+     * @param useinViewerSuperpos
+     * @param usetoColourbyseq
+     * @param viewerColouring
+     */
+    protected void linkStructureViewer(AlignmentPanel ap,
+                                      StructureViewerBase viewer, StructureViewerModel stateData)
+    {
+       // NOTE: if the jalview project is part of a shared session then
+       // view synchronization should/could be done here.
+
+       final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
+       final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
+       final boolean viewerColouring = stateData.isColourByViewer();
+       Map<File, StructureData> oldFiles = stateData.getFileData();
+
+       /*
+        * Add mapping for sequences in this view to an already open viewer
+        */
+       final AAStructureBindingModel binding = viewer.getBinding();
+       for (File id : oldFiles.keySet())
+           {
+               // add this and any other pdb files that should be present in the
+               // viewer
+               StructureData filedat = oldFiles.get(id);
+               String pdbFile = filedat.getFilePath();
+               SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
+               binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
+                                           null);
+               binding.addSequenceForStructFile(pdbFile, seq);
+           }
+       // and add the AlignmentPanel's reference to the view panel
+       viewer.addAlignmentPanel(ap);
+       if (useinViewerSuperpos)
+           {
+               viewer.useAlignmentPanelForSuperposition(ap);
+           }
+       else
+           {
+               viewer.excludeAlignmentPanelForSuperposition(ap);
+           }
+       if (usetoColourbyseq)
+           {
+               viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
+           }
+       else
+           {
+               viewer.excludeAlignmentPanelForColourbyseq(ap);
+           }
     }
-  }
 
-  /**
-   * Get all frames within the Desktop.
-   * 
-   * @return
-   */
-  protected JInternalFrame[] getAllFrames()
-  {
-    JInternalFrame[] frames = null;
-    // TODO is this necessary - is it safe - risk of hanging?
-    do
-    {
-      try
-      {
-        frames = Desktop.desktop.getAllFrames();
-      } catch (ArrayIndexOutOfBoundsException e)
-      {
-        // occasional No such child exceptions are thrown here...
-        try
-        {
-          Thread.sleep(10);
-        } catch (InterruptedException f)
-        {
-        }
-      }
-    } while (frames == null);
-    return frames;
-  }
+    /**
+     * Get all frames within the Desktop.
+     * 
+     * @return
+     */
+    protected JInternalFrame[] getAllFrames()
+    {
+       JInternalFrame[] frames = null;
+       // TODO is this necessary - is it safe - risk of hanging?
+       do
+           {
+               try
+                   {
+                       frames = Desktop.getDesktopPane().getAllFrames();
+                   } catch (ArrayIndexOutOfBoundsException e)
+                   {
+                       // occasional No such child exceptions are thrown here...
+                       try
+                           {
+                               Thread.sleep(10);
+                           } catch (InterruptedException f)
+                           {
+                           }
+                   }
+           } while (frames == null);
+       return frames;
+    }
 
-  /**
-   * Answers true if 'version' is equal to or later than 'supported', where each
-   * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
-   * changes. Development and test values for 'version' are leniently treated
-   * i.e. answer true.
-   * 
-   * @param supported
-   *          - minimum version we are comparing against
-   * @param version
-   *          - version of data being processsed
-   * @return
-   */
-  public static boolean isVersionStringLaterThan(String supported,
-          String version)
+    /**
+     * Answers true if 'version' is equal to or later than 'supported', where each
+     * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
+     * changes. Development and test values for 'version' are leniently treated
+     * i.e. answer true.
+     * 
+     * @param supported
+     *          - minimum version we are comparing against
+     * @param version
+     *          - version of data being processsed
+     * @return
+     */
+    public static boolean isVersionStringLaterThan(String supported,
+                                               String version)
   {
     if (supported == null || version == null
             || version.equalsIgnoreCase("DEVELOPMENT BUILD")
@@ -4625,7 +4850,7 @@ public class Jalview2XML
     }
   }
 
-  AlignFrame loadViewport(String file, List<JSeq> JSEQ,
+  AlignFrame loadViewport(String fileName, File file, List<JSeq> JSEQ,
           List<SequenceI> hiddenSeqs, AlignmentI al, JalviewModel jm,
           Viewport view, String uniqueSeqSetId, String viewId,
           List<JvAnnotRow> autoAlan)
@@ -4645,7 +4870,9 @@ public class Jalview2XML
     // }
     ;
 
-    af.setFileName(file, FileFormat.Jalview);
+    af.alignPanel.setHoldRepaint(true);
+    af.setFile(fileName, file, null, FileFormat.Jalview);
+    af.setFileObject(jarFile); // BH 2019 JAL-3436
 
     final AlignViewport viewport = af.getViewport();
     for (int i = 0; i < JSEQ.size(); i++)
@@ -4752,8 +4979,18 @@ public class Jalview2XML
       viewport.setViewName(view.getViewName());
       af.setInitialTabVisible();
     }
-    af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
-            safeInt(view.getWidth()), safeInt(view.getHeight()));
+    int x = safeInt(view.getXpos());
+    int y = safeInt(view.getYpos());
+    int w = safeInt(view.getWidth());
+    int h = safeInt(view.getHeight());
+    // // BH we cannot let the title bar go off the top
+    // if (Platform.isJS())
+    // {
+    // x = Math.max(50 - w, x);
+    // y = Math.max(0, y);
+    // }
+
+    af.setBounds(x, y, w, h);
     // startSeq set in af.alignPanel.updateLayout below
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
@@ -4975,8 +5212,9 @@ public class Jalview2XML
     String complementaryViewId = view.getComplementId();
     if (complementaryViewId == null)
     {
-      Desktop.addInternalFrame(af, view.getTitle(),
+      Dimension dim = Platform.getDimIfEmbedded(af,
               safeInt(view.getWidth()), safeInt(view.getHeight()));
+      Desktop.addInternalFrame(af, view.getTitle(), dim.width, dim.height);
       // recompute any autoannotation
       af.alignPanel.updateAnnotation(false, true);
       reorderAutoannotation(af, al, autoAlan);
@@ -5240,7 +5478,7 @@ public class Jalview2XML
     return false;
   }
 
-  public void addToSkipList(AlignFrame af)
+  protected void addToSkipList(AlignFrame af)
   {
     if (skipList == null)
     {
@@ -5249,7 +5487,7 @@ public class Jalview2XML
     skipList.put(af.getViewport().getSequenceSetId(), af);
   }
 
-  public void clearSkipList()
+  protected void clearSkipList()
   {
     if (skipList != null)
     {
@@ -5320,8 +5558,8 @@ public class Jalview2XML
       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
       dseqs.copyInto(dsseqs);
       ds = new jalview.datamodel.Alignment(dsseqs);
-      Console.debug("Created new dataset " + vamsasSet.getDatasetId()
-              + " for alignment " + System.identityHashCode(al));
+//      Console.debug("Jalview2XML Created new dataset " + vamsasSet.getDatasetId()
+//              + " for alignment " + System.identityHashCode(al));
       addDatasetRef(vamsasSet.getDatasetId(), ds);
     }
     // set the dataset for the newly imported alignment.
@@ -5752,9 +5990,11 @@ public class Jalview2XML
 
     viewportsAdded.clear();
 
-    AlignFrame af = loadFromObject(jm, null, false, null);
+    AlignFrame af = loadFromObject(jm, null, null, false, null);
     af.getAlignPanels().clear();
     af.closeMenuItem_actionPerformed(true);
+    af.alignPanel.setHoldRepaint(false);
+    this.jarFile = null;
 
     /*
      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
@@ -6058,8 +6298,10 @@ public class Jalview2XML
                   axis.getXPos(), axis.getYPos(), axis.getZPos());
         }
 
+        Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450);
         Desktop.addInternalFrame(panel, MessageManager.formatMessage(
-                "label.calc_title", "PCA", modelName), 475, 450);
+                "label.calc_title", "PCA", modelName), dim.width,
+                dim.height);
       }
     } catch (Exception ex)
     {
@@ -6095,9 +6337,10 @@ public class Jalview2XML
     }
     final String sessionPath = sessionFilePath;
     final String sviewid = viewerData.getKey();
-    try
-    {
-      SwingUtilities.invokeAndWait(new Runnable()
+// BH again was invokeAndWait
+    // try
+    // {
+      javax.swing.SwingUtilities.invokeLater(new Runnable()
       {
         @Override
         public void run()
@@ -6121,11 +6364,11 @@ public class Jalview2XML
           }
         }
       });
-    } catch (InvocationTargetException | InterruptedException ex)
-    {
-      Console.warn("Unexpected error when opening " + viewerType
-              + " structure viewer", ex);
-    }
+//    } catch (InvocationTargetException | InterruptedException ex)
+//    {
+//      Console.warn("Unexpected error when opening " + viewerType
+//              + " structure viewer", ex);
+//    }
   }
 
   /**
@@ -6242,8 +6485,8 @@ public class Jalview2XML
    * @param fcol
    * @return
    */
-  public static Colour marshalColour(String featureType,
-          FeatureColourI fcol)
+  public static Colour marshalColour(
+          String featureType, FeatureColourI fcol)
   {
     Colour col = new Colour();
     if (fcol.isSimpleColour())
@@ -6365,7 +6608,8 @@ public class Jalview2XML
    * @param matcherSetModel
    * @return
    */
-  public static FeatureMatcherSetI parseFilter(String featureType,
+  public static FeatureMatcherSetI parseFilter(
+          String featureType,
           jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel)
   {
     FeatureMatcherSetI result = new FeatureMatcherSet();
@@ -6395,7 +6639,8 @@ public class Jalview2XML
    * @throws IllegalStateException
    *           if AND and OR conditions are mixed
    */
-  protected static void parseFilterConditions(FeatureMatcherSetI matcherSet,
+  protected static void parseFilterConditions(
+          FeatureMatcherSetI matcherSet,
           jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel,
           boolean and)
   {
@@ -6538,3 +6783,4 @@ public class Jalview2XML
     return colour;
   }
 }
+
index be4f497..7ceb901 100644 (file)
@@ -29,12 +29,14 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.ProfilesI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.NucleotideColourScheme;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.ZappoColourScheme;
 import jalview.util.Platform;
+import jalview.workers.InformationThread;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -69,8 +71,14 @@ public class AnnotationRenderer
 
   private final boolean USE_FILL_ROUND_RECT = Platform.isAMacAndNotJS();
 
-  boolean av_renderHistogram = true, av_renderProfile = true,
-          av_normaliseProfile = false;
+  // todo remove these flags, read from group/viewport where needed
+  boolean av_renderHistogram = true;
+
+  boolean av_renderProfile = true;
+
+  boolean av_normaliseProfile = false;
+
+  boolean av_infoHeight = false;
 
   ResidueShaderI profcolour = null;
 
@@ -86,6 +94,8 @@ public class AnnotationRenderer
 
   private boolean av_ignoreGapsConsensus;
 
+  private boolean av_ignoreBelowBackground;
+
   /**
    * attributes set from AwtRenderPanelI
    */
@@ -345,8 +355,12 @@ public class AnnotationRenderer
     complementConsensus = av.getComplementConsensusHash();
     hStrucConsensus = av.getRnaStructureConsensusHash();
     av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
+    av_ignoreBelowBackground = av.isIgnoreBelowBackground();
+    av_infoHeight = av.isInfoLetterHeight();
   }
 
+
+
   /**
    * Returns profile data; the first element is the profile type, the second is
    * the number of distinct values, the third the total count, and the remainder
@@ -362,17 +376,25 @@ public class AnnotationRenderer
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
-    if (aa.autoCalculated && (aa.label.startsWith("Consensus")
-            || aa.label.startsWith("cDNA Consensus")))
+    if (InformationThread.HMM_CALC_ID.equals(aa.getCalcId()))
+    {
+      HiddenMarkovModel hmm = aa.sequenceRef.getHMM();
+      return AAFrequency.extractHMMProfile(hmm, column,
+              av_ignoreBelowBackground, av_infoHeight); // TODO check if this follows standard
+                                         // pipeline
+    }
+    if (aa.autoCalculated
+            && (aa.label.startsWith("Consensus") || aa.label
+                    .startsWith("cDNA Consensus")))
     {
       boolean forComplement = aa.label.startsWith("cDNA Consensus");
-      if (aa.groupRef != null && aa.groupRef.consensusData != null
+      if (aa.groupRef != null && aa.groupRef.getConsensusData() != null
               && aa.groupRef.isShowSequenceLogo())
       {
         // TODO? group consensus for cDNA complement
         return AAFrequency.extractProfile(
-                aa.groupRef.consensusData.get(column),
-                aa.groupRef.getIgnoreGapsConsensus());
+                aa.groupRef.getConsensusData().get(column),
+                                               aa.groupRef.getIgnoreGapsConsensus());
       }
       // TODO extend annotation row to enable dynamic and static profile data to
       // be stored
@@ -501,6 +523,28 @@ public class AnnotationRenderer
         renderProfile = av_renderProfile;
         normaliseProfile = av_normaliseProfile;
       }
+      else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId()))
+      {
+        if (row.groupRef != null)
+        {
+          renderHistogram = row.groupRef.isShowInformationHistogram();
+          renderProfile = row.groupRef.isShowHMMSequenceLogo();
+          normaliseProfile = row.groupRef.isNormaliseHMMSequenceLogo();
+        }
+        else
+        {
+          renderHistogram = av.isShowInformationHistogram();
+          renderProfile = av.isShowHMMSequenceLogo();
+          normaliseProfile = av.isNormaliseHMMSequenceLogo();
+        }
+      }
+      else if (row == consensusAnnot || row == structConsensusAnnot
+              || row == complementConsensusAnnot)
+      {
+        renderHistogram = av_renderHistogram;
+        renderProfile = av_renderProfile;
+        normaliseProfile = av_normaliseProfile;
+      }
 
       Annotation[] row_annotations = row.annotations;
       if (!row.visible)
index 4d97171..7e67598 100644 (file)
@@ -5,16 +5,16 @@
  * 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 
+ * modify it under the terms of the GNU General 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.
+ * PURPOSE.  See the GNU General License for more details.
  * 
- * You should have received a copy of the GNU General Public License
+ * You should have received a copy of the GNU General License
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
@@ -32,16 +32,15 @@ import java.util.Map;
 
 public interface ResidueShaderI
 {
+  void setConsensus(ProfilesI cons);
 
-  public abstract void setConsensus(ProfilesI cons);
+  boolean conservationApplied();
 
-  public abstract boolean conservationApplied();
+  void setConservationApplied(boolean conservationApplied);
 
-  public abstract void setConservationApplied(boolean conservationApplied);
+  void setConservation(Conservation cons);
 
-  public abstract void setConservation(Conservation cons);
-
-  public abstract void alignmentChanged(AnnotatedCollectionI alignment,
+  void alignmentChanged(AnnotatedCollectionI alignment,
           Map<SequenceI, SequenceCollectionI> hiddenReps);
 
   /**
@@ -51,19 +50,19 @@ public interface ResidueShaderI
    * @param consensusThreshold
    * @param ignoreGaps
    */
-  public abstract void setThreshold(int consensusThreshold,
+  void setThreshold(int consensusThreshold,
           boolean ignoreGaps);
 
-  public abstract void setConservationInc(int i);
+  void setConservationInc(int i);
 
-  public abstract int getConservationInc();
+  int getConservationInc();
 
   /**
    * Get the percentage threshold for this colour scheme
    * 
    * @return Returns the percentage threshold
    */
-  public abstract int getThreshold();
+  int getThreshold();
 
   /**
    * Returns the possibly context dependent colour for the given symbol at the
@@ -75,11 +74,11 @@ public interface ResidueShaderI
    * @param seq
    * @return
    */
-  public abstract Color findColour(char symbol, int position,
+  Color findColour(char symbol, int position,
           SequenceI seq);
 
-  public abstract ColourSchemeI getColourScheme();
+  ColourSchemeI getColourScheme();
 
-  public abstract void setColourScheme(ColourSchemeI cs);
+  void setColourScheme(ColourSchemeI cs);
 
 }
\ No newline at end of file
index a37882f..7c8c9a6 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.rest;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.httpserver.AbstractRequestHandler;
 
 import java.io.IOException;
@@ -30,20 +32,16 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * A simple handler to process (or delegate) HTTP requests on /jalview/rest
+ * A simple handler to process (or delegate) HTTP requests on /jalview/rest.
  */
 public class RestHandler extends AbstractRequestHandler
+        implements ApplicationSingletonI
 {
   private static final String MY_PATH = "rest";
 
   private static final String MY_NAME = "Rest";
 
   /**
-   * Singleton instance of this class
-   */
-  private static RestHandler instance = null;
-
-  /**
    * Returns the singleton instance of this class
    * 
    * @return
@@ -53,12 +51,8 @@ public class RestHandler extends AbstractRequestHandler
   {
     synchronized (RestHandler.class)
     {
-      if (instance == null)
-      {
-        instance = new RestHandler();
-      }
+      return (RestHandler) ApplicationSingletonProvider.getInstance(RestHandler.class);
     }
-    return instance;
   }
 
   /**
index a390a15..764bb12 100755 (executable)
@@ -37,6 +37,16 @@ import java.util.Map;
 
 public class AnnotationColourGradient extends FollowerColourScheme
 {
+  /**
+   * map positional scores to transparency rather than colour
+   */
+  boolean positionToTransparency = false;
+
+  /**
+   * compute shade based on annotation row score
+   */
+  boolean perLineScore = false;
+
   public static final int NO_THRESHOLD = -1;
 
   public static final int BELOW_THRESHOLD = 0;
@@ -94,6 +104,8 @@ public class AnnotationColourGradient extends FollowerColourScheme
     acg.predefinedColours = predefinedColours;
     acg.seqAssociated = seqAssociated;
     acg.noGradient = noGradient;
+    acg.positionToTransparency = positionToTransparency;
+    acg.perLineScore = perLineScore;
     return acg;
   }
 
@@ -192,14 +204,16 @@ public class AnnotationColourGradient extends FollowerColourScheme
       AnnotatedCollectionI alcontext = alignment instanceof AlignmentI
               ? alignment
               : alignment.getContext();
-      boolean f = true, rna = false;
-      for (AlignmentAnnotation alan : alcontext
-              .findAnnotation(annotation.getCalcId()))
+      boolean f = true, sf = true, rna = false;
+      long plcount = 0, ancount = 0;
+      for (AlignmentAnnotation alan : alcontext.findAnnotation(annotation
+              .getCalcId()))
       {
         if (alan.sequenceRef != null
                 && (alan.label != null && annotation != null
                         && alan.label.equals(annotation.label)))
         {
+          ancount++;
           if (!rna && alan.isRNA())
           {
             rna = true;
@@ -214,8 +228,30 @@ public class AnnotationColourGradient extends FollowerColourScheme
             aamin = alan.graphMin;
           }
           f = false;
+          if (alan.score == alan.score)
+          {
+            if (sf || alan.score < plmin)
+            {
+              plmin = alan.score;
+            }
+            if (sf || alan.score > plmax)
+            {
+              plmax = alan.score;
+            }
+            sf = false;
+            plcount++;
+          }
         }
       }
+      if (plcount > 0 && plcount == ancount)
+      {
+        perLineScore = plmax!=plmin;
+        aamax=plmax;
+      }
+      else
+      {
+        perLineScore = false;
+      }
       if (rna)
       {
         ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
@@ -223,7 +259,15 @@ public class AnnotationColourGradient extends FollowerColourScheme
     }
   }
 
-  float aamin = 0f, aamax = 0f;
+  /**
+   * positional annotation max/min
+   */
+  double aamin = 0.0, aamax = 0.0;
+
+  /**
+   * per line score max/min
+   */
+  double plmin = Double.NaN, plmax = Double.NaN;
 
   public AlignmentAnnotation getAnnotation()
   {
@@ -439,11 +483,25 @@ public class AnnotationColourGradient extends FollowerColourScheme
       }
     }
 
-    int dr = (int) (redRange * range + redMin);
-    int dg = (int) (greenRange * range + greenMin);
-    int db = (int) (blueRange * range + blueMin);
-
-    return new Color(dr, dg, db);
+    // midtr sets the ceiling for bleaching out the shading
+    int trans = 0, midtr = 239;
+    if (perLineScore)
+    {
+      trans = (int) ((1f - range) * midtr);
+      range = (float) ((ann.score - plmin) / (plmax - aamin));
+    }
+    int dr = (int) (redRange * range + redMin),
+            dg = (int) (greenRange * range + greenMin),
+            db = (int) (blueRange * range + blueMin);
+    if (ann.score == ann.score && positionToTransparency)
+    {
+      return new Color(Math.min(dr + trans, midtr), Math.min(dg
+              + trans, midtr), Math.min(db + trans, midtr));
+    }
+    else
+    {
+      return new Color(dr, dg, db);
+    }
   }
 
   public boolean isPredefinedColours()
@@ -487,4 +545,14 @@ public class AnnotationColourGradient extends FollowerColourScheme
   {
     return false;
   }
+
+  public boolean isPositionToTransparency()
+  {
+    return positionToTransparency;
+  }
+
+  public void setPositionToTransparency(boolean positionToTransparency)
+  {
+    this.positionToTransparency = positionToTransparency;
+  }
 }
index 14daed6..18f24fb 100644 (file)
@@ -23,24 +23,19 @@ package jalview.schemes;
 import java.util.Locale;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
+import jalview.util.ColorUtils;
 
+import java.awt.Color;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-public class ColourSchemes
+public class ColourSchemes implements ApplicationSingletonI
 {
-  /*
-   * singleton instance of this class
-   */
-  private static ColourSchemes instance = new ColourSchemes();
-
-  /*
-   * a map from scheme name (lower-cased) to an instance of it
-   */
-  private Map<String, ColourSchemeI> schemes;
 
   /**
    * Returns the singleton instance of this class
@@ -49,7 +44,8 @@ public class ColourSchemes
    */
   public static ColourSchemes getInstance()
   {
-    return instance;
+    return (ColourSchemes) ApplicationSingletonProvider
+            .getInstance(ColourSchemes.class);
   }
 
   private ColourSchemes()
@@ -58,6 +54,51 @@ public class ColourSchemes
   }
 
   /**
+   * ColourSchemeProperty "static"
+   */
+  public Color[] rnaHelices = null;
+
+  /**
+   * delete the existing cached RNA helices colours
+   */
+  public static void resetRnaHelicesShading()
+  {
+    getInstance().rnaHelices = null;
+  }
+
+  public static void initRnaHelicesShading(int n)
+  {
+    int i = 0;
+    ColourSchemes j = getInstance();
+
+    if (j.rnaHelices == null)
+    {
+      j.rnaHelices = new Color[n + 1];
+    }
+    else if (j.rnaHelices != null && j.rnaHelices.length <= n)
+    {
+      Color[] t = new Color[n + 1];
+      System.arraycopy(j.rnaHelices, 0, t, 0, j.rnaHelices.length);
+      i = j.rnaHelices.length;
+      j.rnaHelices = t;
+    }
+    else
+    {
+      return;
+    }
+    // Generate random colors and store
+    for (; i <= n; i++)
+    {
+      j.rnaHelices[i] = ColorUtils.generateRandomColor(Color.white);
+    }
+  }
+
+  /**
+   * a map from scheme name (lower-cased) to an instance of it
+   */
+  private Map<String, ColourSchemeI> schemes;
+
+  /**
    * Loads an instance of each standard or user-defined colour scheme
    * 
    * @return
index 6e8554f..81799fd 100644 (file)
@@ -22,6 +22,7 @@ package jalview.schemes;
 
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsModelI;
+import jalview.datamodel.features.FeatureMatcherSetI;
 
 /**
  * An adapter class that may be extended to instantiate feature colour schemes
@@ -71,4 +72,10 @@ public class FeatureSettingsAdapter implements FeatureSettingsModelI
     return false;
   }
 
+  @Override
+  public FeatureMatcherSetI getFeatureFilters(String type)
+  {
+    return null;
+  }
+
 }
diff --git a/src/jalview/schemes/HMMMatchScoreColourScheme.java b/src/jalview/schemes/HMMMatchScoreColourScheme.java
new file mode 100644 (file)
index 0000000..96eb26a
--- /dev/null
@@ -0,0 +1,369 @@
+package jalview.schemes;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+
+
+public class HMMMatchScoreColourScheme extends ResidueColourScheme
+{
+  
+  private Map<Character, Map<Integer, Map<String, Double>>> probabilities;
+
+  private List<Integer> ranges;
+
+  private static double binSize;
+
+  public class MatchProbReader
+  {
+    private BufferedReader reader;
+    
+    MatchProbReader() throws FileNotFoundException
+    {
+      reader = new BufferedReader(
+              new FileReader("resources/ProbabilityOfMatch"));
+    }
+    
+    /*
+    public Map<Character, Map<String, Double>>  getProbabilities() throws IOException
+    {
+      Map<Character, Map<String, Double>> probabilities = new HashMap<>();
+      
+      String[] alphabet = reader.readLine().split("\\,");
+      for (int i = 1; i < alphabet.length - 1; i++)
+      {
+        probabilities.put(alphabet[i].replaceAll("\\ ", "").charAt(0),
+                new HashMap<>());
+      }
+      
+      String line = reader.readLine();
+      while(line != null)
+      {
+        String[] contents = line.split("\\,");
+        
+        for(int i = 1; i < contents.length; i++)
+        {
+          probabilities.get(alphabet[i].replaceAll("\\ ", "").charAt(0))
+                  .put(contents[0], Double
+                          .valueOf(contents[i].replaceAll("\\ ", "")));
+        }
+        line = reader.readLine();
+    
+      }
+      reader.close();
+      return probabilities;
+    }
+    */
+    
+    public Map<Character, Map<Integer, Map<String, Double>>> getProbabilities()
+            throws IOException
+    {
+
+      Map<Character, Map<Integer, Map<String, Double>>> probabilities = new HashMap<>();
+
+      ranges = new ArrayList<>();
+      ranges.add(0);
+
+      binSize = Double.valueOf((reader.readLine().replaceAll("\\ ", "")));
+      String line = reader.readLine();
+      char c = line.charAt(0);
+
+      while (line != null)
+      {
+        line = reader.readLine();
+        while (line != null && line.split("\\,").length != 1)
+        {
+          String[] llrs = line.split("\\,");
+          String[] counts = reader.readLine().split("\\,");
+          int range = Integer.valueOf(llrs[0]);
+
+          if (!ranges.contains(range))
+          {
+            ranges.add(range);
+          }
+          if (!probabilities.containsKey(c))
+          {
+            probabilities.put(c, new HashMap<>());
+          }
+          probabilities.get(c).put(range, new HashMap<>());
+
+          for (int i = 1; i < llrs.length; i++)
+          {
+            probabilities.get(c).get(range).put(
+                    llrs[i].replaceAll("\\ ", ""),
+                    Double.valueOf(counts[i].replaceAll("\\ ", "")));
+          }
+
+          line = reader.readLine();
+        }
+        if (line != null)
+        {
+          c = line.charAt(0);
+        }
+      }
+
+      return probabilities;
+    }
+    
+  }
+
+  private static final Color INSERTION_COLOUR = Color.white;
+
+  /*
+   * the aligned HMM consensus sequence to use as reference for colouring
+   */
+  private SequenceI hmmSeq;
+
+  private HiddenMarkovModel hmm;
+
+  private Map<Character, Float> frequencies;
+
+  /**
+   * Constructor given a list of Hidden Markov Model consensus sequences. The
+   * first sequence provides the HMM profile from which we can read the emission
+   * probabilities that determine the colour.
+   * 
+   * @param hmmSeqs
+   * @throws IOException
+   */
+  public HMMMatchScoreColourScheme(List<SequenceI> hmmSeqs)
+  {
+    hmmSeq = hmmSeqs.isEmpty() ? null : hmmSeqs.get(0);
+    hmm = hmmSeq == null ? null : hmmSeq.getHMM();
+
+    try
+    {
+      MatchProbReader probabilityReader = new MatchProbReader();
+      probabilities = probabilityReader.getProbabilities();
+    } catch (IOException e)
+    {
+      System.out.println(e.getStackTrace());
+    }
+  }
+
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
+  public HMMMatchScoreColourScheme()
+  {
+  }
+
+  @Override
+  public Color findColour(char symbol, int column, SequenceI seq,
+          String consensusResidue, float pid)
+  {
+    if (seq == null)
+    {
+      return null;
+    }
+    return findColour(symbol, column, seq.gapMap().length);
+  }
+
+  // TODO change documentation
+  /**
+   * Returns the colour at a particular symbol at a column in the alignment:
+   * <ul>
+   * <li>white for a gap</li>
+   * <li>red for an insertion</li>
+   * <li>orange for negative information content</li>
+   * <li>white to blue for increasing information content</li>
+   * </ul>
+   * 
+   * @param symbol
+   * @param column
+   * @return
+   */
+  private Color findColour(char symbol, int column, int length)
+  {
+    if (getHmm() == null || Comparison.isGap(symbol))
+    {
+      return Color.white;
+    }
+    if (Comparison.isGap(hmmSeq.getCharAt(column)))
+    {
+      return INSERTION_COLOUR;
+    }
+    if (Character.isLowerCase(symbol))
+    {
+      symbol = Character.toUpperCase(symbol);
+    }
+
+    double prob = 0;
+    if (hmm.getBackgroundFrequencies().containsKey(symbol))
+    {
+      int lengthBin = getLengthBin(length);
+
+      double llr = Math
+              .log(getHmm().getMatchEmissionProbability(column, symbol)
+                      / hmm.getBackgroundFrequencies().get(symbol));
+
+      if (!probabilities.containsKey(symbol)
+              || !probabilities.get(symbol).get(lengthBin)
+              .containsKey(format(llr)))
+      {
+        return new Color(140, 140, 140);
+      }
+
+
+      prob = probabilities.get(symbol).get(lengthBin).get(format(llr));
+    }
+    else
+    {
+      return new Color(140, 140, 140);
+    }
+
+    Color colour = Color.ORANGE;
+    if (prob >= 0.5)
+    {
+
+      colour = ColorUtils.getGraduatedColour((float) prob, 0.5f,
+              Color.WHITE, 1f,
+              Color.blue);
+    }
+    else
+    {
+      colour = ColorUtils.getGraduatedColour((float) prob, 0f, Color.red,
+              0.5f, Color.WHITE);
+    }
+
+    return colour;
+  }
+
+  public static String format(Double d)
+  {
+    String formatArg = String.valueOf(binSize);
+
+    // if bin size, need format "%.n" where n is number of decimal places
+    if (binSize < 1)
+    {
+      formatArg = "." + formatArg.split("\\.")[1].length();
+    }
+
+    Double rounded = Math.round(d / binSize) * binSize;
+    String formatted = String.format("%" + formatArg + "f", rounded);
+
+    // format sometimes returns a number rounded to 0 as -0
+    // this ensures output will always be 0
+    if (Double.valueOf(formatted) == 0)
+    {
+      formatted = "0";
+    }
+    return formatted;
+
+  }
+
+  /**
+   * Answers the maximum possible value of information score (log ratio), for use
+   * in scaling a graduated colour range
+   * 
+   * @return
+   */
+  protected float getMaxInformationScore()
+  {
+    return 0f;
+  }
+
+  /**
+   * Answers a new colour scheme instance based on the HMM of the first sequence
+   * in ac that has an HMM
+   */
+  @Override
+  public ColourSchemeI getInstance(AlignViewportI viewport,
+          AnnotatedCollectionI ac)
+  {
+    return newInstance(ac);
+  }
+
+  /**
+   * Constructor given a sequence collection
+   * 
+   * @param ac
+   */
+  public HMMMatchScoreColourScheme(AnnotatedCollectionI ac)
+  {
+    this(ac.getHmmSequences());
+  }
+
+  /**
+   * Answers a new instance of the colour scheme for the given HMM
+   * 
+   * @param ac
+   * @return
+   */
+  protected HMMMatchScoreColourScheme newInstance(AnnotatedCollectionI ac)
+  {
+    return new HMMMatchScoreColourScheme(ac);
+  }
+
+  @Override
+  public boolean isSimple()
+  {
+    return false;
+  }
+
+  /**
+   * Answers true if the sequence collection has an HMM consensus sequence and
+   * that the first HMM sequence contains background frequencies, else false
+   */
+  @Override
+  public boolean isApplicableTo(AnnotatedCollectionI ac)
+  {
+    return !ac.getHmmSequences().isEmpty() && ac.getHmmSequences().get(0)
+            .getHMM().getBackgroundFrequencies() != null;
+  }
+
+  protected Map<Character, Float> getFrequencies()
+  {
+    return frequencies;
+  }
+
+  protected void setFrequencies(Map<Character, Float> frequencies)
+  {
+    this.frequencies = frequencies;
+  }
+
+  protected HiddenMarkovModel getHmm()
+  {
+    return hmm;
+  }
+
+  protected SequenceI getHmmSequence()
+  {
+    return hmmSeq;
+  }
+
+  @Override
+  public String getSchemeName()
+  {
+    return JalviewColourScheme.HMMMatchScore.toString();
+  }
+
+  private int getLengthBin(int l)
+  {
+    for (int i = 1; i < ranges.size(); i++)
+    {
+      if (l >= ranges.get(i - 1) && l < ranges.get(i))
+      {
+        return ranges.get(i);
+      }
+    }
+    return -1;
+  }
+}
+
+
diff --git a/src/jalview/schemes/HmmerColourScheme.java b/src/jalview/schemes/HmmerColourScheme.java
new file mode 100644 (file)
index 0000000..05c9b66
--- /dev/null
@@ -0,0 +1,197 @@
+package jalview.schemes;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for colour schemes based on a selected Hidden Markov Model. The
+ * colour is with reference to an HMM consensus sequence and HMM profile
+ * <ul>
+ * <li>white for a gap</li>
+ * <li>red for an insertion (position is gapped in the HMM consensus)</li>
+ * <li>orange for negative information content</li>
+ * <li>white to blue for increasing information content</li>
+ * </ul>
+ * where information content is the log ratio
+ * 
+ * <pre>
+ *   log(profile match emission probability / residue background probability)
+ * </pre>
+ * 
+ * Sub-class implementations use either global ('Uniprot') or local
+ * ('alignment') background frequencies.
+ * 
+ * @author tzvanaalten
+ * @author gmcarstairs
+ */
+public abstract class HmmerColourScheme extends ResidueColourScheme
+{
+  private static final Color INSERTION_COLOUR = new Color(230, 0, 0); // reddish
+
+  /*
+   * the aligned HMM consensus sequence to use as reference for colouring
+   */
+  private SequenceI hmmSeq;
+
+  private HiddenMarkovModel hmm;
+
+  private Map<Character, Float> frequencies;
+
+  /**
+   * Constructor given a list of Hidden Markov Model consensus sequences. The
+   * first sequence provides the HMM profile from which we can read the emission
+   * probabilities that determine the colour.
+   * 
+   * @param hmmSeqs
+   */
+  public HmmerColourScheme(List<SequenceI> hmmSeqs)
+  {
+    hmmSeq = hmmSeqs.isEmpty() ? null : hmmSeqs.get(0);
+    hmm = hmmSeq == null ? null : hmmSeq.getHMM();
+  }
+
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
+  public HmmerColourScheme()
+  {
+  }
+
+  @Override
+  public Color findColour(char symbol, int column, SequenceI seq,
+          String consensusResidue, float pid)
+  {
+    return findColour(symbol, column);
+  }
+
+  /**
+   * Returns the colour at a particular symbol at a column in the alignment:
+   * <ul>
+   * <li>white for a gap</li>
+   * <li>red for an insertion</li>
+   * <li>orange for negative information content</li>
+   * <li>white to blue for increasing information content</li>
+   * </ul>
+   * 
+   * @param symbol
+   * @param column
+   * @return
+   */
+  private Color findColour(char symbol, int column)
+  {
+    if (getHmm() == null || Comparison.isGap(symbol))
+    {
+      return Color.white;
+    }
+    if (Comparison.isGap(hmmSeq.getCharAt(column)))
+    {
+      return INSERTION_COLOUR;
+    }
+    if (Character.isLowerCase(symbol))
+    {
+      symbol = Character.toUpperCase(symbol);
+    }
+
+    final double prob = getHmm().getMatchEmissionProbability(column,
+            symbol);
+
+    Float freq = 0f;
+
+    if (!frequencies.containsKey(symbol))
+    {
+      return Color.WHITE;
+    }
+    else
+    {
+      freq = frequencies.get(symbol);
+    }
+
+    /*
+     * Orange if match emission probability is less than background probability
+     */
+    double infoRatio = prob / freq.floatValue();
+    Color colour = Color.ORANGE;
+    if (infoRatio >= 1)
+    {
+      /*
+       * log-scale graduated shade of blue if prob is greater than background  
+       */
+      float infoLog = (float) Math.log(infoRatio);
+      colour = ColorUtils.getGraduatedColour(infoLog, 0, Color.WHITE,
+              getMaxInformationScore(), Color.blue);
+    }
+
+    return colour;
+  }
+
+  /**
+   * Answers the maximum possible value of information score (log ratio), for
+   * use in scaling a graduated colour range
+   * 
+   * @return
+   */
+  abstract float getMaxInformationScore();
+
+  /**
+   * Answers a new colour scheme instance based on the HMM of the first sequence
+   * in ac that has an HMM
+   */
+  @Override
+  public ColourSchemeI getInstance(AlignViewportI viewport,
+          AnnotatedCollectionI ac)
+  {
+    return newInstance(ac);
+  }
+
+  /**
+   * Answers a new instance of the colour scheme for the given HMM
+   * 
+   * @param ac
+   * @return
+   */
+  protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac);
+
+  @Override
+  public boolean isSimple()
+  {
+    return false;
+  }
+
+  /**
+   * Answers true if the sequence collection has an HMM consensus sequence, else
+   * false
+   */
+  @Override
+  public boolean isApplicableTo(AnnotatedCollectionI ac)
+  {
+    return !ac.getHmmSequences().isEmpty();
+  }
+
+  protected Map<Character, Float> getFrequencies()
+  {
+    return frequencies;
+  }
+
+  protected void setFrequencies(Map<Character, Float> frequencies)
+  {
+    this.frequencies = frequencies;
+  }
+
+  protected HiddenMarkovModel getHmm()
+  {
+    return hmm;
+  }
+
+  protected SequenceI getHmmSequence()
+  {
+    return hmmSeq;
+  }
+}
diff --git a/src/jalview/schemes/HmmerGlobalBackground.java b/src/jalview/schemes/HmmerGlobalBackground.java
new file mode 100644 (file)
index 0000000..a24b7ab
--- /dev/null
@@ -0,0 +1,73 @@
+package jalview.schemes;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.SequenceCollectionI;
+
+/**
+ * An HMM colour scheme that uses global ('Uniprot') background frequencies for
+ * residues
+ * 
+ * @author tzvanaalten
+ */
+public class HmmerGlobalBackground extends HmmerColourScheme
+{
+  /*
+   * The highest possible log ratio is when match emission probability in
+   * the HMM model is 1, and background (for W) is 0.0109 giving
+   * log(1/0.0109) = log(91.743) = 4.519
+   */
+  private static final float MAX_LOG_RATIO = 4.519f;
+
+  /**
+   * Constructor given a sequence collection
+   * 
+   * @param ac
+   */
+  public HmmerGlobalBackground(SequenceCollectionI ac)
+  {
+    super(ac.getHmmSequences());
+    String alphabetType = getHmm() == null
+            ? ResidueProperties.ALPHABET_AMINO
+            : getHmm().getAlphabetType();
+    setFrequencies(
+            ResidueProperties.backgroundFrequencies.get(alphabetType));
+  }
+
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
+  public HmmerGlobalBackground()
+  {
+  }
+
+  @Override
+  public String getSchemeName()
+  {
+    return JalviewColourScheme.HMMERU.toString();
+  }
+
+  @Override
+  protected HmmerColourScheme newInstance(AnnotatedCollectionI ac)
+  {
+    return new HmmerGlobalBackground(ac);
+  }
+
+  @Override
+  float getMaxInformationScore()
+  {
+    return MAX_LOG_RATIO;
+  }
+
+  /**
+   * Answers a new colour scheme instance based on the HMM of the first sequence
+   * in alignment that has an HMM
+   */
+  @Override
+  public ColourSchemeI getInstance(AlignViewportI viewport,
+          AnnotatedCollectionI ac)
+  {
+    return newInstance(ac);
+  }
+
+}
diff --git a/src/jalview/schemes/HmmerLocalBackground.java b/src/jalview/schemes/HmmerLocalBackground.java
new file mode 100644 (file)
index 0000000..2fe775c
--- /dev/null
@@ -0,0 +1,96 @@
+package jalview.schemes;
+
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.SequenceCollectionI;
+import jalview.datamodel.SequenceI;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An HMM colour scheme that uses local (alignment or sub-group) background
+ * frequencies for residues
+ * 
+ * @author tzvanaalten
+ */
+public class HmmerLocalBackground extends HmmerColourScheme
+{
+  float logTotalCount;
+
+  /**
+   * Constructor given a sequence collection
+   * 
+   * @param ac
+   */
+  public HmmerLocalBackground(AnnotatedCollectionI ac)
+  {
+    super(ac.getHmmSequences());
+    countFrequencies(ac);
+  }
+
+  /**
+   * Default constructor (required by ColourSchemes.loadColourSchemes)
+   */
+  public HmmerLocalBackground()
+  {
+  }
+
+  @Override
+  public String getSchemeName()
+  {
+    return JalviewColourScheme.HMMERA.toString();
+  }
+
+  /**
+   * Counts and stores the relative frequency of every residue in the alignment
+   * (apart from any HMM consensus sequences)
+   * 
+   * @param sc
+   */
+  public void countFrequencies(SequenceCollectionI sc)
+  {
+    // TODO or total counts in Consensus Profile (how do we get at it?)?
+    Map<Character, Float> freqs = new HashMap<>();
+
+    /*
+     * count symbols, excluding any HMM consensus sequences
+     */
+    ResidueCount counts = new ResidueCount();
+    List<SequenceI> seqs = sc.getSequences();
+    for (SequenceI seq : seqs)
+    {
+      if (!seq.hasHMMProfile())
+      {
+        for (char c : seq.getSequence())
+        {
+          counts.add(c);
+        }
+      }
+    }
+    int total = counts.getTotalResidueCount(); // excludes gaps
+
+    for (char symbol : counts.getSymbolCounts().symbols)
+    {
+      double freq = counts.getCount(symbol) / (double) total;
+      freqs.put(symbol, (float) freq);
+    }
+
+    setFrequencies(freqs);
+
+    logTotalCount = (float) Math.log(total);
+  }
+
+  @Override
+  float getMaxInformationScore()
+  {
+    return logTotalCount;
+  }
+
+  @Override
+  protected HmmerColourScheme newInstance(AnnotatedCollectionI ac)
+  {
+    return new HmmerLocalBackground(ac);
+  }
+}
index 965a26b..a09b584 100644 (file)
@@ -47,7 +47,10 @@ public enum JalviewColourScheme
   PurinePyrimidine("Purine/Pyrimidine", PurinePyrimidineColourScheme.class),
   RNAHelices("RNA Helices", RNAHelicesColour.class),
   TCoffee("T-Coffee Scores", TCoffeeColourScheme.class),
-  IdColour("Sequence ID", IdColourScheme.class);
+  IdColour("Sequence ID", IdColourScheme.class),
+  HMMERU("HMMER-Uniprot", HmmerGlobalBackground.class),
+  HMMERA("HMMER-Alignment", HmmerLocalBackground.class),
+  HMMMatchScore("HMM Match Score", HMMMatchScoreColourScheme.class);
   // RNAInteraction("RNA Interaction type", RNAInteractionColourScheme.class)
 
   private String name;
index 7ad35c3..adbb55d 100755 (executable)
@@ -36,6 +36,13 @@ import java.util.Vector;
 
 public class ResidueProperties
 {
+  // alphabet names used in Hidden Markov Model files
+  public static final String ALPHABET_RNA = "RNA";
+
+  public static final String ALPHABET_DNA = "DNA";
+
+  public static final String ALPHABET_AMINO = "amino";
+
   // Stores residue codes/names and colours and other things
   public static final int[] aaIndex; // aaHash version 2.1.1 and below
 
@@ -52,6 +59,9 @@ public class ResidueProperties
   // lookup from modified amino acid (e.g. MSE) to canonical form (e.g. MET)
   public static final Map<String, String> modifications = new HashMap<>();
 
+  // residue background frequencies across different alphabets
+  public static final Map<String, Map<Character, Float>> backgroundFrequencies = new HashMap<>();
+
   static
   {
     aaIndex = new int[255];
@@ -2392,6 +2402,58 @@ public class ResidueProperties
 
   }
 
+  static
+  {
+    Map<Character, Float> amino = new HashMap<>();
+    amino.put('A', 0.0826f);
+    amino.put('Q', 0.0393f);
+    amino.put('L', 0.0965f);
+    amino.put('S', 0.0661f);
+    amino.put('R', 0.0553f);
+    amino.put('E', 0.0674f);
+    amino.put('K', 0.0582f);
+    amino.put('T', 0.0535f);
+    amino.put('N', 0.0406f);
+    amino.put('G', 0.0708f);
+    amino.put('M', 0.0241f);
+    amino.put('W', 0.0109f);
+    amino.put('D', 0.0546f);
+    amino.put('H', 0.0227f);
+    amino.put('F', 0.0386f);
+    amino.put('Y', 0.0292f);
+    amino.put('C', 0.0137f);
+    amino.put('I', 0.0593f);
+    amino.put('P', 0.0472f);
+    amino.put('V', 0.0686f);
+    backgroundFrequencies.put(ALPHABET_AMINO, amino);
+    // todo: these don't match https://www.ebi.ac.uk/uniprot/TrEMBLstats - what
+    // are they?
+  }
+
+  // TODO get correct frequencies
+
+  static
+  {
+    Map<Character, Float> dna = new HashMap<>();
+    dna.put('A', 0.25f);
+    dna.put('C', 0.25f);
+    dna.put('T', 0.25f);
+    dna.put('G', 0.25f);
+    backgroundFrequencies.put(ALPHABET_DNA, dna);
+
+  }
+
+  static
+  {
+    Map<Character, Float> rna = new HashMap<>();
+    rna.put('A', 0.25f);
+    rna.put('C', 0.25f);
+    rna.put('T', 0.25f);
+    rna.put('G', 0.25f);
+    backgroundFrequencies.put(ALPHABET_RNA, rna);
+
+  }
+
   public static String getCanonicalAminoAcid(String aA)
   {
     String canonical = modifications.get(aA);
index cbd59b1..0489e1f 100644 (file)
  */
 package jalview.structure;
 
+
 import java.util.Locale;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
+
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
 
@@ -32,13 +37,23 @@ import jalview.datamodel.PDBEntry.Type;
  * @author tcofoegbu
  *
  */
-public class StructureImportSettings
+public class StructureImportSettings implements ApplicationSingletonI
 {
+  private StructureImportSettings()
+  {
+    // private singleton
+  }
+
+  private static StructureImportSettings getInstance()
+  {
+    return (StructureImportSettings) ApplicationSingletonProvider
+            .getInstance(StructureImportSettings.class);
+  }
   /**
    * set to true to add derived sequence annotations (temp factor read from
    * file, or computed secondary structure) to the alignment
    */
-  private static boolean visibleChainAnnotation = false;
+  private boolean visibleChainAnnotation = false;
 
   /**
    * Set true to predict secondary structure (using JMol for protein, Annotate3D
@@ -50,7 +65,7 @@ public class StructureImportSettings
    * Set true (with predictSecondaryStructure=true) to predict secondary
    * structure using an external service (currently Annotate3D for RNA only)
    */
-  private static boolean externalSecondaryStructure = false;
+  private boolean externalSecondaryStructure = false;
 
   private static boolean showSeqFeatures = true;
 
@@ -63,92 +78,93 @@ public class StructureImportSettings
    * Determines the default file format for structure files to be downloaded
    * from the PDB sequence fetcher. Possible options include: PDB|mmCIF
    */
-  private static PDBEntry.Type defaultStructureFileFormat = Type.PDB;
+  private PDBEntry.Type defaultStructureFileFormat = Type.PDB; // TODO 2.12 should be mmCIF now ?
 
   /**
    * Determines the parser used for parsing PDB format file. Possible options
    * are : JMolParser|JalveiwParser
    */
-  private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
+  private StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
 
   public static void addSettings(boolean addAlignmentAnnotations,
           boolean processSecStr, boolean externalSecStr)
   {
-    StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations;
-    StructureImportSettings.processSecStr = processSecStr;
-    StructureImportSettings.externalSecondaryStructure = externalSecStr;
-    StructureImportSettings.showSeqFeatures = true;
+    StructureImportSettings s = getInstance();
+    s.visibleChainAnnotation = addAlignmentAnnotations;
+    s.processSecStr = processSecStr;
+    s.externalSecondaryStructure = externalSecStr;
+    s.showSeqFeatures = true;
   }
 
   public static boolean isVisibleChainAnnotation()
   {
-    return visibleChainAnnotation;
+    return getInstance().visibleChainAnnotation;
   }
 
   public static void setVisibleChainAnnotation(
           boolean visibleChainAnnotation)
   {
-    StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation;
+    getInstance().visibleChainAnnotation = visibleChainAnnotation;
   }
 
   public static boolean isProcessSecondaryStructure()
   {
-    return processSecStr;
+    return getInstance().processSecStr;
   }
 
   public static void setProcessSecondaryStructure(
           boolean processSecondaryStructure)
   {
-    StructureImportSettings.processSecStr = processSecondaryStructure;
+    getInstance().processSecStr = processSecondaryStructure;
   }
 
   public static boolean isExternalSecondaryStructure()
   {
-    return externalSecondaryStructure;
+    return getInstance().externalSecondaryStructure;
   }
 
   public static void setExternalSecondaryStructure(
           boolean externalSecondaryStructure)
   {
-    StructureImportSettings.externalSecondaryStructure = externalSecondaryStructure;
+    getInstance().externalSecondaryStructure = externalSecondaryStructure;
   }
 
   public static boolean isShowSeqFeatures()
   {
-    return showSeqFeatures;
+    return getInstance().showSeqFeatures;
   }
 
   public static void setShowSeqFeatures(boolean showSeqFeatures)
   {
-    StructureImportSettings.showSeqFeatures = showSeqFeatures;
+    getInstance().showSeqFeatures = showSeqFeatures;
   }
 
   public static PDBEntry.Type getDefaultStructureFileFormat()
   {
-    return defaultStructureFileFormat;
+    return getInstance().defaultStructureFileFormat;
   }
 
   public static void setDefaultStructureFileFormat(
           String defaultStructureFileFormat)
   {
-    StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type
+    getInstance().defaultStructureFileFormat = PDBEntry.Type
             .valueOf(defaultStructureFileFormat.toUpperCase(Locale.ROOT));
   }
 
   public static String getDefaultPDBFileParser()
   {
-    return defaultPDBFileParser.toString();
+    return getInstance().defaultPDBFileParser.toString();
   }
 
   public static void setDefaultPDBFileParser(
           StructureParser defaultPDBFileParser)
   {
-    StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser;
+    getInstance().defaultPDBFileParser = defaultPDBFileParser;
   }
 
   public static void setDefaultPDBFileParser(String defaultPDBFileParser)
   {
-    StructureImportSettings.defaultPDBFileParser = StructureParser
+    getInstance().defaultPDBFileParser = StructureParser
             .valueOf(defaultPDBFileParser.toUpperCase(Locale.ROOT));
   }
 
index c8a846c..ae5431b 100644 (file)
  */
 package jalview.structure;
 
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Vector;
 
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Console;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
@@ -58,15 +49,27 @@ import jalview.util.Platform;
 import jalview.ws.sifts.SiftsClient;
 import jalview.ws.sifts.SiftsException;
 import jalview.ws.sifts.SiftsSettings;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
 import mc_view.Atom;
 import mc_view.PDBChain;
 import mc_view.PDBfile;
 
-public class StructureSelectionManager
+public class StructureSelectionManager implements ApplicationSingletonI
 {
   public final static String NEWLINE = System.lineSeparator();
 
-  static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
 
   private List<StructureMapping> mappings = new ArrayList<>();
 
@@ -84,6 +87,111 @@ public class StructureSelectionManager
   private List<CommandListener> commandListeners = new ArrayList<>();
 
   private List<SelectionListener> sel_listeners = new ArrayList<>();
+  /*
+   * instances of this class scoped by some context class
+   */
+  private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
+
+  /**
+   * Answers an instance of this class for the current application (Java or JS
+   * 'applet') scope
+   * 
+   * @return
+   */
+  private static StructureSelectionManager getInstance()
+  {
+    return (StructureSelectionManager) ApplicationSingletonProvider
+            .getInstance(StructureSelectionManager.class);
+  }
+
+  /**
+   * Private constructor as all 'singleton' instances are managed here or by
+   * ApplicationSingletonProvider
+   */
+  private StructureSelectionManager()
+  {
+    selectionManagers = new IdentityHashMap<>();
+  }
+
+  /**
+   * Answers an instance of this class for the current application (Java or JS
+   * 'applet') scope, and scoped to the specified context
+   * 
+   * @param context
+   * @return
+   */
+  public static StructureSelectionManager getStructureSelectionManager(
+          StructureSelectionManagerProvider context)
+  {
+    return getInstance().getInstanceForContext(context);
+  }
+
+  /**
+   * Answers an instance of this class scoped to the given context. The instance
+   * is created on the first request for the context, thereafter the same
+   * instance is returned. Note that the context may be null (this is the case
+   * when running headless without a Desktop).
+   * 
+   * @param context
+   * @return
+   */
+  StructureSelectionManager getInstanceForContext(
+          StructureSelectionManagerProvider context)
+  {
+    StructureSelectionManager instance = selectionManagers.get(context);
+    if (instance == null)
+    {
+      instance = new StructureSelectionManager();
+      selectionManagers.put(context, instance);
+    }
+    return instance;
+  }
+/** Null provider in 2.11.2
+
+
+  private static StructureSelectionManager nullProvider = null;
+
+  public static StructureSelectionManager getStructureSelectionManager(
+          StructureSelectionManagerProvider context)
+  {
+    if (context == null)
+    {
+      if (nullProvider == null)
+      {
+        if (instances != null)
+        {
+          throw new Error(MessageManager.getString(
+                  "error.implementation_error_structure_selection_manager_null"),
+                  new NullPointerException(MessageManager
+                          .getString("exception.ssm_context_is_null")));
+        }
+        else
+        {
+          nullProvider = new StructureSelectionManager();
+        }
+        return nullProvider;
+      }
+    }
+    if (instances == null)
+    {
+      instances = new java.util.IdentityHashMap<>();
+    }
+    StructureSelectionManager instance = instances.get(context);
+    if (instance == null)
+    {
+      if (nullProvider != null)
+      {
+        instance = nullProvider;
+      }
+      else
+      {
+        instance = new StructureSelectionManager();
+      }
+      instances.put(context, instance);
+    }
+    return instance;
+  }
+*/
 
   /**
    * @return true if will try to use external services for processing secondary
@@ -199,49 +307,6 @@ public class StructureSelectionManager
             || pdbIdFileName.containsKey(idOrFile);
   }
 
-  private static StructureSelectionManager nullProvider = null;
-
-  public static StructureSelectionManager getStructureSelectionManager(
-          StructureSelectionManagerProvider context)
-  {
-    if (context == null)
-    {
-      if (nullProvider == null)
-      {
-        if (instances != null)
-        {
-          throw new Error(MessageManager.getString(
-                  "error.implementation_error_structure_selection_manager_null"),
-                  new NullPointerException(MessageManager
-                          .getString("exception.ssm_context_is_null")));
-        }
-        else
-        {
-          nullProvider = new StructureSelectionManager();
-        }
-        return nullProvider;
-      }
-    }
-    if (instances == null)
-    {
-      instances = new java.util.IdentityHashMap<>();
-    }
-    StructureSelectionManager instance = instances.get(context);
-    if (instance == null)
-    {
-      if (nullProvider != null)
-      {
-        instance = nullProvider;
-      }
-      else
-      {
-        instance = new StructureSelectionManager();
-      }
-      instances.put(context, instance);
-    }
-    return instance;
-  }
-
   /**
    * flag controlling whether SeqMappings are relayed from received sequence
    * mouse over events to other sequences
@@ -271,7 +336,7 @@ public class StructureSelectionManager
     return relaySeqMappings;
   }
 
-  Vector listeners = new Vector();
+  Vector<Object> listeners = new Vector<>();
 
   /**
    * register a listener for alignment sequence mouseover events
@@ -309,6 +374,8 @@ public class StructureSelectionManager
    * Import structure data and register a structure mapping for broadcasting
    * colouring, mouseovers and selection events (convenience wrapper).
    * 
+   * This is the standard entry point.
+   * 
    * @param sequence
    *          - one or more sequences to be mapped to pdbFile
    * @param targetChains
@@ -321,7 +388,7 @@ public class StructureSelectionManager
    * @return null or the structure data parsed as a pdb file
    */
   synchronized public StructureFile setMapping(SequenceI[] sequence,
-          String[] targetChains, String pdbFile, DataSourceType protocol,
+          String[] targetChains, String pdbFile, DataSourceType protocol, 
           IProgressIndicator progress)
   {
     return computeMapping(true, sequence, targetChains, pdbFile, protocol,
@@ -333,8 +400,11 @@ public class StructureSelectionManager
    * broadcasting colouring, mouseovers and selection events (convenience
    * wrapper).
    * 
+   * 
+   * 
    * @param forStructureView
-   *          when true, record the mapping for use in mouseOvers
+   *          when true (testng only), record the mapping for use in mouseOvers
+   *          (testng only)
    * @param sequence
    *          - one or more sequences to be mapped to pdbFile
    * @param targetChains
@@ -378,9 +448,9 @@ public class StructureSelectionManager
    *          mapping operation
    * @return null or the structure data parsed as a pdb file
    */
-  synchronized public StructureFile computeMapping(boolean forStructureView,
-          SequenceI[] sequenceArray, String[] targetChainIds,
-          String pdbFile, DataSourceType sourceType,
+  synchronized private StructureFile computeMapping(
+          boolean forStructureView, SequenceI[] sequenceArray,
+          String[] targetChainIds, String pdbFile, DataSourceType sourceType,
           IProgressIndicator progress)
   {
     long progressSessionId = System.currentTimeMillis() * 3;
@@ -411,20 +481,17 @@ public class StructureSelectionManager
         registerPDBFile(pdb.getId().trim(), pdbFile);
       }
       // if PDBId is unavailable then skip SIFTS mapping execution path
-      // TODO: JAL-3868 need to know if structure is actually from
-      // PDB (has valid PDB ID and has provenance suggesting it
+      // 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)
       boolean isProtein = false;
-      for (SequenceI s : sequenceArray)
-      {
-        if (s.isProtein())
-        {
+      for (SequenceI s:sequenceArray) {
+        if (s.isProtein()) {
           isProtein = true;
           break;
         }
       }
-      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
-              && !pdb.getId().startsWith("AF-") && isProtein;
+      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-") && isProtein;
 
     } catch (Exception ex)
     {
@@ -536,11 +603,9 @@ public class StructureSelectionManager
       List<StructureMapping> seqToStrucMapping = new ArrayList<>();
       if (isMapUsingSIFTs && seq.isProtein())
       {
-        if (progress != null)
-        {
-          progress.setProgressBar(
-                  MessageManager
-                          .getString("status.obtaining_mapping_with_sifts"),
+        if (progress!=null) {
+          progress.setProgressBar(MessageManager
+                .getString("status.obtaining_mapping_with_sifts"),
                   progressSessionId);
         }
         jalview.datamodel.Mapping sqmpping = maxAlignseq
@@ -583,8 +648,9 @@ public class StructureSelectionManager
             StructureMapping siftsMapping = null;
             try
             {
-              siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
-                      pdb, chain, sqmpping, maxAlignseq, siftsClient);
+              siftsMapping = getStructureMapping(seq,
+                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
+                      siftsClient);
               foundSiftsMappings.add(siftsMapping);
               chain.makeExactMapping(siftsMapping, seq);
               chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
@@ -594,10 +660,12 @@ public class StructureSelectionManager
             } catch (SiftsException e)
             {
               System.err.println(e.getMessage());
-            } catch (Exception e)
+            }
+            catch (Exception e)
             {
-              System.err.println(
-                      "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
+              System.err
+                      .println(
+                              "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
               System.err.println(e.getMessage());
             }
           }
@@ -623,9 +691,8 @@ public class StructureSelectionManager
       {
         if (progress != null)
         {
-          progress.setProgressBar(
-                  MessageManager.getString(
-                          "status.obtaining_mapping_with_nw_alignment"),
+          progress.setProgressBar(MessageManager
+                                 .getString("status.obtaining_mapping_with_nw_alignment"),
                   progressSessionId);
         }
         StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
@@ -715,8 +782,7 @@ public class StructureSelectionManager
   private StructureMapping getStructureMapping(SequenceI seq,
           String pdbFile, String targetChainId, StructureFile pdb,
           PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
-          AlignSeq maxAlignseq, SiftsClient siftsClient)
-          throws SiftsException
+          AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
   {
     StructureMapping curChainMapping = siftsClient
             .getSiftsStructureMapping(seq, pdbFile, targetChainId);
@@ -1261,8 +1327,11 @@ public class StructureSelectionManager
   }
 
   /**
-   * Resets this object to its initial state by removing all registered
-   * listeners, codon mappings, PDB file mappings
+   * Reset this object to its initial state by removing all registered
+   * listeners, codon mappings, PDB file mappings.
+   * 
+   * Called only by Desktop and testng.
+   * 
    */
   public void resetAll()
   {
@@ -1300,7 +1369,11 @@ public class StructureSelectionManager
     }
   }
 
-  public void addSelectionListener(SelectionListener selecter)
+  public List<SelectionListener> getListeners() {
+    return sel_listeners;
+  }
+  
+   public void addSelectionListener(SelectionListener selecter)
   {
     if (!sel_listeners.contains(selecter))
     {
@@ -1353,34 +1426,15 @@ public class StructureSelectionManager
     }
   }
 
+  
   /**
-   * release all references associated with this manager provider
+   * Removes the instance associated with this provider
    * 
-   * @param jalviewLite
+   * @param provider
    */
-  public static void release(StructureSelectionManagerProvider jalviewLite)
+  public static void release(StructureSelectionManagerProvider provider)
   {
-    // synchronized (instances)
-    {
-      if (instances == null)
-      {
-        return;
-      }
-      StructureSelectionManager mnger = (instances.get(jalviewLite));
-      if (mnger != null)
-      {
-        instances.remove(jalviewLite);
-        try
-        {
-          /* bsoares 2019-03-20 finalize deprecated, no apparent external
-           * resources to close
-           */
-          // mnger.finalize();
-        } catch (Throwable x)
-        {
-        }
-      }
-    }
+    getInstance().selectionManagers.remove(provider);
   }
 
   public void registerPDBEntry(PDBEntry pdbentry)
index 7dd1a19..7c38ab6 100644 (file)
 
 package jalview.urls;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * Holds settings for identifiers.org e.g. url, download location
- * 
- * @author $author$
- * @version $Revision$
  */
-public class IdOrgSettings
+public class IdOrgSettings implements ApplicationSingletonI
 {
-  private static String url;
+  private String url;
+
+  private String location;
+
+  private static IdOrgSettings getInstance()
+  {
+    return (IdOrgSettings) ApplicationSingletonProvider
+            .getInstance(IdOrgSettings.class);
+  }
 
-  private static String location;
+  private IdOrgSettings()
+  {
+    // private singleton
+  }
 
   public static void setUrl(String seturl)
   {
-    url = seturl;
+    getInstance().url = seturl;
   }
 
   public static void setDownloadLocation(String setloc)
   {
-    location = setloc;
+    getInstance().location = setloc;
   }
 
   public static String getUrl()
   {
-    return url;
+    return getInstance().url;
   }
 
   public static String getDownloadLocation()
   {
-    return location;
+    return getInstance().location;
   }
 }
index 850a230..24d2406 100644 (file)
@@ -84,7 +84,6 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
     try
     {
       // NOTE: THIS WILL FAIL IN SWINGJS BECAUSE IT INVOLVES A FILE READER
-
       FileReader reader = new FileReader(idFileName);
       String key = "";
       Map<String, Object> obj = (Map<String, Object>) JSONUtils
@@ -119,13 +118,6 @@ public class IdentifiersUrlProvider extends UrlProviderImpl
       }
     } catch (IOException | ParseException e)
     {
-      // unnecessary e.printStackTrace();
-      // Note how in JavaScript we can grab the first bytes from any file
-      // reader.
-      // Typical report here is "NetworkError" because the file does not exist.
-      // "https://." is coming from System.getProperty("user.home"), but this
-      // could
-      // be set by the page developer to anything, of course.
       errorMessage = e.toString();
       idData.clear();
     }
index f9fa80d..3e4f72b 100644 (file)
@@ -50,7 +50,12 @@ public class BrowserLauncher
   {
     if (Platform.isJS())
     {
-      Platform.openURL(url);
+       try {
+             Platform.openURL(url);
+       } catch (Throwable t) {
+           System.err.println("Couldn't open "+url);
+           System.err.print(t.getStackTrace());
+       }
       return;
     }
     else
@@ -117,4 +122,4 @@ public class BrowserLauncher
     return "jalview.default.browser";
   }
 
-}
\ No newline at end of file
+}
index 6734735..008c8b0 100644 (file)
@@ -222,12 +222,15 @@ public class ColorUtils
     colour = colour.trim();
 
     Color col = null;
-    try
-    {
-      int value = Integer.parseInt(colour, 16);
-      col = new Color(value);
-    } catch (NumberFormatException ex)
+    if (StringUtils.isHexString(colour))
     {
+      try
+      {
+        int value = Integer.parseInt(colour, 16);
+        col = new Color(value);
+      } catch (NumberFormatException ex)
+      {
+      }
     }
 
     if (col == null)
index cd98ee7..fabfbff 100644 (file)
@@ -256,7 +256,7 @@ public class Comparison
    */
   public static final boolean isGap(char c)
   {
-    return (c == GAP_DASH || c == GAP_DOT || c == GAP_SPACE) ? true : false;
+    return (c == GAP_DASH || c == GAP_DOT || c == GAP_SPACE);
   }
 
   /**
index 5337852..fbf0177 100755 (executable)
@@ -353,6 +353,15 @@ public class DBRefUtils
 
   };
 
+  private static Regex PARSE_REGEX;
+
+  private static Regex getParseRegex()
+  {
+    return (PARSE_REGEX == null ? PARSE_REGEX = Platform.newRegex(
+            "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)")
+            : PARSE_REGEX);
+  }
+
   /**
    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
    * database is PDB.
@@ -380,8 +389,7 @@ public class DBRefUtils
          * Check for PFAM style stockhom PDB accession id citation e.g.
          * "1WRI A; 7-80;"
          */
-        Regex r = new com.stevesoft.pat.Regex(
-                "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
+        Regex r = getParseRegex();
         if (r.search(acn.trim()))
         {
           String pdbid = r.stringMatched(1);
diff --git a/src/jalview/util/FileUtils.java b/src/jalview/util/FileUtils.java
new file mode 100644 (file)
index 0000000..7e607ab
--- /dev/null
@@ -0,0 +1,208 @@
+package jalview.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Miscellaneous file-related functions
+ */
+public final class FileUtils
+{
+
+  /**
+   * Answers the executable file for the given command, or null if not found or
+   * not executable. The path to the executable is the command name prefixed by
+   * the given folder path, optionally with .exe appended.
+   * 
+   * @param cmd
+   *          command short name, for example hmmbuild
+   * @param binaryPath
+   *          parent folder for the executable
+   * @return
+   */
+  public static File getExecutable(String cmd, String binaryPath)
+  {
+    File file = new File(binaryPath, cmd);
+    if (!file.canExecute())
+    {
+      file = new File(binaryPath, cmd + ".exe");
+      {
+        if (!file.canExecute())
+        {
+          file = null;
+        }
+      }
+    }
+    return file;
+  }
+
+  /**
+   * Answers the path to the folder containing the given executable file, by
+   * searching the PATH environment variable. Answers null if no such executable
+   * can be found.
+   * 
+   * @param cmd
+   * @return
+   */
+  public static String getPathTo(String cmd)
+  {
+    String paths = System.getenv("PATH");
+    // backslash is to escape regular expression argument
+    for (String path : paths.split("\\" + File.pathSeparator))
+    {
+      if (getExecutable(cmd, path) != null)
+      {
+        return path;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * A convenience method to create a temporary file that is deleted on exit of
+   * the JVM
+   * 
+   * @param prefix
+   * @param suffix
+   * @return
+   * @throws IOException
+   */
+  public static File createTempFile(String prefix, String suffix)
+          throws IOException
+  {
+    File f = File.createTempFile(prefix, suffix);
+    f.deleteOnExit();
+    return f;
+  }
+
+  /**
+   * Answers a (possibly empty) list of file paths found by searching below
+   * <code>from</code> that match the supplied regular expression
+   * <code>pattern</code>. Results may include <code>from</code> itself if it
+   * matches. If an exception occurs it is written to syserr and any results up to
+   * that point are returned. Note that the regular expression match applies to
+   * the whole path of any matched file.
+   * <p>
+   * WARNING: because the whole directory tree below <code>from</code> is
+   * searched, this method may be slow if used for a high level directory, or may
+   * exit prematurely if security or other exceptions occur.
+   * 
+   * <pre>
+   * Example: 
+   *   findMatchingPaths(Paths.get("C:/Program Files"), ".*&#47chimera.exe$")
+   * </pre>
+   * 
+   * @param from
+   * @param pattern
+   * 
+   * @return
+   * @see https://stackoverflow.com/questions/794381/how-to-find-files-that-match-a-wildcard-string-in-java/31685610#comment62441832_31685610
+   */
+  public static List<String> findMatchingPaths(Path from, String pattern)
+  {
+    List<String> matches = new ArrayList<>();
+    PathMatcher pathMatcher = FileSystems.getDefault()
+            .getPathMatcher("regex:" + pattern);
+    try
+    {
+      Files.walk(from).filter(pathMatcher::matches)
+              .forEach(m -> matches.add(m.toString()));
+    } catch (IOException e)
+    {
+      System.err.println(
+              "Error searching for " + pattern + " : " + e.toString());
+    }
+
+    return matches;
+  }
+
+  /**
+   * Answers a (possibly empty) list of paths to files below the given root path,
+   * that match the given pattern. The pattern should be a '/' delimited set of
+   * glob patterns, each of which is used to match child file names (not full
+   * paths). Note that 'directory spanning' glob patterns (**) are <em>not</em>
+   * supported by this method.
+   * <p>
+   * For example
+   * 
+   * <pre>
+   *   findMatches("C:\\", "Program Files*&#47Chimera*&#47bin/{chimera,chimera.exe}"
+   * </pre>
+   * 
+   * would match "C:\Program Files\Chimera 1.11\bin\chimera.exe" and "C:\Program
+   * Files (x86)\Chimera 1.10.1\bin\chimera"
+   * 
+   * @param root
+   * @param pattern
+   * @return
+   * @see https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob
+   */
+  public static List<String> findMatches(String root, String pattern)
+  {
+    List<String> results = new ArrayList<>();
+    try
+    {
+      Path from = Paths.get(root);
+      findMatches(results, from, Arrays.asList(pattern.split("/")));
+    } catch (Throwable e)
+    {
+      // Paths.get can throw IllegalArgumentException
+      System.err.println(String.format("Error searching %s for %s: %s",
+              root, pattern, e.toString()));
+    }
+
+    return results;
+  }
+
+  /**
+   * A helper method that performs recursive search of file patterns and adds any
+   * 'leaf node' matches to the results list
+   * 
+   * @param results
+   * @param from
+   * @param patterns
+   */
+  protected static void findMatches(List<String> results, Path from,
+          List<String> patterns)
+  {
+    if (patterns.isEmpty())
+    {
+      /*
+       * reached end of recursion with all components matched
+       */
+      results.add(from.toString());
+      return;
+    }
+
+    String pattern = patterns.get(0);
+    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(from,
+            pattern))
+    {
+      dirStream.forEach(p -> {
+
+        /*
+         * matched a next level file - search below it
+         * (ignore non-directory non-leaf matches)
+         */
+        List<String> subList = patterns.subList(1, patterns.size());
+        if (subList.isEmpty() || p.toFile().isDirectory())
+        {
+          findMatches(results, p, subList);
+        }
+      });
+    } catch (IOException e)
+    {
+      System.err.println(String.format("Error searching %s: %s", pattern,
+              e.toString()));
+    }
+  }
+}
diff --git a/src/jalview/util/HMMProbabilityDistributionAnalyser.java b/src/jalview/util/HMMProbabilityDistributionAnalyser.java
new file mode 100644 (file)
index 0000000..66ae552
--- /dev/null
@@ -0,0 +1,978 @@
+package jalview.util;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.HMMFile;
+import jalview.io.StockholmFile;
+import jalview.schemes.ResidueProperties;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.Vector;
+
+/**
+ * Processes probability data. The file indexes used in this program represent
+ * the index of the location of a family or hmm in their respective files,
+ * starting from 0.
+ * 
+ * @author TZVanaalten
+ *
+ */
+public class HMMProbabilityDistributionAnalyser
+{
+  AlignmentAnnotation reference = null;
+
+  Vector<SequenceI> sequences;
+
+  HiddenMarkovModel hmm;
+
+  // contains the raw data produced
+  List<ArrayList<Double>> raw = new ArrayList<>();
+
+  // contains binned data
+  Map<String, Double> binned = new HashMap<>();
+
+  // location of the family file
+  String families = "/media/sf_Shared_Folder/PFAM/Family/SeedFamilies.seed";
+
+  // location of the file containing the family-clan links
+  final static String FAMILIESTOCLAN = "/media/sf_Shared_Folder/PFAM/Family/Clanlinks.dat";
+
+  // location of the HMM file
+  String hmms = "/media/sf_Shared_Folder/PFAM/HMMs/Pfam-A.hmm";
+
+  // suffix for raw file
+  final static String RAW = "/Raw.csv";
+
+  // suffix for binned file
+  final static String BINNED = "/Binned.csv";
+
+  // normalisation scale
+  final static double SCALE = 1;
+
+  // current position in file
+  int currentFilePosition = 0;
+
+  final static String NL = "\n";
+
+  Random generator = new Random();
+
+  // current directory
+  String currentFolder;
+
+  boolean keepRaw = false;
+
+  /**
+   * Sets the working directory.
+   * 
+   * @param path
+   */
+  public void setFolder(String path)
+  {
+    currentFolder = path;
+  }
+
+  /**
+   * Moves a buffered reader forward in the file by a certain amount of entries.
+   * Each entry in the file is delimited by '//'.
+   * 
+   * @param index
+   *          The index of the location in the file.
+   * @param br
+   * @throws IOException
+   */
+  public void moveLocationBy(int index, BufferedReader br)
+          throws IOException
+  {
+    for (int i = 0; i < index; i++)
+    {
+      String line = br.readLine();
+      while (!"//".equals(line))
+      {
+        line = br.readLine();
+
+      }
+    }
+
+  }
+  
+  /**
+   * Analyses a specified number of families and then saves the data. Before
+   * analysing the data, the previous saved data will be imported and after
+   * analysing this, the data is exported back into the file. The file must be
+   * in flat file format.
+   * 
+   * @param increments
+   *          The number of families to read before saving.
+   * @throws IOException
+   */
+  public void run(int increments, boolean keepRawData) throws IOException
+  {
+    keepRaw = keepRawData;
+    try
+    {
+      readPreviousData(currentFolder);
+      BufferedReader posReader = new BufferedReader(
+              new FileReader(currentFolder + "/CurrentPosition.txt"));
+
+      String line = posReader.readLine();
+      posReader.close();
+      currentFilePosition = Integer.parseInt(line);
+    } catch (Exception e)
+    {
+      System.out.println("No previous data found");
+    }
+
+
+
+    BufferedReader inputSTO = new BufferedReader(new FileReader(families));
+    BufferedReader inputHMM = new BufferedReader(new FileReader(hmms));
+
+
+
+    moveLocationBy(currentFilePosition, inputHMM);
+    moveLocationBy(currentFilePosition, inputSTO);
+
+    int filesRead = 0;
+    int i = 0;
+    while (filesRead < increments)
+    {
+
+      readStockholm(inputSTO);
+
+      readHMM(inputHMM);
+
+        int count = countValidResidues();
+        processData(count);
+        filesRead++;
+
+      currentFilePosition++;
+      System.out.println(i);
+      i++;
+    }
+
+    PrintWriter p = new PrintWriter(
+            new File(currentFolder + "/CurrentPosition.txt"));
+    p.print(currentFilePosition);
+    p.close();
+    exportData(currentFolder);
+    raw.clear();
+    binned.clear();
+
+  }
+
+  /**
+   * Analyses all families and then saves the data. Before analysing the data,
+   * the previous saved data will be imported and after analysing this, the data
+   * is exported back into the file. The file must be in flat file format.
+   * 
+   * @param increments
+   *          The number of families to read before saving.
+   * @throws IOException
+   */
+  public void runToEnd(int minCount, int maxCount, boolean keepRawData,
+          boolean forClans)
+          throws IOException
+  {
+    keepRaw = keepRawData;
+    BufferedReader inputSTO = null;
+    BufferedReader inputHMM = null;
+    int size = 0;
+    int files = 1;
+    try
+    {
+    if (forClans)
+    {
+        files = 603;
+    }
+    int filesRead = 0;
+    for (int clan = 0; clan < files; clan++)
+    {
+      System.out.println(clan);
+      String clanPath = "";
+      int numberOfFamilies = 0;
+      if (forClans)
+      {
+        clanPath = currentFolder + "/Clan" + clan;
+          if (!new File(clanPath).exists())
+          {
+            continue;
+          }
+        BufferedReader famCountReader = new BufferedReader(
+                new FileReader(clanPath + "/NumberOfFamilies.txt"));
+        numberOfFamilies = Integer.parseInt(famCountReader.readLine());
+      }
+      else
+      {
+        numberOfFamilies = 1;
+      }
+      
+      for (int fam = 0; fam < numberOfFamilies; fam++)
+      {
+        if (forClans)
+        {
+          families = clanPath + "/Families/Fam" + fam + ".sto";
+          hmms = clanPath + "/HMMs/HMM" + fam + ".hmm";
+        }
+
+        inputSTO = new BufferedReader(new FileReader(families));
+        inputHMM = new BufferedReader(new FileReader(hmms));
+
+
+        int i = 0;
+        boolean endReached = atEnd(inputSTO);
+        while (!endReached)
+        {
+          readStockholm(inputSTO);
+          readHMM(inputHMM);
+
+        int count = countValidResidues();
+            if (count >= minCount && count < maxCount)
+            {
+              processData(count);
+            }
+        filesRead++;
+          System.out.println(filesRead);
+      endReached = atEnd(inputSTO);
+      }
+      }
+    }
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    } finally
+    {
+      exportData(currentFolder);
+      raw.clear();
+      binned.clear();
+    }
+  }
+
+  /**
+   * Reads the previous data from both files
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public void readPreviousData(String source) throws IOException
+  {
+    readBinned(source);
+    if (keepRaw)
+    {
+      readRaw(source);
+    }
+  }
+
+  /**
+   * Reads the previous data from the binned file.
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public void readBinned(String source) throws IOException
+  {
+    BufferedReader input = new BufferedReader(
+            new FileReader(source + BINNED));
+    String line = input.readLine();
+    binned = new HashMap<>();
+    while (!("".equals(line) || line == null))
+    {
+      Scanner scanner = new Scanner(line);
+      scanner.useDelimiter(",");
+      String key = scanner.next();
+      String value = scanner.next();
+      binned.put(key, Double.valueOf(value));
+      scanner.close();
+      line = input.readLine();
+    }
+
+    input.close();
+  }
+
+  /**
+   * Reads the previous data from the raw file.
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public void readRaw(String source) throws IOException
+  {
+    BufferedReader input = new BufferedReader(new FileReader(source + RAW));
+    String line = input.readLine();
+    if (line == null)
+    {
+      input.close();
+      return;
+    }
+    Scanner numberScanner = new Scanner(line);
+    numberScanner.useDelimiter(",");
+    raw = new ArrayList<>();
+    while (numberScanner.hasNext())
+    {
+      numberScanner.next();
+      raw.add(new ArrayList<Double>());
+    }
+    numberScanner.close();
+
+    line = input.readLine();
+    while (!("".equals(line) || line == null))
+    {
+      Scanner scanner = new Scanner(line);
+      scanner.useDelimiter(",");
+
+      int i = 0;
+      while (scanner.hasNext())
+      {
+        String value;
+        value = scanner.next();
+        if (!value.equals("EMPTY"))
+        {
+          raw.get(i).add(Double.parseDouble(value));
+        }
+        else
+        {
+          raw.get(i).add(null);
+        }
+
+        i++;
+      }
+      scanner.close();
+      line = input.readLine();
+    }
+
+    input.close();
+  }
+
+  /**
+   * Counts the number of valid residues in the sequence.
+   * 
+   * @return
+   */
+  public int countValidResidues()
+  {
+    int count = 0;
+
+    for (int width = 0; width < sequences.size(); width++)
+    {
+      for (int length = 1; length < hmm.getLength() + 1; length++)
+      {
+        char symbol;
+        int alignPos;
+        alignPos = hmm.getNodeMapPosition(length);
+
+        symbol = sequences.get(width).getCharAt(alignPos);
+        if (ResidueProperties.backgroundFrequencies.get("amino")
+                .containsKey(symbol))
+        {
+          count++;
+        }
+      }
+    }
+
+    return count;
+  }
+
+  /**
+   * Processes data, and stores it in both a raw and binned format.
+   * 
+   * @param count
+   */
+  public void processData(int count)
+  {
+    int rawPos = 0;
+    if (keepRaw)
+    {
+      raw.add(new ArrayList<Double>());
+      rawPos = raw.size() - 1;
+    }
+    Double total = 0d;
+    for (int width = 0; width < sequences.size(); width++)
+    {
+      for (int length = 1; length < hmm.getLength() + 1; length++)
+      {
+        char symbol;
+        int alignPos;
+        alignPos = hmm.getNodeMapPosition(length);
+        
+        symbol = sequences.get(width).getCharAt(alignPos);
+        if (ResidueProperties.backgroundFrequencies.get("amino")
+                .containsKey(symbol))
+        {
+          Double prob;
+          Float bfreq;
+          Double llr;
+          prob = hmm.getMatchEmissionProbability(alignPos, symbol);
+          bfreq = ResidueProperties.backgroundFrequencies.get("amino")
+                  .get(symbol);
+          if (prob == 0 || bfreq == 0)
+          {
+            System.out.println("error");
+          }
+          llr = Math.log(prob / bfreq);
+          if (keepRaw)
+          {
+            raw.get(rawPos).add(llr);
+          }
+
+          String output;
+          output = String.format("%.1f", llr);
+          total += Double.parseDouble(output);
+          if ("-0.0".equals(output))
+          {
+            output = "0.0";
+          }
+          if (binned.containsKey(output))
+          {
+            double prev = binned.get(output);
+            prev += (SCALE / count);
+            binned.put(output, prev);
+
+          }
+          else
+          {
+            binned.put(output, SCALE / count);
+          }
+        }
+      }
+    }
+    System.out.println(total / count);
+  }
+
+
+  /**
+   * Reads in the sequence data from a Stockholm file.
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public void readStockholm(BufferedReader inputSTO) throws IOException
+  {
+    FileParse parserSTO = new FileParse(inputSTO, "", DataSourceType.FILE);
+    StockholmFile file = new StockholmFile(parserSTO);
+    Vector<AlignmentAnnotation> annots = file.getAnnotations();
+
+    for (AlignmentAnnotation annot : annots)
+    {
+      if (annot.label.contains("Reference"))
+      {
+        reference = annot;
+      }
+    }
+    sequences = file.getSeqs();
+  }
+
+  /**
+   * Reads in the HMM data from a HMMer file.
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public void readHMM(BufferedReader inputHMM) throws IOException
+  {
+    FileParse parserHMM = new FileParse(inputHMM, "", DataSourceType.FILE);
+    HMMFile file = new HMMFile(parserHMM);
+    hmm = file.getHMM();
+
+
+  }
+
+  /**
+   * Exports both the binned and raw data into separate files.
+   * 
+   * @param location
+   * @throws FileNotFoundException
+   */
+  public void exportData(String location) throws FileNotFoundException
+  {
+    PrintWriter writerBin = new PrintWriter(new File(location + BINNED));
+    for (Map.Entry<String, Double> entry : binned.entrySet())
+    {
+      writerBin.println(entry.getKey() + "," + entry.getValue());
+    }
+    writerBin.close();
+    if (keepRaw)
+    {
+
+    PrintWriter writerRaw = new PrintWriter(new File(location + RAW));
+    
+    StringBuilder identifier = new StringBuilder();
+    
+    for (int i = 1; i < raw.size() + 1; i++)
+    {
+      identifier.append("Fam " + i + ",");
+    }
+    
+    writerRaw.println(identifier);
+    
+    boolean rowIsEmpty = false;
+    int row = 0;
+    while (!rowIsEmpty)
+    {
+      rowIsEmpty = true;
+      StringBuilder string = new StringBuilder();
+      for (int column = 0; column < raw.size(); column++)
+      {
+        if (raw.get(column).size() <= row)
+        {
+          string.append("EMPTY,");
+        }
+        else
+        {
+          string.append(raw.get(column).get(row) + ",");
+          rowIsEmpty = false;
+        }
+      }
+      row++;
+      writerRaw.println(string);
+    }
+    writerRaw.close();
+
+    }
+
+  }
+
+  /**
+   * Prints the specified family on the console.
+   * 
+   * @param index
+   * @throws IOException
+   */
+  public void printFam(int index) throws IOException
+  {
+    BufferedReader br = new BufferedReader(new FileReader(families));
+
+    moveLocationBy(index, br);
+
+    String line = br.readLine();
+
+    while (!"//".equals(line))
+    {
+      System.out.println(line);
+      line = br.readLine();
+    }
+    System.out.println(line);
+    br.close();
+
+  }
+
+  /**
+   * Prints the specified HMM on the console.
+   * 
+   * @param index
+   * @throws IOException
+   */
+  public void printHMM(int index) throws IOException
+  {
+    BufferedReader br = new BufferedReader(new FileReader(hmms));
+
+    moveLocationBy(index, br);
+
+    String line = br.readLine();
+
+    while (!"//".equals(line))
+    {
+      System.out.println(line);
+      line = br.readLine();
+    }
+    System.out.println(line);
+    br.close();
+
+  }
+
+  /**
+   * Prints the specified family to a .sto file.
+   * 
+   * @param index
+   * @throws IOException
+   */
+  public void exportFam(int index, String location) throws IOException
+  {
+    BufferedReader br = new BufferedReader(new FileReader(families));
+
+    moveLocationBy(index, br);
+
+    String line = br.readLine();
+    PrintWriter writer = new PrintWriter(
+            new FileOutputStream(new File(location), true));
+    while (!"//".equals(line))
+    {
+      writer.println(line);
+      line = br.readLine();
+    }
+    writer.println(line);
+    writer.close();
+    br.close();
+
+  }
+
+  public void exportFile(BufferedReader br, String location, boolean append)
+          throws IOException
+  {
+    String line = br.readLine();
+    PrintWriter writer = new PrintWriter(
+            new FileOutputStream(location, append));
+    while (!"//".equals(line))
+    {
+      writer.println(line);
+      line = br.readLine();
+    }
+    writer.println(line);
+    writer.close();
+
+
+  }
+
+  public String getHMMName(int index) throws IOException
+  {
+    String name;
+
+    BufferedReader nameFinder = new BufferedReader(new FileReader(hmms));
+
+    moveLocationBy(index, nameFinder);
+
+    nameFinder.readLine();
+
+    Scanner scanner = new Scanner(nameFinder.readLine());
+    name = scanner.next();
+    name = scanner.next();
+    scanner.close();
+    return name;
+  }
+
+  public String getFamilyName(int index) throws IOException
+  {
+    String name;
+
+    BufferedReader nameFinder = new BufferedReader(
+            new FileReader(families));
+
+    moveLocationBy(index, nameFinder);
+
+    nameFinder.readLine();
+
+    Scanner scanner = new Scanner(nameFinder.readLine());
+    name = scanner.next();
+    name = scanner.next();
+    name = scanner.next();
+    scanner.close();
+    return name;
+  }
+
+  /**
+   * Prints the specified family to a .hmm file.
+   * 
+   * @param index
+   * @throws IOException
+   */
+  public void exportHMM(int index, String location) throws IOException
+  {
+
+
+    BufferedReader br = new BufferedReader(new FileReader(hmms));
+
+    moveLocationBy(index, br);
+
+    String line = br.readLine();
+
+    PrintWriter writer = new PrintWriter(
+            new FileOutputStream(new File(location), true));
+    while (!"//".equals(line))
+    {
+      writer.println(line);
+      line = br.readLine();
+    }
+    writer.println(line);
+    writer.close();
+    br.close();
+
+  }
+  
+  /**
+   * Clears all raw, binned and current position data in the current directory.
+   * 
+   * @throws FileNotFoundException
+   */
+  public void clear() throws FileNotFoundException
+  {
+    PrintWriter pos = new PrintWriter(
+            currentFolder + "/CurrentPosition.txt");
+    pos.println("0");
+    
+    PrintWriter raw = new PrintWriter(currentFolder + RAW);
+    
+    PrintWriter bin = new PrintWriter(currentFolder + BINNED);
+    
+    pos.close();
+    bin.close();
+    raw.close();
+  }
+
+  public void sortIntoClans(String directory) throws IOException
+  {
+    BufferedReader clanFinder = new BufferedReader(new FileReader(FAMILIESTOCLAN));
+    BufferedReader familyReader = new BufferedReader(
+            new FileReader(families));
+    BufferedReader hmmReader = new BufferedReader(new FileReader(hmms));
+    int families = 0;
+    // moveLocationBy(7000, familyReader);
+    // moveLocationBy(7000, clanFinder);
+    // moveLocationBy(7000, hmmReader);
+    HashMap<String, Integer> clanIndexes = new HashMap<>();
+    ArrayList<Integer> familyCounts = new ArrayList<>();
+    int filePos = 0; 
+    int clanCount = 0;
+    String line;
+    line = clanFinder.readLine();
+    
+    while (!"".equals(line) && !" ".equals(line) && line != null)
+    {
+     String clanName;
+      boolean inClan = false;
+     while (!(line.indexOf("//") > -1))
+     {
+       
+      if (line.indexOf("#=GF CL") > -1)
+      {
+          families++;
+          System.out.println(families);
+          inClan = true;
+        Scanner scanner = new Scanner(line);
+        scanner.next();
+        scanner.next();
+        clanName = scanner.next();
+          scanner.close();
+        
+        if (!clanIndexes.containsKey(clanName))
+        {
+          clanIndexes.put(clanName, clanCount);
+            clanCount++;
+            familyCounts.add(0);
+        }
+
+
+          Integer clanI = clanIndexes.get(clanName);
+          String clanPath = directory + "/Clan" + clanI.toString();
+          createFolders(clanPath);
+
+          int index = clanIndexes.get(clanName);
+          exportFile(familyReader,
+                  clanPath + "/Families/Fam" + familyCounts.get(index)
+                          + ".sto",
+                  false);
+          exportFile(hmmReader,
+                  clanPath + "/HMMs/HMM" + familyCounts.get(index) + ".hmm",
+                  false);
+
+          int count = familyCounts.get(index);
+          count++;
+          familyCounts.set(index, count);
+      }
+        line = clanFinder.readLine();
+
+      }
+      if (!inClan)
+      {
+        moveLocationBy(1, familyReader);
+        moveLocationBy(1, hmmReader);
+      }
+      filePos++;
+      // System.out.println(filePos + " files read.");
+      line = clanFinder.readLine();
+
+     }
+    clanFinder.close();
+
+    for (int clan = 0; clan < clanCount; clan++)
+    {
+      PrintWriter writer = new PrintWriter(
+              directory + "/Clan" + clan + "/NumberOfFamilies.txt");
+      int count = familyCounts.get(clan);
+      writer.print(count);
+      writer.close();
+    }
+      
+    }
+
+  public String getFamilies()
+  {
+    return families;
+  }
+
+  public void setFamilies(String families)
+  {
+    this.families = currentFolder + families;
+  }
+
+  public String getHmms()
+  {
+    return hmms;
+  }
+
+  public void setHmms(String hmms)
+  {
+    this.hmms = currentFolder + hmms;
+  }
+    
+  public void alignWithinClan(String exportLocation, String clansLocation)
+          throws IOException, InterruptedException
+  {
+    int alignmentsExported = 0;
+    for (int clan = 0; clan < 604; clan++)
+    {
+      System.out.println(clan);
+      int famCount = 0;
+      String clanPath = clansLocation + "/Clan" + clan;
+      int numberOfFamilies;
+      BufferedReader br = new BufferedReader(
+              new FileReader(clanPath + "/NumberOfFamilies.txt"));
+      String line = br.readLine();
+      numberOfFamilies = Integer.parseInt(line);
+      br.close();
+      if (numberOfFamilies == 1)
+      {
+        continue;
+      }
+      final String commandExportLocation = exportLocation + "/Clan" + clan;
+      createFolders(commandExportLocation);
+      for (int family = 0; family < numberOfFamilies; family++)
+      {
+        famCount++;
+        ArrayList<Integer> indexes = new ArrayList<>();
+        for (int i = 0; i < numberOfFamilies; i++)
+        {
+          if (i != family)
+          {
+            indexes.add(i);
+          }
+        }
+
+        int hmmIndex = getRandom(indexes);
+        String famPath = clanPath + "/Families/Fam" + family + ".sto";
+        String hmmPath = clanPath + "/HMMs/HMM" + hmmIndex + ".hmm";
+        String command = "/media/sf_Shared_Folder/hmmer/binaries/hmmalign --mapali "
+                + clanPath + "/Families/Fam" + hmmIndex + ".sto"
+                + " --trim ";
+        command += hmmPath + " ";
+        command += famPath;
+        final int familyIndex = family;
+        final Process p = Runtime.getRuntime().exec(command);
+
+        new Thread(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            BufferedReader input = new BufferedReader(
+                    new InputStreamReader(p.getInputStream()));
+            String line = null;
+
+            try
+            {
+              PrintWriter writer = new PrintWriter(commandExportLocation
+                      + "/Families/Fam" + familyIndex + ".sto");
+              String lastLine = "";
+              boolean dataFound = false;
+              while ((line = input.readLine()) != null)
+              {
+                if (line.contains("#=GR") && !dataFound)
+                {
+                  writer.println(lastLine);
+                  dataFound = true;
+                }
+                if (line.contains("#") || dataFound || " ".equals(line)
+                        || "".equals(line) || "//".equals(line))
+                {
+                  writer.println(line);
+                }
+                lastLine = line;
+              }
+              writer.close();
+            } catch (IOException e)
+            {
+              e.printStackTrace();
+            }
+          }
+        }).start();
+
+        p.waitFor();
+
+        BufferedReader hmmExporter = new BufferedReader(
+                new FileReader(hmmPath));
+
+        exportFile(hmmExporter,
+                commandExportLocation + "/HMMs/HMM" + family + ".hmm",
+                false);
+
+        alignmentsExported++;
+
+
+      }
+      PrintWriter writer = new PrintWriter(
+              commandExportLocation + "/NumberOfFamilies.txt");
+      writer.print(famCount);
+      writer.close();
+    }
+
+  }
+
+  public boolean atEnd(BufferedReader br) throws IOException
+  {
+    boolean end = false;
+    br.mark(80);
+    String line = br.readLine();
+    if ("".equals(line) || line == null)
+    {
+      end = true;
+    }
+    br.reset();
+    return end;
+  }
+
+  public int getRandom(ArrayList<Integer> list)
+  {
+    if (!(list.size() > 0))
+    {
+      System.out.println("Error - size = " + list.size());
+    }
+    int index = generator.nextInt(list.size());
+    int value = list.get(index);
+    list.remove(index);
+    return value;
+  }
+
+  public void createFolders(String clanPath)
+  {
+    File clanFolder = new File(clanPath);
+    if (!clanFolder.exists())
+    {
+      clanFolder.mkdir();
+    }
+
+    File famFolder = new File(clanPath + "/Families");
+    File hmmFolder = new File(clanPath + "/HMMs");
+    if (!famFolder.exists())
+    {
+      famFolder.mkdir();
+      hmmFolder.mkdir();
+    }
+  }
+}
+
+
+
+
index 0454cab..ca311ae 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.util;
 
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
@@ -73,10 +76,10 @@ 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
@@ -84,8 +87,8 @@ public class HttpUtils
    * @throws IOException
    * @throws ProtocolException
    */
-  public static boolean checkUrlAvailable(URL url, int readTimeout)
-          throws IOException, ProtocolException
+  public static boolean checkUrlAvailable(URL url,
+          int readTimeout) throws IOException, ProtocolException
   {
     // System.out.println(System.currentTimeMillis() + " " + url);
 
@@ -101,4 +104,46 @@ public class HttpUtils
     return connection.getResponseCode() == 200;
   }
 
+  /**
+   * download from given URL and return a pointer to temporary file
+   */
+  public static File fetchURLToTemp(String url) throws OutOfMemoryError,
+          IOException
+  {
+    long time = System.currentTimeMillis();
+    URL rcall = new URL(url);
+
+    InputStream is = new BufferedInputStream(rcall.openStream());
+    File outFile = null;
+    try
+    {
+      outFile = File.createTempFile("jalview", ".xml");
+      outFile.deleteOnExit();
+      if (outFile.length() == 0)
+      {
+        outFile.delete();
+        return null;
+      }
+    } catch (Exception ex)
+    {
+    }
+
+    if (outFile != null)
+    {
+      FileOutputStream fio = new FileOutputStream(outFile);
+      byte[] bb = new byte[32 * 1024];
+      int l;
+      while ((l = is.read(bb)) > 0)
+      {
+        fio.write(bb, 0, l);
+      }
+      fio.close();
+      is.close();
+      return outFile;
+    }
+    else
+    {
+      return null;
+    }
+  }
 }
index b3eabae..8c49f41 100644 (file)
@@ -932,70 +932,15 @@ public class MapList
 
     for (int[] range : map.getFromRanges())
     {
-      addRange(range, fromShifts);
+      MappingUtils.addRange(range, fromShifts);
     }
     for (int[] range : map.getToRanges())
     {
-      addRange(range, toShifts);
+      MappingUtils.addRange(range, toShifts);
     }
   }
 
   /**
-   * Adds the given range to a list of ranges. If the new range just extends
-   * existing ranges, the current endpoint is updated instead.
-   * 
-   * @param range
-   * @param addTo
-   */
-  static void addRange(int[] range, List<int[]> addTo)
-  {
-    /*
-     * list is empty - add to it!
-     */
-    if (addTo.size() == 0)
-    {
-      addTo.add(range);
-      return;
-    }
-
-    int[] last = addTo.get(addTo.size() - 1);
-    boolean lastForward = last[1] >= last[0];
-    boolean newForward = range[1] >= range[0];
-
-    /*
-     * contiguous range in the same direction - just update endpoint
-     */
-    if (lastForward == newForward && last[1] == range[0])
-    {
-      last[1] = range[1];
-      return;
-    }
-
-    /*
-     * next range starts at +1 in forward sense - update endpoint
-     */
-    if (lastForward && newForward && range[0] == last[1] + 1)
-    {
-      last[1] = range[1];
-      return;
-    }
-
-    /*
-     * next range starts at -1 in reverse sense - update endpoint
-     */
-    if (!lastForward && !newForward && range[0] == last[1] - 1)
-    {
-      last[1] = range[1];
-      return;
-    }
-
-    /*
-     * just add the new range
-     */
-    addTo.add(range);
-  }
-
-  /**
    * Returns true if mapping is from forward strand, false if from reverse
    * strand. Result is just based on the first 'from' range that is not a single
    * position. Default is true unless proven to be false. Behaviour is not well
index 25cb810..d60d4eb 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;
@@ -49,6 +43,12 @@ 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.
  * 
@@ -364,6 +364,7 @@ public final class MappingUtils
        */
       int startResiduePos = selected.findPosition(firstUngappedPos);
       int endResiduePos = selected.findPosition(lastUngappedPos);
+
       for (SequenceI seq : mapTo.getAlignment().getSequences())
       {
         int mappedStartResidue = 0;
@@ -530,7 +531,7 @@ public final class MappingUtils
 
     if (colsel == null)
     {
-      return;
+      return; // mappedColumns;
     }
 
     char fromGapChar = mapFrom.getAlignment().getGapCharacter();
@@ -552,9 +553,10 @@ public final class MappingUtils
     while (regions.hasNext())
     {
       mapHiddenColumns(regions.next(), codonFrames, newHidden,
-              fromSequences, toSequences, fromGapChar);
+              fromSequences,
+              toSequences, fromGapChar);
     }
-    return;
+    return; // mappedColumns;
   }
 
   /**
@@ -1026,6 +1028,60 @@ public final class MappingUtils
       }
     }
   }
+  /**
+   * Adds the given range to a list of ranges. If the new range just extends
+   * existing ranges, the current endpoint is updated instead.
+   * 
+   * @param range
+   * @param addTo
+   */
+  public static void addRange(int[] range, List<int[]> addTo)
+  {
+    /*
+     * list is empty - add to it!
+     */
+    if (addTo.size() == 0)
+    {
+      addTo.add(range);
+      return;
+    }
+  
+    int[] last = addTo.get(addTo.size() - 1);
+    boolean lastForward = last[1] >= last[0];
+    boolean newForward = range[1] >= range[0];
+  
+    /*
+     * contiguous range in the same direction - just update endpoint
+     */
+    if (lastForward == newForward && last[1] == range[0])
+    {
+      last[1] = range[1];
+      return;
+    }
+  
+    /*
+     * next range starts at +1 in forward sense - update endpoint
+     */
+    if (lastForward && newForward && range[0] == last[1] + 1)
+    {
+      last[1] = range[1];
+      return;
+    }
+  
+    /*
+     * next range starts at -1 in reverse sense - update endpoint
+     */
+    if (!lastForward && !newForward && range[0] == last[1] - 1)
+    {
+      last[1] = range[1];
+      return;
+    }
+  
+    /*
+     * just add the new range
+     */
+    addTo.add(range);
+  }
 
   /**
    * Converts a list of {@code start-end} ranges to a single array of
index acd4591..a0c2cdd 100644 (file)
@@ -57,7 +57,7 @@ public class MessageManager
       /* Getting messages for GV */
       log.info("Getting messages for lang: " + loc);
       Control control = Control.getControl(Control.FORMAT_PROPERTIES);
-      rb = ResourceBundle.getBundle("lang.Messages", loc, control);
+      rb = ResourceBundle.getBundle("lang.Messages", Platform.getLocaleOrNone(loc), control);
       // if (log.isLoggable(Level.FINEST))
       // {
       // // this might take a while, so we only do it if it will be shown
index 573e2d5..5b89db3 100644 (file)
  */
 package jalview.util;
 
-import jalview.javascript.json.JSON;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsEnvironment;
 
 import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.io.BufferedReader;
 import java.io.File;
@@ -32,14 +35,33 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.reflect.Method;
 import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.swing.SwingUtilities;
 
 import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
 
+import com.stevesoft.pat.Regex;
+
+import jalview.bin.Jalview;
+import jalview.javascript.json.JSON;
+import swingjs.api.JSUtilI;
 /**
  * System platform information used by Applet and Application
  * 
@@ -56,6 +78,25 @@ public class Platform
 
   private static Boolean isHeadless = null;
 
+  private static swingjs.api.JSUtilI jsutil;
+
+  static
+  {
+    if (isJS)
+    {
+      try
+      {
+        // this is ok - it's a highly embedded method in Java; the deprecation
+        // is
+        // really a recommended best practice.
+        jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance());
+      } catch (InstantiationException | IllegalAccessException
+              | ClassNotFoundException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
   /**
    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
    * 
@@ -68,6 +109,56 @@ public class Platform
             : isMac);
   }
 
+  public static int SHORTCUT_KEY_MASK = (Platform.isMac()
+          ? KeyEvent.META_DOWN_MASK
+          : KeyEvent.CTRL_DOWN_MASK);
+
+  static
+  {
+    if (!GraphicsEnvironment.isHeadless())
+    {
+      // Using non-deprecated Extended key mask modifiers, but Java 8 has no
+      // getMenuShortcutKeyMaskEx method
+      Toolkit tk = Toolkit.getDefaultToolkit();
+      Method method = null;
+      try
+      {
+        method = tk.getClass().getMethod("getMenuShortcutKeyMaskEx");
+      } catch (Exception e)
+      {
+        System.err.println(
+                "Could not find Toolkit method getMenuShortcutKeyMaskEx. Trying getMenuShortcutKeyMask.");
+      }
+      if (method == null)
+      {
+        try
+        {
+          method = tk.getClass().getMethod("getMenuShortcutKeyMask");
+        } catch (Exception e)
+        {
+          System.err.println(
+                  "Could not find Toolkit method getMenuShortcutKeyMaskEx or getMenuShortcutKeyMask.");
+          e.printStackTrace();
+        }
+      }
+      if (method != null)
+      {
+        try
+        {
+          method.setAccessible(true);
+          SHORTCUT_KEY_MASK = ((int) method.invoke(tk, new Object[0]));
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+      if (SHORTCUT_KEY_MASK <= 0xF)
+      {
+        // shift this into the extended region (was Java 8)
+        SHORTCUT_KEY_MASK = SHORTCUT_KEY_MASK << 6;
+      }
+    }
+  }
   /**
    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
    * 
@@ -115,7 +206,7 @@ public class Platform
   }
 
   /**
-   * Check if we are on a Microsoft plaform...
+   * Check if we are on a Microsoft platform...
    * 
    * @return true if we have to cope with another platform variation
    */
@@ -182,30 +273,14 @@ public class Platform
    */
   protected static boolean isControlDown(MouseEvent e, boolean aMac)
   {
-    if (!aMac)
-    {
-      return e.isControlDown();
-
-      // Jalview 2.11 code below: above is as amended for JalviewJS
-      // /*
-      // * answer false for right mouse button
-      // */
-      // if (e.isPopupTrigger())
-      // {
-      // return false;
-      // }
-      // return
-      // (jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() //
-      // .getMenuShortcutKeyMaskEx()
-      // & jalview.util.ShortcutKeyMaskExWrapper
-      // .getModifiersEx(e)) != 0; // getModifiers()) != 0;
-    }
-    // answer false for right mouse button
-    // shortcut key will be META for a Mac
-    return !e.isPopupTrigger()
-            && (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    & e.getModifiers()) != 0;
-    // could we use e.isMetaDown() here?
+    //
+    // System.out.println(e.isPopupTrigger()
+    // + " " + ((SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0)
+    // + " " + e.isControlDown());
+    return (aMac
+            ? !e.isPopupTrigger()
+                    && (SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0
+            : e.isControlDown());
   }
 
   // BH: I don't know about that previous method. Here is what SwingJS uses.
@@ -279,6 +354,20 @@ public class Platform
 
   public static long time, mark, set, duration;
 
+  /**
+   * typical usage:
+   * 
+   * Platform.timeCheck(null, Platform.TIME_MARK);
+   * 
+   * ...
+   * 
+   * Platform.timeCheck("some message", Platform.TIME_MARK);
+   * 
+   * reset...[set/mark]n...get
+   * 
+   * @param msg
+   * @param mode
+   */
   public static void timeCheck(String msg, int mode)
   {
     long t = System.currentTimeMillis();
@@ -286,6 +375,7 @@ public class Platform
     {
     case TIME_RESET:
       time = mark = t;
+      duration = 0;
       if (msg != null)
       {
         System.err.println("Platform: timer reset\t\t\t" + msg);
@@ -294,6 +384,7 @@ public class Platform
     case TIME_MARK:
       if (set > 0)
       {
+        // total time between set/mark points
         duration += (t - set);
       }
       else
@@ -326,84 +417,71 @@ public class Platform
 
   public static void cacheFileData(String path, Object data)
   {
-    if (!isJS() || data == null)
+    if (isJS && data != null)
     {
-      return;
+      jsutil.cachePathData(path, data);
     }
-    /**
-     * @j2sNative
-     * 
-     *            swingjs.JSUtil.cacheFileData$S$O(path, data);
-     * 
-     */
   }
 
   public static void cacheFileData(File file)
   {
-    byte[] data;
-    if (!isJS() || (data = Platform.getFileBytes(file)) == null)
+    if (isJS)
     {
-      return;
+      byte[] data = Platform.getFileBytes(file);
+      {
+        if (data != null)
+        {
+          cacheFileData(file.toString(), data);
+        }
+      }
     }
-    cacheFileData(file.toString(), data);
   }
 
   public static byte[] getFileBytes(File f)
   {
-    return /** @j2sNative f && swingjs.JSUtil.getFileAsBytes$O(f) || */
-    null;
+    return (isJS && f != null ? jsutil.getBytes(f) : null);
   }
 
   public static byte[] getFileAsBytes(String fileStr)
   {
-    byte[] bytes = null;
-    // BH 2018 hack for no support for access-origin
-    /**
-     * @j2sNative bytes = swingjs.JSUtil.getFileAsBytes$O(fileStr)
-     */
-    cacheFileData(fileStr, bytes);
-    return bytes;
+    if (isJS && fileStr != null)
+    {
+      byte[] bytes = (byte[]) jsutil.getFile(fileStr, false);
+      cacheFileData(fileStr, bytes);
+      return bytes;
+    }
+    return null;
   }
 
-  @SuppressWarnings("unused")
   public static String getFileAsString(String url)
   {
-    String ret = null;
-    /**
-     * @j2sNative
-     * 
-     *            ret = swingjs.JSUtil.getFileAsString$S(url);
-     * 
-     * 
-     */
-    cacheFileData(url, ret);
-    return ret;
+    if (isJS && url != null)
+    {
+      String ret = (String) jsutil.getFile(url, true);
+      cacheFileData(url, ret);
+      return ret;
+    }
+    return null;
   }
 
   public static boolean setFileBytes(File f, String urlstring)
   {
-    if (!isJS())
+    if (isJS && f != null && urlstring != null)
     {
-      return false;
+      @SuppressWarnings("unused")
+      byte[] bytes = getFileAsBytes(urlstring);
+      jsutil.setFileBytes(f, bytes);
+      return true;
     }
-    @SuppressWarnings("unused")
-    byte[] bytes = getFileAsBytes(urlstring);
-    // TODO temporary doubling of ç§˜bytes and _bytes;
-    // just remove _bytes when new transpiler has been installed
-    /**
-     * @j2sNative f.\u79d8bytes = f._bytes = bytes;
-     */
-    return true;
+    return false;
   }
 
   public static void addJ2SBinaryType(String ext)
   {
-    /**
-     * @j2sNative
-     * 
-     *            J2S._binaryTypes.push("." + ext + "?");
-     * 
-     */
+    if (isJS)
+    {
+      jsutil.addBinaryFileType(ext);
+    }
   }
 
   /**
@@ -426,7 +504,7 @@ public class Platform
    * @param url
    * @return true if window has been opened
    */
-  public static boolean openURL(String url)
+  public static boolean openURL(String url) throws IOException
   {
     if (!isJS())
     {
@@ -443,11 +521,7 @@ public class Platform
 
   public static String getUniqueAppletID()
   {
-    /**
-     * @j2sNative return swingjs.JSUtil.getApplet$()._uniqueId;
-     *
-     */
-    return null;
+    return (isJS ? (String) jsutil.getAppletAttribute("_uniqueId") : null);
 
   }
 
@@ -461,28 +535,30 @@ public class Platform
    */
   public static void readInfoProperties(String prefix, Properties p)
   {
-    if (!isJS())
+    if (isJS)
     {
-      return;
-    }
-    String id = getUniqueAppletID();
-    String key = "", value = "";
-    /**
-     * @j2sNative var info = swingjs.JSUtil.getApplet$().__Info || {}; for (var
-     *            key in info) { if (key.indexOf(prefix) == 0) { value = "" +
-     *            info[key];
-     */
+      String id = getUniqueAppletID();
 
-    System.out.println(
-            "Platform id=" + id + " reading Info." + key + " = " + value);
-    p.put(id + "_" + key, value);
+      String key = "";
+      String value = "";
+      @SuppressWarnings("unused")
+      Object info = jsutil.getAppletAttribute("__Info");
+      /**
+       * @j2sNative for (key in info) { value = info[key];
+       */
 
-    /**
-     * @j2sNative
-     * 
-     * 
-     *            } }
-     */
+      if (key.indexOf(prefix) == 0)
+      {
+        System.out.println("Platform id=" + id + " reading Info." + key
+                + " = " + value);
+        p.put(key, value);
+
+      }
+
+      /**
+       * @j2sNative }
+       */
+    }
   }
 
   public static void setAjaxJSON(URL url)
@@ -561,11 +637,9 @@ public class Platform
   public static void streamToFile(InputStream is, File outFile)
           throws IOException
   {
-    if (isJS() && /**
-                   * @j2sNative outFile.setBytes$O && outFile.setBytes$O(is) &&
-                   */
-            true)
+    if (isJS)
     {
+      jsutil.setFileBytes(outFile, is);
       return;
     }
     FileOutputStream fio = new FileOutputStream(outFile);
@@ -597,11 +671,12 @@ public class Platform
   public static void addJ2SDirectDatabaseCall(String domain)
   {
 
-    if (isJS())
+    if (isJS)
     {
+      jsutil.addDirectDatabaseCall(domain);
       System.out.println(
-              "Platform adding known access-control-allow-origin * for domain "
-                      + domain);
+            "Platform adding known access-control-allow-origin * for domain "
+                    + domain);
       /**
        * @j2sNative
        * 
@@ -611,10 +686,13 @@ public class Platform
 
   }
 
+  /**
+   * Allow for URL-line command arguments. Untested.
+   * 
+   */
   public static void getURLCommandArguments()
   {
-    try
-    {
+      try {
       /**
        * Retrieve the first query field as command arguments to Jalview. Include
        * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's
@@ -622,13 +700,9 @@ public class Platform
        * 
        * @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."));
+       *            + "?").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)
     {
@@ -657,4 +731,327 @@ public class Platform
     String p2 = path2.replace('\\', '/');
     return p1.equals(p2);
   }
+  ///////////// JAL-3253 Applet additions //////////////
+
+  /**
+   * Retrieve the object's embedded size from a div's style on a page if
+   * embedded in SwingJS.
+   * 
+   * @param frame
+   *          JFrame or JInternalFrame
+   * @param defaultWidth
+   *          use -1 to return null (no default size)
+   * @param defaultHeight
+   * @return the embedded dimensions or null (no default size or not embedded)
+   */
+  public static Dimension getDimIfEmbedded(Component frame,
+          int defaultWidth, int defaultHeight)
+  {
+    Dimension d = null;
+    if (isJS)
+    {
+      d = (Dimension) getEmbeddedAttribute(frame, "dim");
+    }
+    return (d == null && defaultWidth >= 0
+            ? new Dimension(defaultWidth, defaultHeight)
+            : d);
+
+  }
+
+  public static Regex newRegex(String regex)
+  {
+    return newRegex(regex, null);
+  }
+
+  public static Regex newRegex(String searchString, String replaceString)
+  {
+    ensureRegex();
+    return (replaceString == null ? new Regex(searchString)
+            : new Regex(searchString, replaceString));
+  }
+
+  public static Regex newRegexPerl(String code)
+  {
+    ensureRegex();
+    return Regex.perlCode(code);
+  }
+
+  /**
+   * Initialize Java debug logging. A representative sample -- adapt as desired.
+   */
+  public static void startJavaLogging()
+  {
+    /**
+     * @j2sIgnore
+     */
+    {
+      logClass("java.awt.EventDispatchThread", "java.awt.EventQueue",
+              "java.awt.Component", "java.awt.focus.Component",
+              "java.awt.event.Component",
+              "java.awt.focus.DefaultKeyboardFocusManager");
+    }
+  }
+
+  /**
+   * Initiate Java logging for a given class. Only for Java, not JavaScript;
+   * Allows debugging of complex event processing.
+   * 
+   * @param className
+   */
+  public static void logClass(String... classNames)
+  {
+    /**
+     * @j2sIgnore
+     * 
+     * 
+     */
+    {
+      Logger rootLogger = Logger.getLogger("");
+      rootLogger.setLevel(Level.ALL);
+      ConsoleHandler consoleHandler = new ConsoleHandler();
+      consoleHandler.setLevel(Level.ALL);
+      for (int i = classNames.length; --i >= 0;)
+      {
+        Logger logger = Logger.getLogger(classNames[i]);
+        logger.setLevel(Level.ALL);
+        logger.addHandler(consoleHandler);
+      }
+    }
+  }
+
+  /**
+   * load a resource -- probably a core file -- if and only if a particular
+   * class has not been instantialized. We use a String here because if we used
+   * a .class object, that reference itself would simply load the class, and we
+   * want the core package to include that as well.
+   * 
+   * @param resourcePath
+   * @param className
+   */
+  public static void loadStaticResource(String resourcePath,
+          String className)
+  {
+    if (isJS)
+    {
+      jsutil.loadResourceIfClassUnknown(resourcePath, className);
+    }
+  }
+
+  public static void ensureRegex()
+  {
+    if (isJS)
+    {
+      loadStaticResource("core/core_stevesoft.z.js",
+              "com.stevesoft.pat.Regex");
+    }
+  }
+
+  /**
+   * Set the "app" property of the HTML5 applet object, for example,
+   * "testApplet.app", to point to the Jalview instance. This will be the object
+   * that page developers use that is similar to the original Java applet object
+   * that was accessed via LiveConnect.
+   * 
+   * @param j
+   */
+  public static void setAppClass(Object j)
+  {
+    if (isJS)
+    {
+      jsutil.setAppClass(j);
+    }
+  }
+
+  /**
+   *
+   * If this frame is embedded in a web page, return a known type.
+   * 
+   * @param frame
+   *          a JFrame or JInternalFrame
+   * @param type
+   *          "name", "node", "init", "dim", or any DOM attribute, such as "id"
+   * @return null if frame is not embedded.
+   */
+  public static Object getEmbeddedAttribute(Component frame, String type)
+  {
+    return (isJS ? jsutil.getEmbeddedAttribute(frame, type) : null);
+  }
+
+  public static void stackTrace()
+  {
+    try
+    {
+      throw new NullPointerException();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+  }
+
+  public static URL getDocumentBase()
+  {
+    return (isJS ? jsutil.getDocumentBase() : null);
+  }
+
+  public static URL getCodeBase()
+  {
+    return (isJS ? jsutil.getCodeBase() : null);
+  }
+
+  public static String getUserPath(String subpath)
+  {
+    char sep = File.separatorChar;
+    return System.getProperty("user.home") + sep
+            + subpath.replace('/', sep);
+  }
+
+  /**
+   * This method enables checking if a cached file has exceeded a certain
+   * threshold(in days)
+   * 
+   * @param file
+   *          the cached file
+   * @param noOfDays
+   *          the threshold in days
+   * @return
+   */
+  public static boolean isFileOlderThanThreshold(File file, int noOfDays)
+  {
+    if (isJS())
+    {
+      // not meaningful in SwingJS -- this is a session-specific temp file. It
+      // doesn't have a timestamp.
+      return false;
+    }
+    Path filePath = file.toPath();
+    BasicFileAttributes attr;
+    int diffInDays = 0;
+    try
+    {
+      attr = Files.readAttributes(filePath, BasicFileAttributes.class);
+      diffInDays = (int) ((new Date().getTime()
+              - attr.lastModifiedTime().toMillis())
+              / (1000 * 60 * 60 * 24));
+      // System.out.println("Diff in days : " + diffInDays);
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    return noOfDays <= diffInDays;
+  }
+
+  /**
+   * Get the leading integer part of a string that begins with an integer.
+   * 
+   * @param input
+   *          - the string input to process
+   * @param failValue
+   *          - value returned if unsuccessful
+   * @return
+   */
+  public static int getLeadingIntegerValue(String input, int failValue)
+  {
+    if (input == null)
+    {
+      return failValue;
+    }
+    if (isJS)
+    {
+      int val = /** @j2sNative 1 ? parseInt(input) : */
+              0;
+      return (val == val + 0 ? val : failValue);
+    }
+    // JavaScript does not support Regex ? lookahead
+    String[] parts = input.split("(?=\\D)(?<=\\d)");
+    if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+"))
+    {
+      return Integer.valueOf(parts[0]);
+    }
+    return failValue;
+  }
+
+  public static Map<String, Object> getAppletInfoAsMap()
+  {
+    return (isJS ? jsutil.getAppletInfoAsMap() : null);
+  }
+
+  /**
+   * Get the SwingJS applet ID and combine that with the frameType
+   * 
+   * @param frameType
+   *          "alignment", "desktop", etc., or null
+   * @return
+   */
+  public static String getAppID(String frameType)
+  {
+
+    String id = Jalview.getInstance().j2sAppletID;
+    if (id == null)
+    {
+      Jalview.getInstance().j2sAppletID = id = (isJS
+              ? (String) jsutil.getAppletAttribute("_id")
+              : "jalview");
+    }
+    return id + (frameType == null ? "" : "-" + frameType);
+  }
+
+  /**
+   * Option to avoid unnecessary seeking of nonexistent resources in JavaScript.
+   * Works in Java as well.
+   * 
+   * @param loc
+   * @return
+   */
+  public static Locale getLocaleOrNone(Locale loc)
+  {
+    return (isJS && loc.getLanguage() == "en" ? new Locale("") : loc);
+  }
+
+  /**
+   * From UrlDownloadClient; trivial in JavaScript; painful in Java.
+   * 
+   * @param urlstring
+   * @param outfile
+   * @throws IOException
+   */
+  public static void download(String urlstring, String outfile)
+          throws IOException
+  {
+    Path temp = null;
+    try (InputStream is = new URL(urlstring).openStream())
+    {
+      if (isJS)
+      { // so much easier!
+        streamToFile(is, new File(outfile));
+        return;
+      }
+      temp = Files.createTempFile(".jalview_", ".tmp");
+      try (FileOutputStream fos = new FileOutputStream(temp.toString());
+              ReadableByteChannel rbc = Channels.newChannel(is))
+      {
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        // copy tempfile to outfile once our download completes
+        // incase something goes wrong
+        Files.copy(temp, Paths.get(outfile),
+                StandardCopyOption.REPLACE_EXISTING);
+      }
+    } catch (IOException e)
+    {
+      throw e;
+    } finally
+    {
+      try
+      {
+        if (temp != null)
+        {
+          Files.deleteIfExists(temp);
+        }
+      } catch (IOException e)
+      {
+        System.out.println("Exception while deleting download temp file: "
+                + e.getMessage());
+      }
+    }
+  }
 }
diff --git a/src/jalview/util/ProbabilityAnalyserKickstarter.java b/src/jalview/util/ProbabilityAnalyserKickstarter.java
new file mode 100644 (file)
index 0000000..59c0a9f
--- /dev/null
@@ -0,0 +1,221 @@
+package jalview.util;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * This class contains the brain of the analyser program, and contains a number
+ * of commands for the processing of data.
+ * 
+ * @author TZVanaalten
+ *
+ */
+
+public class ProbabilityAnalyserKickstarter
+{
+
+  public static void main(String[] args)
+          throws IOException, InterruptedException
+  {
+
+    // this does all of the processing
+    HMMProbabilityDistributionAnalyser analyser = new HMMProbabilityDistributionAnalyser();
+
+    boolean running = true;
+    System.out.println("ACTIVATED");
+    while (running)
+    {
+      Scanner keyboard = new Scanner(System.in);
+      String command = keyboard.nextLine();
+
+      Scanner inputScanner = new Scanner(command);
+      // prints family to console. Syntax is printFam <index>
+      if (command.indexOf("printFam") > -1)
+      {
+        try
+        {
+          inputScanner.next();
+          int index = inputScanner.nextInt();
+          analyser.printFam(index);
+          continue;
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+
+      }
+      // prints HMM to console. Syntax is printHMM <index>
+      if (command.indexOf("printHMM") > -1)
+      {
+        try
+        {
+        inputScanner.next();
+        int index = inputScanner.nextInt();
+        analyser.printHMM(index);
+        continue;
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+      }
+      // prints family to file in current folder. Syntax is exportFam <index>.
+      if (command.indexOf("exportFam") > -1)
+      {
+        try
+        {
+        inputScanner.next();
+        int index = inputScanner.nextInt();
+          String location = inputScanner.next();
+          analyser.exportFam(index, location);
+        continue;
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+      }
+      // prints HMM to file in current folder. Syntax is exportHMM <index>.
+      if (command.indexOf("exportHMM") > -1)
+      {
+        try
+        {
+        inputScanner.next();
+        int index = inputScanner.nextInt();
+          String location = inputScanner.next();
+          analyser.exportHMM(index, location);
+        continue;
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+      }
+      // Processes data. Syntax is run <number of loops> <increments>. The
+      // number loops specifies the number of increments the program will run.
+      // After each increment, the data stored currently in the program is
+      // exported and re-read back into the program. This is to ensure that the
+      // program can be terminated without losing a large quantity of data. The
+      // increment is the number of families read per 'save'.
+      if (command.indexOf("run") > -1 && !(command.indexOf("ToEnd") > -1))
+      {
+        try
+        {
+
+        inputScanner.next();
+
+        int loops = inputScanner.nextInt();
+        int increments = inputScanner.nextInt();
+        boolean keepRaw = inputScanner.nextBoolean();
+
+        for (int i = 0; i < loops; i++)
+        {
+          analyser.run(increments, keepRaw);
+          System.out.println("Saved");
+        }
+        System.out.println("Task completed");
+        continue;
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+        continue;
+      }
+      if ((command.indexOf("runToEnd") > -1))
+      {
+        try
+        {
+
+          inputScanner.next();
+          int minCount = inputScanner.nextInt();
+          int maxCount = inputScanner.nextInt();
+          boolean keepRaw = inputScanner.nextBoolean();
+          boolean forClans = inputScanner.nextBoolean();
+          analyser.runToEnd(minCount, maxCount, keepRaw, forClans);
+          System.out.println("Task completed");
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+        continue;
+      }
+      // terminates program. Syntax is terminate.
+      if (command.indexOf("terminate") > -1)
+      {
+        running = false;
+        continue;
+      }
+      // clears files in current directory (Only a specific set of files).
+      // Syntax is clear.
+      if (command.indexOf("clear") > -1)
+      {
+        analyser.clear();
+        continue;
+      }
+      // changes current directory. Syntax is cd <directory>
+      if (command.indexOf("cd") > -1)
+      {
+        try
+        {
+        inputScanner.next();
+        analyser.setFolder(inputScanner.next());
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+
+        }
+        continue;
+      }
+
+      if (command.indexOf("getFamName") > -1)
+      {
+        try
+        {
+        inputScanner.next();
+        System.out.println(analyser.getFamilyName(inputScanner.nextInt()));
+
+        } catch (Exception e)
+        {
+          System.out.println("Command failed");
+        }
+        continue;
+      }
+      if (command.indexOf("sortIntoClans") > -1)
+      {
+        inputScanner.next();
+        analyser.sortIntoClans(inputScanner.next());
+          continue;
+
+      }
+      if (command.indexOf("setFamilies") > -1)
+      {
+        inputScanner.next();
+        analyser.setFamilies(inputScanner.next());
+        continue;
+
+      }
+
+      if (command.indexOf("setHMMs") > -1)
+      {
+        inputScanner.next();
+        analyser.setHmms(inputScanner.next());
+        continue;
+
+      }
+
+      if (command.indexOf("alignWithinClans") > -1)
+      {
+        inputScanner.next();
+        String export = inputScanner.next();
+        String clans = inputScanner.next();
+        analyser.alignWithinClan(export, clans);
+        continue;
+
+      }
+
+      System.out.println("Unrecognised command");
+    }
+
+
+
+
+  }
+
+}
index 83330b9..4c33cf8 100644 (file)
  */
 package jalview.util;
 
-import java.awt.event.MouseEvent;
-
 public class ShortcutKeyMaskExWrapper
 {
-
-  private static final Float specversion;
-
-  private static final float modern;
-
-  public static final int SHIFT_DOWN_MASK;
-
-  public static final int ALT_DOWN_MASK;
-
-  private static final ShortcutKeyMaskExWrapperI wrapper;
-
-  static
-  {
-    specversion = Platform.isJS() ? Float.valueOf(8)
-            : Float.parseFloat(
-                    System.getProperty("java.specification.version"));
-    modern = 11;
-
-    if (specversion >= modern)
-    {
-      wrapper = new jalview.util.ShortcutKeyMaskExWrapper11();
-      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.SHIFT_DOWN_MASK;
-      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.ALT_DOWN_MASK;
-    }
-    else
-    {
-      wrapper = new jalview.util.ShortcutKeyMaskExWrapper8();
-      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.SHIFT_DOWN_MASK;
-      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.ALT_DOWN_MASK;
-    }
-  }
-
-  public static int getMenuShortcutKeyMaskEx()
-  {
-    return wrapper.getMenuShortcutKeyMaskEx();
-  }
-
-  public static int getModifiersEx(MouseEvent e)
-  {
-    return wrapper.getModifiersEx(e);
-  }
+//
+//  public static int SHIFT_DOWN_MASK = KeyEvent.SHIFT_DOWN_MASK;
+//
+//  public static int ALT_DOWN_MASK = KeyEvent.ALT_DOWN_MASK;
+//
+//  public static int SHORTCUT_KEY_MASK = (Platform.isMac() ? KeyEvent.META_DOWN_MASK : KeyEvent.CTRL_DOWN_MASK);
+// 
+//  static
+//  {
+//    if (!GraphicsEnvironment.isHeadless())
+//    {
+//      try
+//      {
+//
+//        Class<? extends Toolkit> tk = Toolkit.getDefaultToolkit().getClass();
+//        Method method = tk.getMethod("getMenuShortcutKeyMaskEx");
+//        if (method == null)
+//          method = tk.getMethod("getMenuShortcutKeyMask");
+//        SHORTCUT_KEY_MASK = ((int) method.invoke(tk, new Object[0]));
+//        if (SHORTCUT_KEY_MASK <= 0xF)
+//        {
+//          // shift this into the extended region (was Java 8)
+//          SHORTCUT_KEY_MASK = SHORTCUT_KEY_MASK << 6;
+//        }
+//      } catch (Exception e)
+//      {
+//      }
+//    }
+//  }
+//
+//  public static int getMenuShortcutKeyMaskEx()
+//  {
+//    return SHORTCUT_KEY_MASK;
+//  }
 
 }
index 74c565d..a00e0d3 100644 (file)
@@ -24,9 +24,6 @@ import java.awt.event.MouseEvent;
 
 public interface ShortcutKeyMaskExWrapperI
 {
-  public static int SHIFT_DOWN_MASK = 0;
-
-  public static int ALT_DOWN_MASK = 0;
 
   public int getMenuShortcutKeyMaskEx();
 
index 8b62e48..c7a8f33 100644 (file)
@@ -449,7 +449,7 @@ public class StringUtils
     {
       text = text.substring(0, endTag);
     }
-
+  
     if (startTag == -1 && (text.contains("<") || text.contains(">")))
     {
       text = text.replaceAll("<", "&lt;");
@@ -572,6 +572,31 @@ public class StringUtils
     return enc;
   }
 
+  /**
+   * Answers true if the string is not empty and consists only of digits, or
+   * characters 'a'-'f' or 'A'-'F', else false
+   * 
+   * @param s
+   * @return
+   */
+  public static boolean isHexString(String s)
+  {
+    int j = s.length();
+    if (j == 0)
+    {
+      return false;
+    }
+    for (int i = 0; i < j; i++)
+    {
+      int c = s.charAt(i);
+      if (!(c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public static int firstCharPosIgnoreCase(String text, String chars)
   {
     int min = text.length() + 1;
index 828a72f..58d52da 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.util;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.jar.JarInputStream;
 
@@ -45,4 +46,9 @@ public interface jarInputStreamProvider
    *         with it
    */
   String getFilename();
+
+  /**
+   * @return jarFile name if from SwingJS
+   */
+  File getFile();
 }
index 08af2ec..9991421 100644 (file)
@@ -23,7 +23,8 @@ package jalview.viewmodel;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
-import jalview.api.AlignCalcManagerI;
+import jalview.api.AlignCalcManagerI2;
+import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignExportSettingsI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
@@ -56,9 +57,10 @@ import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.styles.ViewStyle;
-import jalview.workers.AlignCalcManager;
+import jalview.workers.AlignCalcManager2;
 import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
+import jalview.workers.InformationThread;
 import jalview.workers.StrucConsensusThread;
 
 import java.awt.Color;
@@ -83,6 +85,8 @@ import java.util.Map;
 public abstract class AlignmentViewport
         implements AlignViewportI, CommandListener, VamsasSource
 {
+  public static final String PROPERTY_ALIGNMENT = "alignment";
+  public static final String PROPERTY_SEQUENCE = "sequence";
   protected ViewportRanges ranges;
 
   protected ViewStyleI viewStyle = new ViewStyle();
@@ -103,6 +107,27 @@ public abstract class AlignmentViewport
    * alignment displayed in the viewport. Please use get/setter
    */
   protected AlignmentI alignment;
+  
+  /*
+   * probably unused indicator that view is of a dataset rather than an
+   * alignment
+   */
+
+  protected boolean ignoreBelowBackGroundFrequencyCalculation = false;
+
+  protected boolean infoLetterHeight = false;
+
+  protected AlignmentAnnotation occupancy;
+  
+  /**
+   * results of alignment consensus analysis for visible portion of view
+   */
+  protected ProfilesI consensusProfiles;
+
+  /**
+   * HMM profile for the alignment
+   */
+  protected ProfilesI hmmProfiles;
 
   public AlignmentViewport(AlignmentI al)
   {
@@ -591,6 +616,7 @@ public abstract class AlignmentViewport
    */
   protected boolean isDataset = false;
 
+  
   public void setDataset(boolean b)
   {
     isDataset = b;
@@ -605,14 +631,34 @@ public abstract class AlignmentViewport
 
   protected ColumnSelection colSel = new ColumnSelection();
 
-  public boolean autoCalculateConsensus = true;
+  protected boolean autoCalculateConsensusAndConservation = true;
+
+  public boolean getAutoCalculateConsensusAndConservation()
+  { // BH 2019.07.24
+    return autoCalculateConsensusAndConservation;
+  }
+
+  public void setAutoCalculateConsensusAndConservation(boolean b)
+  {
+    autoCalculateConsensusAndConservation = b;
+  }
 
   protected boolean autoCalculateStrucConsensus = true;
 
+  public boolean getAutoCalculateStrucConsensus()
+  { // BH 2019.07.24
+    return autoCalculateStrucConsensus;
+  }
+
+  public void setAutoCalculateStrucConsensus(boolean b)
+  {
+    autoCalculateStrucConsensus = b;
+  }
   protected boolean ignoreGapsInConsensusCalculation = false;
 
   protected ResidueShaderI residueShading = new ResidueShader();
 
+  
   @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
   {
@@ -664,7 +710,8 @@ public abstract class AlignmentViewport
          * retain any colour thresholds per group while
          * changing choice of colour scheme (JAL-2386)
          */
-        sg.setColourScheme(cs == null ? null : cs.getInstance(this, sg));
+        sg.setColourScheme(
+                cs == null ? null : cs.getInstance(this, sg));
         if (cs != null)
         {
           sg.getGroupColourScheme().alignmentChanged(sg,
@@ -686,6 +733,7 @@ public abstract class AlignmentViewport
     return residueShading;
   }
 
+  
   protected AlignmentAnnotation consensus;
 
   protected AlignmentAnnotation complementConsensus;
@@ -720,6 +768,7 @@ public abstract class AlignmentViewport
 
   protected Conservation hconservation = null;
 
+  
   @Override
   public void setConservation(Conservation cons)
   {
@@ -758,6 +807,18 @@ public abstract class AlignmentViewport
   }
 
   @Override
+  public void setHmmProfiles(ProfilesI info)
+  {
+    hmmProfiles = info;
+  }
+
+  @Override
+  public ProfilesI getHmmProfiles()
+  {
+    return hmmProfiles;
+  }
+
+  @Override
   public Hashtable<String, Object>[] getComplementConsensusHash()
   {
     return hcomplementConsensus;
@@ -813,7 +874,7 @@ public abstract class AlignmentViewport
     return strucConsensus;
   }
 
-  protected AlignCalcManagerI calculator = new AlignCalcManager();
+  protected AlignCalcManagerI2 calculator = new AlignCalcManager2();
 
   /**
    * trigger update of conservation annotation
@@ -823,12 +884,12 @@ public abstract class AlignmentViewport
     // see note in mantis : issue number 8585
     if (alignment.isNucleotide()
             || (conservation == null && quality == null)
-            || !autoCalculateConsensus)
+            || !autoCalculateConsensusAndConservation)
     {
       return;
     }
-    if (calculator.getRegisteredWorkersOfClass(
-            jalview.workers.ConservationThread.class) == null)
+    if (calculator.getWorkersOfClass(
+            jalview.workers.ConservationThread.class).isEmpty())
     {
       calculator.registerWorker(
               new jalview.workers.ConservationThread(this, ap));
@@ -841,12 +902,11 @@ public abstract class AlignmentViewport
   public void updateConsensus(final AlignmentViewPanel ap)
   {
     // see note in mantis : issue number 8585
-    if (consensus == null || !autoCalculateConsensus)
+    if (consensus == null || !autoCalculateConsensusAndConservation)
     {
       return;
     }
-    if (calculator
-            .getRegisteredWorkersOfClass(ConsensusThread.class) == null)
+    if (calculator.getWorkersOfClass(ConsensusThread.class).isEmpty())
     {
       calculator.registerWorker(new ConsensusThread(this, ap));
     }
@@ -877,16 +937,22 @@ public abstract class AlignmentViewport
       }
       if (doConsensus)
       {
-        if (calculator.getRegisteredWorkersOfClass(
-                ComplementConsensusThread.class) == null)
+        if (calculator.getWorkersOfClass(ComplementConsensusThread.class).isEmpty())
         {
-          calculator
-                  .registerWorker(new ComplementConsensusThread(this, ap));
+          calculator.registerWorker(new ComplementConsensusThread(this, ap));
         }
       }
     }
   }
 
+  @Override
+  public void initInformationWorker(final AlignmentViewPanel ap)
+  {
+    if (calculator.getWorkersOfClass(InformationThread.class).isEmpty())
+    {
+      calculator.registerWorker(new InformationThread(this, ap));
+    }
+  }
   // --------START Structure Conservation
   public void updateStrucConsensus(final AlignmentViewPanel ap)
   {
@@ -902,8 +968,7 @@ public abstract class AlignmentViewport
     {
       return;
     }
-    if (calculator.getRegisteredWorkersOfClass(
-            StrucConsensusThread.class) == null)
+    if (calculator.getWorkersOfClass(StrucConsensusThread.class).isEmpty())
     {
       calculator.registerWorker(new StrucConsensusThread(this, ap));
     }
@@ -922,7 +987,7 @@ public abstract class AlignmentViewport
     {
       return false;
     }
-    if (calculator.workingInvolvedWith(alignmentAnnotation))
+    if (calculator.isWorkingWithAnnotation(alignmentAnnotation))
     {
       // System.err.println("grey out ("+alignmentAnnotation.label+")");
       return true;
@@ -950,12 +1015,14 @@ public abstract class AlignmentViewport
     strucConsensus = null;
     conservation = null;
     quality = null;
+    consensusProfiles = null;
     groupConsensus = null;
     groupConservation = null;
     hconsensus = null;
     hconservation = null;
     hcomplementConsensus = null;
     gapcounts = null;
+    calculator.shutdown();
     calculator = null;
     residueShading = null; // may hold a reference to Consensus
     changeSupport = null;
@@ -975,7 +1042,7 @@ public abstract class AlignmentViewport
   }
 
   @Override
-  public AlignCalcManagerI getCalcManager()
+  public AlignCalcManagerI2 getCalcManager()
   {
     return calculator;
   }
@@ -1006,6 +1073,21 @@ public abstract class AlignmentViewport
   protected boolean showConsensusHistogram = true;
 
   /**
+   * should hmm profile be rendered by default
+   */
+  protected boolean hmmShowSequenceLogo = false;
+
+  /**
+   * should hmm profile be rendered normalised to row height
+   */
+  protected boolean hmmNormaliseSequenceLogo = false;
+
+  /**
+   * should information histograms be rendered by default
+   */
+  protected boolean hmmShowHistogram = true;
+
+  /**
    * @return the showConsensusProfile
    */
   @Override
@@ -1015,6 +1097,15 @@ public abstract class AlignmentViewport
   }
 
   /**
+   * @return the showInformationProfile
+   */
+  @Override
+  public boolean isShowHMMSequenceLogo()
+  {
+    return hmmShowSequenceLogo;
+  }
+
+  /**
    * @param showSequenceLogo
    *          the new value
    */
@@ -1025,13 +1116,30 @@ public abstract class AlignmentViewport
       // TODO: decouple settings setting from calculation when refactoring
       // annotation update method from alignframe to viewport
       this.showSequenceLogo = showSequenceLogo;
-      calculator.updateAnnotationFor(ConsensusThread.class);
-      calculator.updateAnnotationFor(ComplementConsensusThread.class);
-      calculator.updateAnnotationFor(StrucConsensusThread.class);
+      for (AlignCalcWorkerI worker : calculator.getWorkers())
+      {
+        if (worker.getClass().equals(ConsensusThread.class) ||
+                worker.getClass().equals(ComplementConsensusThread.class) ||
+                worker.getClass().equals(StrucConsensusThread.class))
+        {
+          worker.updateAnnotation();
+        }
+      }
     }
     this.showSequenceLogo = showSequenceLogo;
   }
 
+  public void setShowHMMSequenceLogo(boolean showHMMSequenceLogo)
+  {
+    if (showHMMSequenceLogo != this.hmmShowSequenceLogo)
+    {
+      this.hmmShowSequenceLogo = showHMMSequenceLogo;
+      // TODO: updateAnnotation if description (tooltip) will show
+      // profile in place of information content?
+      // calculator.updateAnnotationFor(InformationThread.class);
+    }
+    this.hmmShowSequenceLogo = showHMMSequenceLogo;
+  }
   /**
    * @param showConsensusHistogram
    *          the showConsensusHistogram to set
@@ -1042,6 +1150,14 @@ public abstract class AlignmentViewport
   }
 
   /**
+   * @param showInformationHistogram
+   */
+  public void setShowInformationHistogram(boolean showInformationHistogram)
+  {
+    this.hmmShowHistogram = showInformationHistogram;
+  }
+
+  /**
    * @return the showGroupConservation
    */
   public boolean isShowGroupConservation()
@@ -1087,6 +1203,17 @@ public abstract class AlignmentViewport
   }
 
   /**
+   * 
+   * @return flag to indicate if the information content histogram should be
+   *         rendered by default
+   */
+  @Override
+  public boolean isShowInformationHistogram()
+  {
+    return this.hmmShowHistogram;
+  }
+
+  /**
    * when set, updateAlignment will always ensure sequences are of equal length
    */
   private boolean padGaps = false;
@@ -1242,7 +1369,16 @@ public abstract class AlignmentViewport
                 ignoreGapsInConsensusCalculation);
       }
     }
+  }
 
+  public void setIgnoreBelowBackground(boolean b, AlignmentViewPanel ap)
+  {
+    ignoreBelowBackGroundFrequencyCalculation = b;
+  }
+
+  public void setInfoLetterHeight(boolean b, AlignmentViewPanel ap)
+  {
+    infoLetterHeight = b;
   }
 
   private long sgrouphash = -1, colselhash = -1;
@@ -1275,21 +1411,22 @@ public abstract class AlignmentViewport
    * checks current colsel against record of last hash value, and optionally
    * updates record.
    * 
-   * @param b
+   * @param updateHash
    *          update the record of last hash value
    * @return true if colsel changed since last call (when b is true)
    */
-  public boolean isColSelChanged(boolean b)
+  public boolean isColSelChanged(boolean updateHash)
   {
     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
-      if (b)
+      if (updateHash)
       {
         colselhash = hc;
       }
       return true;
     }
+    notifySequence();
     return false;
   }
 
@@ -1299,6 +1436,17 @@ public abstract class AlignmentViewport
     return ignoreGapsInConsensusCalculation;
   }
 
+  @Override
+  public boolean isIgnoreBelowBackground()
+  {
+    return ignoreBelowBackGroundFrequencyCalculation;
+  }
+
+  @Override
+  public boolean isInfoLetterHeight()
+  {
+    return infoLetterHeight;
+  }
   // property change stuff
   // JBPNote Prolly only need this in the applet version.
   private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
@@ -1350,21 +1498,6 @@ public abstract class AlignmentViewport
     }
   }
 
-  /**
-   * Property change listener for changes in alignment
-   * 
-   * @param prop
-   *          DOCUMENT ME!
-   * @param oldvalue
-   *          DOCUMENT ME!
-   * @param newvalue
-   *          DOCUMENT ME!
-   */
-  public void firePropertyChange(String prop, Object oldvalue,
-          Object newvalue)
-  {
-    changeSupport.firePropertyChange(prop, oldvalue, newvalue);
-  }
 
   // common hide/show column stuff
 
@@ -1430,9 +1563,9 @@ public abstract class AlignmentViewport
 
       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
 
-      firePropertyChange("alignment", null, alignment.getSequences());
       // used to set hasHiddenRows/hiddenRepSequences here, after the property
       // changed event
+      notifySequence();
       sendSelection();
     }
   }
@@ -1460,7 +1593,7 @@ public abstract class AlignmentViewport
 
       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
 
-      firePropertyChange("alignment", null, alignment.getSequences());
+      notifyAlignment();
       sendSelection();
     }
   }
@@ -1494,7 +1627,7 @@ public abstract class AlignmentViewport
         setSequenceAnnotationsVisible(seq[i], false);
       }
       ranges.setStartSeq(startSeq);
-      firePropertyChange("alignment", null, alignment.getSequences());
+      notifyAlignment();
     }
   }
 
@@ -1820,8 +1953,9 @@ public abstract class AlignmentViewport
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          clone.makeVisibleAnnotation(selectionGroup.getStartRes(),
-                  selectionGroup.getEndRes(), alignment.getHiddenColumns());
+          clone.makeVisibleAnnotation(
+                  selectionGroup.getStartRes(), selectionGroup.getEndRes(),
+                  alignment.getHiddenColumns());
         }
         else
         {
@@ -1858,11 +1992,11 @@ public abstract class AlignmentViewport
     {
       alignment.padGaps();
     }
-    if (autoCalculateConsensus)
+    if (autoCalculateConsensusAndConservation)
     {
       updateConsensus(ap);
     }
-    if (hconsensus != null && autoCalculateConsensus)
+    if (hconsensus != null && autoCalculateConsensusAndConservation)
     {
       updateConservation(ap);
     }
@@ -1892,7 +2026,6 @@ public abstract class AlignmentViewport
 
     updateAllColourSchemes();
     calculator.restartWorkers();
-    // alignment.adjustSequenceAnnotations();
   }
 
   /**
@@ -2151,6 +2284,9 @@ public abstract class AlignmentViewport
     boolean showprf = isShowSequenceLogo();
     boolean showConsHist = isShowConsensusHistogram();
     boolean normLogo = isNormaliseSequenceLogo();
+    boolean showHMMPrf = isShowHMMSequenceLogo();
+    boolean showInfoHist = isShowInformationHistogram();
+    boolean normHMMLogo = isNormaliseHMMSequenceLogo();
 
     /**
      * TODO reorder the annotation rows according to group/sequence ordering on
@@ -2188,6 +2324,9 @@ public abstract class AlignmentViewport
           sg.setshowSequenceLogo(showprf);
           sg.setShowConsensusHistogram(showConsHist);
           sg.setNormaliseSequenceLogo(normLogo);
+          sg.setShowHMMSequenceLogo(showHMMPrf);
+          sg.setShowInformationHistogram(showInfoHist);
+          sg.setNormaliseHMMSequenceLogo(normHMMLogo);
         }
         if (conv)
         {
@@ -2972,6 +3111,18 @@ public abstract class AlignmentViewport
     return sq;
   }
 
+  public boolean hasReferenceAnnotation()
+  {
+    AlignmentAnnotation[] annots = this.alignment.getAlignmentAnnotation();
+    for (AlignmentAnnotation annot : annots)
+    {
+      if ("RF".equals(annot.label) || annot.label.contains("Reference"))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
   @Override
   public void setCurrentTree(TreeModel tree)
   {
@@ -2985,8 +3136,7 @@ public abstract class AlignmentViewport
   }
 
   @Override
-  public AlignmentExportData getAlignExportData(
-          AlignExportSettingsI options)
+  public AlignmentExportData getAlignExportData(AlignExportSettingsI options)
   {
     AlignmentI alignmentToExport = null;
     String[] omitHidden = null;
@@ -3014,7 +3164,28 @@ public abstract class AlignmentViewport
             omitHidden, alignmentStartEnd);
     return ed;
   }
+  
+  @Override
+  public boolean isNormaliseSequenceLogo()
+  {
+    return normaliseSequenceLogo;
+  }
 
+  public void setNormaliseSequenceLogo(boolean state)
+  {
+    normaliseSequenceLogo = state;
+  }
+
+  @Override
+  public boolean isNormaliseHMMSequenceLogo()
+  {
+    return hmmNormaliseSequenceLogo;
+  }
+
+  public void setNormaliseHMMSequenceLogo(boolean state)
+  {
+    hmmNormaliseSequenceLogo = state;
+  }
   /**
    * flag set to indicate if structure views might be out of sync with sequences
    * in the alignment
@@ -3096,4 +3267,61 @@ public abstract class AlignmentViewport
     return (alignment.getHiddenColumns().getVisContigsIterator(start, end,
             false));
   }
+  /**
+   * Filters out sequences with an eValue higher than the specified value. The
+   * filtered sequences are hidden or deleted. Sequences with no eValues are also
+   * filtered out.
+   * 
+   * @param eValue
+   * @param delete
+   */
+  public void filterByEvalue(double eValue)
+  {
+    for (SequenceI seq : alignment.getSequencesArray())
+    {
+      if ((seq.getAnnotation("Search Scores") == null
+              || seq.getAnnotation("Search Scores")[0].getEValue() > eValue)
+              && seq.getHMM() == null)
+      {
+        hideSequence(new SequenceI[] { seq });
+      }
+    }
+  }
+
+  /**
+   * Filters out sequences with an score lower than the specified value. The
+   * filtered sequences are hidden or deleted.
+   * 
+   * @param score
+   * @param delete
+   */
+  public void filterByScore(double score)
+  {
+    for (SequenceI seq : alignment.getSequencesArray())
+    {
+      if ((seq.getAnnotation("Search Scores") == null
+              || seq.getAnnotation("Search Scores")[0]
+                      .getBitScore() < score)
+              && seq.getHMM() == null)
+      {
+        hideSequence(new SequenceI[] { seq });
+      }
+    }
+  }  
+
+  /**
+   * Notify TreePanel and AlignmentPanel of some sort of alignment change.
+   */
+  public void notifyAlignment()
+  {
+    changeSupport.firePropertyChange(PROPERTY_ALIGNMENT, null, alignment.getSequences());
+  }
+  
+  /**
+   * Notify AlignmentPanel of a sequence column selection or visibility changes.
+   */
+  public void notifySequence()
+  {
+    changeSupport.firePropertyChange(PROPERTY_SEQUENCE, null, null);
+  }
 }
index 0235081..c153dc1 100644 (file)
@@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 
+import java.awt.Dimension;
 import java.awt.Graphics;
 
 public abstract class OverviewDimensions
@@ -71,15 +72,22 @@ public abstract class OverviewDimensions
    *          true if the annotation panel is to be shown, false otherwise
    */
   public OverviewDimensions(ViewportRanges ranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
+    if (!showAnnotationPanel)
+    {
+      graphHeight = 0;
+    }
+
     // scale the initial size of overviewpanel to shape of alignment
     float initialScale = (float) ranges.getAbsoluteAlignmentWidth()
             / (float) ranges.getAbsoluteAlignmentHeight();
 
-    if (!showAnnotationPanel)
+    if (dim != null)
     {
-      graphHeight = 0;
+      width = dim.width;
+      sequencesHeight = dim.height;
+      return;
     }
 
     if (ranges.getAbsoluteAlignmentWidth() > ranges
index de90a21..598b989 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.viewmodel;
 
+import java.awt.Dimension;
+
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
 import jalview.datamodel.AlignmentI;
@@ -38,10 +40,21 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   private int ydiff; // when dragging, difference in alignment units between
                      // start sequence and original mouse click position
 
+  /**
+   * for testng only
+   * 
+   * @param vpranges
+   * @param showAnnotationPanel
+   */  
+  @Deprecated
+  public OverviewDimensionsHideHidden(ViewportRanges vpranges, boolean showAnnotationPanel) {
+    this(vpranges, showAnnotationPanel, null);
+  }
+
   public OverviewDimensionsHideHidden(ViewportRanges vpranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
-    super(vpranges, showAnnotationPanel);
+    super(vpranges, showAnnotationPanel, dim);
     ranges = vpranges;
     resetAlignmentDims();
   }
index 4c36cfa..b4fa934 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.viewmodel;
 
+import java.awt.Dimension;
+
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
 import jalview.datamodel.AlignmentI;
@@ -38,6 +40,18 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   private int ydiff; // when dragging, difference in alignment units between
                      // start sequence and original mouse click position
 
+  
+  /**
+   * for testng only
+   * 
+   * @param vpranges
+   * @param showAnnotationPanel
+   */  
+  @Deprecated
+  public OverviewDimensionsShowHidden(ViewportRanges vpranges, boolean showAnnotationPanel) {
+    this(vpranges, showAnnotationPanel, null);
+  }
+
   /**
    * Create an OverviewDimensions object
    * 
@@ -47,9 +61,9 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
    *          true if the annotation panel is to be shown, false otherwise
    */
   public OverviewDimensionsShowHidden(ViewportRanges vpranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
-    super(vpranges, showAnnotationPanel);
+    super(vpranges, showAnnotationPanel, dim);
     ranges = vpranges;
     resetAlignmentDims();
   }
index 104fb62..e1abb54 100644 (file)
@@ -112,7 +112,7 @@ public class ViewportRanges extends ViewportProperties
    * Set first residue visible in the viewport, and retain the current width.
    * Fires a property change event.
    * 
-   * @param res
+   * @param res 
    *          residue position
    */
   public void setStartRes(int res)
@@ -136,13 +136,25 @@ public class ViewportRanges extends ViewportProperties
     int[] oldvalues = updateStartEndRes(start, end);
     int oldstartres = oldvalues[0];
     int oldendres = oldvalues[1];
+    
+    if (oldstartres == startRes && oldendres == endRes)
+    {
+      return; // BH 2019.07.27 standard check for no changes
+    }
 
+    // "STARTRES" is a misnomer here -- really "STARTORENDRES"
+    // note that this could be "no change" if the range is just being expanded
     changeSupport.firePropertyChange(STARTRES, oldstartres, startRes);
     if (oldstartres == startRes)
     {
+      // only caught in ViewportRangesTest
+      // No listener cares about this
+      // "ENDRES" is a misnomer here -- really "ENDONLYRES"
+      // BH 2019.07.27 adds end change check
+      // fire only if only the end is changed
       // event won't be fired if start positions are same
       // fire an event for the end positions in case they changed
-      changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
+     changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
     }
   }
 
@@ -224,7 +236,6 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setStartEndSeq(int start, int end)
   {
-    // System.out.println("ViewportRange setStartEndSeq " + start + " " + end);
     int[] oldvalues = updateStartEndSeq(start, end);
     int oldstartseq = oldvalues[0];
     int oldendseq = oldvalues[1];
@@ -250,34 +261,16 @@ public class ViewportRanges extends ViewportProperties
    */
   private int[] updateStartEndSeq(int start, int end)
   {
-    int oldstartseq = this.startSeq;
-    int visibleHeight = getVisibleAlignmentHeight();
-    if (start > visibleHeight - 1)
-    {
-      startSeq = Math.max(visibleHeight - 1, 0);
-    }
-    else if (start < 0)
-    {
-      startSeq = 0;
-    }
-    else
-    {
-      startSeq = start;
-    }
 
+//    if (end == 3 && this.endSeq == 14 || end == 13 && this.endSeq == 3) {
+//      new NullPointerException().printStackTrace(System.out);
+//      System.out.println("ViewportRange updateStartEndSeq " + start + " " + end + " " + Thread.currentThread());
+//    }
+    int oldstartseq = this.startSeq;
     int oldendseq = this.endSeq;
-    if (end >= visibleHeight)
-    {
-      endSeq = Math.max(visibleHeight - 1, 0);
-    }
-    else if (end < 0)
-    {
-      endSeq = 0;
-    }
-    else
-    {
-      endSeq = end;
-    }
+    int max = getVisibleAlignmentHeight() - 1;
+    startSeq = Math.max(0,  Math.min(start, max));
+    endSeq = Math.max(0, Math.min(end, max));
     return new int[] { oldstartseq, oldendseq };
   }
 
@@ -392,21 +385,15 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setViewportStartAndWidth(int start, int w)
   {
-    int vpstart = start;
-    if (vpstart < 0)
-    {
-      vpstart = 0;
-    }
-
-    /*
-     * if not wrapped, don't leave white space at the right margin
-     */
+    int vpstart = Math.max(0, start);
+     
     if (!wrappedMode)
     {
-      if ((w <= getVisibleAlignmentWidth())
-              && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
+      // if not wrapped, don't leave white space at the right margin
+      int maxStart = getVisibleAlignmentWidth() - w;
+      if (maxStart >= 0)
       {
-        vpstart = getVisibleAlignmentWidth() - w;
+        vpstart = Math.min(vpstart, maxStart);
       }
 
     }
@@ -425,22 +412,15 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setViewportStartAndHeight(int start, int h)
   {
-    int vpstart = start;
-
-    int visHeight = getVisibleAlignmentHeight();
-    if (vpstart < 0)
-    {
-      vpstart = 0;
-    }
-    else if (h <= visHeight && vpstart + h > visHeight)
-    // viewport height is less than the full alignment and we are running off
-    // the bottom
+    int vpstart = Math.max(0, start);
+    int maxStart = getVisibleAlignmentHeight() - h;
+    if (maxStart > 0)
     {
-      vpstart = visHeight - h;
+      // can't start higher than vertical extent will allow
+      // (viewport height is less than the full alignment
+      // and we are running off the bottom)
+      vpstart = Math.min(vpstart, maxStart);
     }
-    // System.out.println("ViewportRanges setviewportStartAndHeight " + vpstart
-    // + " " + start + " " + h + " " + getVisibleAlignmentHeight());
-
     setStartEndSeq(vpstart, vpstart + h - 1);
   }
 
@@ -797,4 +777,10 @@ public class ViewportRanges extends ViewportProperties
 
     return maxScroll;
   }
+  
+
+  @Override
+  public String toString() {
+     return "[ViewportRange " + startSeq + "-" + endSeq + ", "+ startRes + "-" + endRes + "]";
+  }
 }
diff --git a/src/jalview/workers/AlignCalcManager.java b/src/jalview/workers/AlignCalcManager.java
deleted file mode 100644 (file)
index 08ef3a2..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * 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.workers;
-
-import jalview.api.AlignCalcManagerI;
-import jalview.api.AlignCalcWorkerI;
-import jalview.datamodel.AlignmentAnnotation;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class AlignCalcManager implements AlignCalcManagerI
-{
-  /*
-   * list of registered workers
-   */
-  private volatile List<AlignCalcWorkerI> restartable;
-
-  /*
-   * types of worker _not_ to run (for example, because they have
-   * previously thrown errors)
-   */
-  private volatile List<Class<? extends AlignCalcWorkerI>> blackList;
-
-  /*
-   * global record of calculations in progress
-   */
-  private volatile List<AlignCalcWorkerI> inProgress;
-
-  /*
-   * record of calculations pending or in progress in the current context
-   */
-  private volatile Map<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>> updating;
-
-  /*
-   * workers that have run to completion so are candidates for visual-only 
-   * update of their results
-   */
-  private HashSet<AlignCalcWorkerI> canUpdate;
-
-  /**
-   * Constructor
-   */
-  public AlignCalcManager()
-  {
-    restartable = Collections
-            .synchronizedList(new ArrayList<AlignCalcWorkerI>());
-    blackList = Collections.synchronizedList(
-            new ArrayList<Class<? extends AlignCalcWorkerI>>());
-    inProgress = Collections
-            .synchronizedList(new ArrayList<AlignCalcWorkerI>());
-    updating = Collections.synchronizedMap(
-            new Hashtable<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>>());
-    canUpdate = new HashSet<AlignCalcWorkerI>();
-  }
-
-  @Override
-  public void notifyStart(AlignCalcWorkerI worker)
-  {
-    synchronized (updating)
-    {
-      List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
-      if (upd == null)
-      {
-        updating.put(worker.getClass(), upd = Collections
-                .synchronizedList(new ArrayList<AlignCalcWorkerI>()));
-      }
-      synchronized (upd)
-      {
-        upd.add(worker);
-      }
-    }
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI)
-   */
-  @Override
-  public boolean isPending(AlignCalcWorkerI workingClass)
-  {
-    List<AlignCalcWorkerI> upd;
-    synchronized (updating)
-    {
-      upd = updating.get(workingClass.getClass());
-      if (upd == null)
-      {
-        return false;
-      }
-      synchronized (upd)
-      {
-        if (upd.size() > 1)
-        {
-          return true;
-        }
-      }
-      return false;
-    }
-  }
-
-  @Override
-  public boolean notifyWorking(AlignCalcWorkerI worker)
-  {
-    synchronized (inProgress)
-    {
-      if (inProgress.contains(worker))
-      {
-        return false; // worker is already working, so ask caller to wait around
-      }
-      else
-      {
-        inProgress.add(worker);
-      }
-    }
-    return true;
-  }
-
-  @Override
-  public void workerComplete(AlignCalcWorkerI worker)
-  {
-    synchronized (inProgress)
-    {
-      // System.err.println("Worker " + worker + " marked as complete.");
-      inProgress.remove(worker);
-      List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
-      if (upd != null)
-      {
-        synchronized (upd)
-        {
-          upd.remove(worker);
-        }
-        canUpdate.add(worker);
-      }
-    }
-  }
-
-  @Override
-  public void disableWorker(AlignCalcWorkerI worker)
-  {
-    synchronized (blackList)
-    {
-      blackList.add(worker.getClass());
-    }
-  }
-
-  @Override
-  public boolean isDisabled(AlignCalcWorkerI worker)
-  {
-    synchronized (blackList)
-    {
-      return blackList.contains(worker.getClass());
-    }
-  }
-
-  @Override
-  public void startWorker(AlignCalcWorkerI worker)
-  {
-    if (!isDisabled(worker))
-    {
-      Thread tw = new Thread(worker);
-      tw.setName(worker.getClass().toString());
-      tw.start();
-    }
-  }
-
-  @Override
-  public boolean isWorking(AlignCalcWorkerI worker)
-  {
-    synchronized (inProgress)
-    {// System.err.println("isWorking : worker "+(worker!=null ?
-     // worker.getClass():"null")+ " "+hashCode());
-      return worker != null && inProgress.contains(worker);
-    }
-  }
-
-  @Override
-  public boolean isWorking()
-  {
-    synchronized (inProgress)
-    {
-      // System.err.println("isWorking "+hashCode());
-      return inProgress.size() > 0;
-    }
-  }
-
-  @Override
-  public void registerWorker(AlignCalcWorkerI worker)
-  {
-    synchronized (restartable)
-    {
-      if (!restartable.contains(worker))
-      {
-        restartable.add(worker);
-      }
-      startWorker(worker);
-    }
-  }
-
-  @Override
-  public void restartWorkers()
-  {
-    synchronized (restartable)
-    {
-      for (AlignCalcWorkerI worker : restartable)
-      {
-        startWorker(worker);
-      }
-    }
-  }
-
-  @Override
-  public boolean workingInvolvedWith(
-          AlignmentAnnotation alignmentAnnotation)
-  {
-    synchronized (inProgress)
-    {
-      for (AlignCalcWorkerI worker : inProgress)
-      {
-        if (worker.involves(alignmentAnnotation))
-        {
-          return true;
-        }
-      }
-    }
-    synchronized (updating)
-    {
-      for (List<AlignCalcWorkerI> workers : updating.values())
-      {
-        for (AlignCalcWorkerI worker : workers)
-        {
-          if (worker.involves(alignmentAnnotation))
-          {
-            return true;
-          }
-        }
-      }
-    }
-    return false;
-  }
-
-  @Override
-  public void updateAnnotationFor(
-          Class<? extends AlignCalcWorkerI> workerClass)
-  {
-
-    AlignCalcWorkerI[] workers;
-    synchronized (canUpdate)
-    {
-      workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
-    }
-    for (AlignCalcWorkerI worker : workers)
-    {
-      if (workerClass.equals(worker.getClass()))
-      {
-        worker.updateAnnotation();
-      }
-    }
-  }
-
-  @Override
-  public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
-          Class<? extends AlignCalcWorkerI> workerClass)
-  {
-    List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
-    synchronized (canUpdate)
-    {
-      for (AlignCalcWorkerI worker : canUpdate)
-      {
-        if (workerClass.equals(worker.getClass()))
-        {
-          workingClass.add(worker);
-        }
-      }
-    }
-    return (workingClass.size() == 0) ? null : workingClass;
-  }
-
-  @Override
-  public void enableWorker(AlignCalcWorkerI worker)
-  {
-    synchronized (blackList)
-    {
-      blackList.remove(worker.getClass());
-    }
-  }
-
-  @Override
-  public void removeRegisteredWorkersOfClass(
-          Class<? extends AlignCalcWorkerI> typeToRemove)
-  {
-    List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
-    Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
-    synchronized (restartable)
-    {
-      for (AlignCalcWorkerI worker : restartable)
-      {
-        if (typeToRemove.equals(worker.getClass()))
-        {
-          removable.add(worker);
-          toremovannot.add(worker);
-        }
-      }
-      restartable.removeAll(removable);
-    }
-    synchronized (canUpdate)
-    {
-      for (AlignCalcWorkerI worker : canUpdate)
-      {
-        if (typeToRemove.equals(worker.getClass()))
-        {
-          removable.add(worker);
-          toremovannot.add(worker);
-        }
-      }
-      canUpdate.removeAll(removable);
-    }
-    // TODO: finish testing this extension
-
-    /*
-     * synchronized (inProgress) { // need to kill or mark as dead any running
-     * threads... (inProgress.get(typeToRemove)); }
-     * 
-     * if (workers == null) { return; } for (AlignCalcWorkerI worker : workers)
-     * {
-     * 
-     * if (isPending(worker)) { worker.abortAndDestroy(); startWorker(worker); }
-     * else { System.err.println("Pending exists for " + workerClass); } }
-     */
-  }
-
-  /**
-   * Deletes the worker that update the given annotation, provided it is marked
-   * as deletable.
-   */
-  @Override
-  public void removeWorkerForAnnotation(AlignmentAnnotation ann)
-  {
-    /*
-     * first just find those to remove (to avoid
-     * ConcurrentModificationException)
-     */
-    List<AlignCalcWorkerI> toRemove = new ArrayList<AlignCalcWorkerI>();
-    for (AlignCalcWorkerI worker : restartable)
-    {
-      if (worker.involves(ann))
-      {
-        if (worker.isDeletable())
-        {
-          toRemove.add(worker);
-        }
-      }
-    }
-
-    /*
-     * remove all references to deleted workers so any references 
-     * they hold to annotation data can be garbage collected 
-     */
-    for (AlignCalcWorkerI worker : toRemove)
-    {
-      restartable.remove(worker);
-      blackList.remove(worker.getClass());
-      inProgress.remove(worker);
-      canUpdate.remove(worker);
-      synchronized (updating)
-      {
-        List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
-        if (upd != null)
-        {
-          upd.remove(worker);
-        }
-      }
-    }
-  }
-}
diff --git a/src/jalview/workers/AlignCalcManager2.java b/src/jalview/workers/AlignCalcManager2.java
new file mode 100644 (file)
index 0000000..eba241a
--- /dev/null
@@ -0,0 +1,548 @@
+package jalview.workers;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.String.format;
+import static java.util.Collections.synchronizedMap;
+import static java.util.Collections.unmodifiableList;
+
+import java.util.ArrayList;
+
+import jalview.api.AlignCalcListener;
+import jalview.api.AlignCalcManagerI2;
+import jalview.api.AlignCalcWorkerI;
+import jalview.api.PollableAlignCalcWorkerI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentAnnotation;
+
+public class AlignCalcManager2 implements AlignCalcManagerI2
+{
+  private abstract class WorkerManager
+  {
+    protected volatile boolean enabled = true;
+
+    protected AlignCalcWorkerI worker;
+
+    WorkerManager(AlignCalcWorkerI worker)
+    {
+      this.worker = worker;
+    }
+
+    protected AlignCalcWorkerI getWorker()
+    {
+      return worker;
+    }
+
+    boolean isEnabled()
+    {
+      return enabled;
+    }
+
+    void setEnabled(boolean enabled)
+    {
+      this.enabled = enabled;
+    }
+
+    synchronized void restart()
+    {
+      if (!isEnabled())
+      {
+        return;
+      }
+      if (!isRegistered())
+      {
+        setEnabled(false);
+      }
+      if (isWorking())
+      {
+        cancel();
+      }
+      submit();
+    }
+
+    protected boolean isRegistered()
+    {
+      return registered.containsKey(getWorker());
+    }
+
+    abstract boolean isWorking();
+
+    protected abstract void submit();
+
+    abstract void cancel();
+  }
+
+  private class SimpleWorkerManager extends WorkerManager
+  {
+    private Future<?> task = null;
+
+    SimpleWorkerManager(AlignCalcWorkerI worker)
+    {
+      super(worker);
+    }
+
+    @Override
+    boolean isWorking()
+    {
+      return task != null && !task.isDone();
+    }
+
+    @Override
+    protected void submit()
+    {
+      if (task != null && !(task.isDone() || task.isCancelled()))
+      {
+        throw new IllegalStateException(
+                "Cannot submit new task if the prevoius one is still running");
+      }
+      Console.debug(
+              format("Worker %s queued", getWorker().getClass().getName()));
+      task = executor.submit(() -> {
+        try
+        {
+          Console.debug(format("Worker %s started",
+                  getWorker().getClass().getName()));
+          getWorker().run();
+          Console.debug(format("Worker %s finished",
+                  getWorker().getClass().getName()));
+        } catch (InterruptedException e)
+        {
+          Console.debug(format("Worker %s interrupted",
+                  getWorker().getClass().getName()));
+        } catch (Throwable th)
+        {
+          Console.debug(format("Worker %s failed",
+                  getWorker().getClass().getName()), th);
+        } finally
+        {
+          if (!isRegistered())
+          {
+            // delete worker reference so garbage collector can remove it
+            worker = null;
+          }
+        }
+      });
+    }
+
+    @Override
+    synchronized void cancel()
+    {
+      if (!isWorking())
+      {
+        return;
+      }
+      Console.debug(format("Cancelling worker %s",
+              getWorker().getClass().getName()));
+      task.cancel(true);
+    }
+  }
+
+  private class PollableWorkerManager extends WorkerManager
+  {
+    private Future<?> task = null;
+
+    PollableWorkerManager(PollableAlignCalcWorkerI worker)
+    {
+      super(worker);
+    }
+
+    @Override
+    protected PollableAlignCalcWorkerI getWorker()
+    {
+      return (PollableAlignCalcWorkerI) super.getWorker();
+    }
+
+    @Override
+    boolean isWorking()
+    {
+      return task != null && !task.isDone();
+    }
+
+    protected void submit()
+    {
+      if (task != null && !(task.isDone() || task.isCancelled()))
+      {
+        throw new IllegalStateException(
+                "Cannot submit new task if the prevoius one is still running");
+      }
+      Console.debug(
+              format("Worker %s queued", getWorker().getClass().getName()));
+      final var runnable = new Runnable()
+      {
+        private boolean started = false;
+
+        private boolean completed = false;
+
+        Future<?> future = null;
+
+        @Override
+        public void run()
+        {
+          try
+          {
+            if (!started)
+            {
+              Console.debug(format("Worker %s started",
+                      getWorker().getClass().getName()));
+              getWorker().startUp();
+              started = true;
+            }
+            else if (!completed)
+            {
+              Console.debug(format("Polling worker %s",
+                      getWorker().getClass().getName()));
+              if (getWorker().poll())
+              {
+                Console.debug(format("Worker %s finished",
+                        getWorker().getClass().getName()));
+                completed = true;
+              }
+            }
+          } catch (Throwable th)
+          {
+            Console.debug(format("Worker %s failed",
+                    getWorker().getClass().getName()), th);
+            completed = true;
+          }
+          if (completed)
+          {
+            final var worker = getWorker();
+            if (!isRegistered())
+              PollableWorkerManager.super.worker = null;
+            Console.debug(format("Finalizing completed worker %s",
+                    worker.getClass().getName()));
+            worker.done();
+            // almost impossible, but the future may be null at this point
+            // let it throw NPE to cancel forcefully
+            future.cancel(false);
+          }
+        }
+      };
+      runnable.future = task = executor.scheduleWithFixedDelay(runnable, 10,
+              1000, TimeUnit.MILLISECONDS);
+    }
+
+    synchronized protected void cancel()
+    {
+      if (!isWorking())
+      {
+        return;
+      }
+      Console.debug(format("Cancelling worker %s",
+              getWorker().getClass().getName()));
+      task.cancel(false);
+      executor.submit(() -> {
+        final var worker = getWorker();
+        if (!isRegistered())
+          PollableWorkerManager.super.worker = null;
+        if (worker != null)
+        {
+          worker.cancel();
+          Console.debug(format("Finalizing cancelled worker %s",
+                  worker.getClass().getName()));
+          worker.done();
+        }
+      });
+    }
+  }
+
+  private final ScheduledExecutorService executor = Executors
+          .newSingleThreadScheduledExecutor();
+
+  private final Map<AlignCalcWorkerI, WorkerManager> registered = synchronizedMap(
+          new HashMap<>());
+
+  private final Map<AlignCalcWorkerI, WorkerManager> oneshot = synchronizedMap(
+          new WeakHashMap<>());
+
+  private final List<AlignCalcListener> listeners = new CopyOnWriteArrayList<>();
+
+  private WorkerManager createManager(AlignCalcWorkerI worker)
+  {
+    if (worker instanceof PollableAlignCalcWorkerI)
+    {
+      return new PollableWorkerManager((PollableAlignCalcWorkerI) worker);
+    }
+    else
+    {
+      return new SimpleWorkerManager(worker);
+    }
+  }
+
+  @Override
+  public void registerWorker(AlignCalcWorkerI worker)
+  {
+    Objects.requireNonNull(worker);
+    WorkerManager manager = createManager(worker);
+    registered.putIfAbsent(worker, manager);
+    startWorker(worker);
+  }
+
+  @Override
+  public List<AlignCalcWorkerI> getWorkers()
+  {
+    List<AlignCalcWorkerI> result = new ArrayList<>(registered.size());
+    result.addAll(registered.keySet());
+    return result;
+  }
+
+  @Override
+  public List<AlignCalcWorkerI> getWorkersOfClass(
+          Class<? extends AlignCalcWorkerI> cls)
+  {
+    List<AlignCalcWorkerI> collected = new ArrayList<>();
+    for (var worker : getWorkers())
+    {
+      if (worker.getClass().equals(cls))
+      {
+        collected.add(worker);
+      }
+    }
+    return unmodifiableList(collected);
+  }
+
+  @Override
+  public void removeWorker(AlignCalcWorkerI worker)
+  {
+    if (worker.isDeletable())
+    {
+      registered.remove(worker);
+    }
+  }
+
+  @Override
+  public void removeWorkerForAnnotation(AlignmentAnnotation annot)
+  {
+    synchronized (registered)
+    {
+      for (var worker : getWorkers())
+      {
+        if (worker.involves(annot))
+        {
+          removeWorker(worker);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void removeWorkersOfClass(Class<? extends AlignCalcWorkerI> cls)
+  {
+    synchronized (registered)
+    {
+      for (var worker : getWorkers())
+      {
+        if (worker.getClass().equals(cls))
+        {
+          removeWorker(worker);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void disableWorker(AlignCalcWorkerI worker)
+  {
+    // Null pointer check might be needed
+    registered.get(worker).setEnabled(false);
+  }
+
+  @Override
+  public void enableWorker(AlignCalcWorkerI worker)
+  {
+    // Null pointer check might be needed
+    registered.get(worker).setEnabled(true);
+  }
+
+  @Override
+  public boolean isDisabled(AlignCalcWorkerI worker)
+  {
+    if (registered.containsKey(worker))
+    {
+      return !registered.get(worker).isEnabled();
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  @Override
+  public boolean isWorking(AlignCalcWorkerI worker)
+  {
+    var manager = registered.get(worker);
+    if (manager == null)
+      manager = oneshot.get(worker);
+    if (manager == null)
+      return false;
+    else
+      return manager.isWorking();
+  }
+
+  @Override
+  public boolean isWorkingWithAnnotation(AlignmentAnnotation annot)
+  {
+    synchronized (registered)
+    {
+      for (var entry : registered.entrySet())
+        if (entry.getKey().involves(annot) && entry.getValue().isWorking())
+          return true;
+    }
+    synchronized (oneshot)
+    {
+      for (var entry : registered.entrySet())
+        if (entry.getKey().involves(annot) && entry.getValue().isWorking())
+          return true;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean isWorking()
+  {
+    synchronized (registered)
+    {
+      for (var manager : registered.values())
+        if (manager.isWorking())
+          return true;
+    }
+    synchronized (oneshot)
+    {
+      for (var manager : oneshot.values())
+        if (manager.isWorking())
+          return true;
+    }
+    return false;
+  }
+
+  @Override
+  public void startWorker(AlignCalcWorkerI worker)
+  {
+    Objects.requireNonNull(worker);
+    var manager = registered.get(worker);
+    if (manager == null)
+    {
+      Console.warn("Starting unregistered worker " + worker);
+      manager = createManager(worker);
+      oneshot.put(worker, manager);
+    }
+    manager.restart();
+  }
+
+  @Override
+  public void restartWorkers()
+  {
+    synchronized (registered)
+    {
+      for (var manager : registered.values())
+      {
+        manager.restart();
+      }
+    }
+  }
+
+  @Override
+  public void cancelWorker(AlignCalcWorkerI worker)
+  {
+    Objects.requireNonNull(worker);
+    var manager = registered.get(worker);
+    if (manager == null)
+      manager = oneshot.get(worker);
+    if (manager == null)
+    {
+      throw new NoSuchElementException();
+    }
+    manager.cancel();
+  }
+
+  private void notifyQueued(AlignCalcWorkerI worker)
+  {
+    for (AlignCalcListener listener : listeners)
+    {
+      listener.workerQueued(worker);
+    }
+  }
+
+  private void notifyStarted(AlignCalcWorkerI worker)
+  {
+    for (AlignCalcListener listener : listeners)
+    {
+      listener.workerStarted(worker);
+    }
+  }
+
+  private void notifyCompleted(AlignCalcWorkerI worker)
+  {
+    for (AlignCalcListener listener : listeners)
+    {
+      try
+      {
+        listener.workerCompleted(worker);
+      } catch (RuntimeException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  private void notifyCancelled(AlignCalcWorkerI worker)
+  {
+    for (AlignCalcListener listener : listeners)
+    {
+      try
+      {
+        listener.workerCancelled(worker);
+      } catch (RuntimeException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  private void notifyExceptional(AlignCalcWorkerI worker,
+          Throwable throwable)
+  {
+    for (AlignCalcListener listener : listeners)
+    {
+      try
+      {
+        listener.workerExceptional(worker, throwable);
+      } catch (RuntimeException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Override
+  public void addAlignCalcListener(AlignCalcListener listener)
+  {
+    listeners.add(listener);
+  }
+
+  @Override
+  public void removeAlignCalcListener(AlignCalcListener listener)
+  {
+    listeners.remove(listener);
+  }
+
+  @Override
+  public void shutdown()
+  {
+    executor.shutdownNow();
+    listeners.clear();
+    registered.clear();
+  }
+
+}
index 0ad8726..16fc467 100644 (file)
@@ -20,7 +20,7 @@
  */
 package jalview.workers;
 
-import jalview.api.AlignCalcManagerI;
+import jalview.api.AlignCalcManagerI2;
 import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
@@ -43,7 +43,7 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
    */
   protected AlignViewportI alignViewport;
 
-  protected AlignCalcManagerI calcMan;
+  protected AlignCalcManagerI2 calcMan;
 
   protected AlignmentViewPanel ap;
 
@@ -61,7 +61,8 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
   {
     if (calcMan != null)
     {
-      calcMan.workerComplete(this);
+      calcMan.cancelWorker(this);
+      calcMan.removeWorker(this);
     }
     alignViewport = null;
     calcMan = null;
@@ -136,4 +137,8 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
     }
   }
 
+  public AlignViewportI getAlignViewport()
+  {
+    return alignViewport;
+  }
 }
index 8f37f15..98e26b3 100644 (file)
@@ -59,63 +59,41 @@ class AnnotationWorker extends AlignCalcWorker
   @Override
   public void run()
   {
-    try
+    if (alignViewport.isClosed())
     {
-      calcMan.notifyStart(this);
-
-      while (!calcMan.notifyWorking(this))
-      {
-        try
-        {
-          Thread.sleep(200);
-        } catch (InterruptedException ex)
-        {
-          ex.printStackTrace();
-        }
-      }
-      if (alignViewport.isClosed())
-      {
-        abortAndDestroy();
-        return;
-      }
+      abortAndDestroy();
+      return;
+    }
 
-      // removeAnnotation();
+    // removeAnnotation();
       AlignmentI alignment = alignViewport.getAlignment();
-      if (alignment != null)
+    if (alignment != null)
+    {
+      try
       {
-        try
+        List<AlignmentAnnotation> anns = counter.calculateAnnotation(
+                alignment, new FeatureRenderer(alignViewport));
+        for (AlignmentAnnotation ann : anns)
         {
-          List<AlignmentAnnotation> anns = counter.calculateAnnotation(
-                  alignment, new FeatureRenderer(alignViewport));
-          for (AlignmentAnnotation ann : anns)
+          AlignmentAnnotation theAnn = alignment.findOrCreateAnnotation(
+                  ann.label, ann.description, false, null, null);
+          theAnn.showAllColLabels = true;
+          theAnn.graph = AlignmentAnnotation.BAR_GRAPH;
+          theAnn.scaleColLabel = true;
+          theAnn.annotations = ann.annotations;
+          setGraphMinMax(theAnn, theAnn.annotations);
+          theAnn.validateRangeAndDisplay();
+          if (!ourAnnots.contains(theAnn))
           {
-            AlignmentAnnotation theAnn = alignment.findOrCreateAnnotation(
-                    ann.label, ann.description, false, null, null);
-            theAnn.showAllColLabels = true;
-            theAnn.graph = AlignmentAnnotation.BAR_GRAPH;
-            theAnn.scaleColLabel = true;
-            theAnn.annotations = ann.annotations;
-            setGraphMinMax(theAnn, theAnn.annotations);
-            theAnn.validateRangeAndDisplay();
-            if (!ourAnnots.contains(theAnn))
-            {
-              ourAnnots.add(theAnn);
-            }
-            // alignment.addAnnotation(ann);
+            ourAnnots.add(theAnn);
           }
-        } catch (IndexOutOfBoundsException x)
-        {
-          // probable race condition. just finish and return without any fuss.
-          return;
+          // alignment.addAnnotation(ann);
         }
+      } catch (IndexOutOfBoundsException x)
+      {
+        // probable race condition. just finish and return without any fuss.
+        return;
       }
-    } catch (OutOfMemoryError error)
-    {
-      ap.raiseOOMWarning("calculating annotations", error);
-      calcMan.disableWorker(this);
-    } finally
-    {
-      calcMan.workerComplete(this);
     }
 
     if (ap != null)
index 74695fe..a711f2b 100644 (file)
@@ -70,44 +70,22 @@ class ColumnCounterSetWorker extends AlignCalcWorker
   public void run()
   {
     boolean annotationAdded = false;
-    try
+    if (alignViewport.isClosed())
     {
-      calcMan.notifyStart(this);
+      abortAndDestroy();
+      return;
+    }
 
-      while (!calcMan.notifyWorking(this))
+    if (alignViewport.getAlignment() != null)
+    {
+      try
       {
-        try
-        {
-          Thread.sleep(200);
-        } catch (InterruptedException ex)
-        {
-          ex.printStackTrace();
-        }
-      }
-      if (alignViewport.isClosed())
+        annotationAdded = computeAnnotations();
+      } catch (IndexOutOfBoundsException x)
       {
-        abortAndDestroy();
+        // probable race condition. just finish and return without any fuss.
         return;
       }
-
-      if (alignViewport.getAlignment() != null)
-      {
-        try
-        {
-          annotationAdded = computeAnnotations();
-        } catch (IndexOutOfBoundsException x)
-        {
-          // probable race condition. just finish and return without any fuss.
-          return;
-        }
-      }
-    } catch (OutOfMemoryError error)
-    {
-      ap.raiseOOMWarning("calculating feature counts", error);
-      calcMan.disableWorker(this);
-    } finally
-    {
-      calcMan.workerComplete(this);
     }
 
     if (ap != null)
index 1a5aaa4..b97c4c5 100644 (file)
@@ -41,71 +41,33 @@ public class ConsensusThread extends AlignCalcWorker
   @Override
   public void run()
   {
-    if (calcMan.isPending(this))
+    AlignmentAnnotation consensus = getConsensusAnnotation();
+    AlignmentAnnotation gap = getGapAnnotation();
+    if ((consensus == null && gap == null))
     {
       return;
     }
-    calcMan.notifyStart(this);
-    // long started = System.currentTimeMillis();
-    try
+    if (alignViewport.isClosed())
     {
-      AlignmentAnnotation consensus = getConsensusAnnotation();
-      AlignmentAnnotation gap = getGapAnnotation();
-      if ((consensus == null && gap == null) || calcMan.isPending(this))
-      {
-        calcMan.workerComplete(this);
-        return;
-      }
-      while (!calcMan.notifyWorking(this))
-      {
-        // System.err.println("Thread
-        // (Consensus"+Thread.currentThread().getName()+") Waiting around.");
-        try
-        {
-          if (ap != null)
-          {
-            ap.paintAlignment(false, false);
-          }
-          Thread.sleep(200);
-        } catch (Exception ex)
-        {
-          ex.printStackTrace();
-        }
-      }
-      if (alignViewport.isClosed())
-      {
-        abortAndDestroy();
-        return;
-      }
-      AlignmentI alignment = alignViewport.getAlignment();
+      abortAndDestroy();
+      return;
+    }
+    AlignmentI alignment = alignViewport.getAlignment();
 
-      int aWidth = -1;
+    int aWidth = -1;
 
-      if (alignment == null || (aWidth = alignment.getWidth()) < 0)
-      {
-        calcMan.workerComplete(this);
-        return;
-      }
+    if (alignment == null || (aWidth = alignment.getWidth()) < 0)
+    {
+      return;
+    }
 
-      eraseConsensus(aWidth);
-      computeConsensus(alignment);
-      updateResultAnnotation(true);
+    eraseConsensus(aWidth);
+    computeConsensus(alignment);
+    updateResultAnnotation(true);
 
-      if (ap != null)
-      {
-        ap.paintAlignment(true, true);
-      }
-    } catch (OutOfMemoryError error)
+    if (ap != null)
     {
-      calcMan.disableWorker(this);
-      ap.raiseOOMWarning("calculating consensus", error);
-    } finally
-    {
-      /*
-       * e.g. ArrayIndexOutOfBoundsException can happen due to a race condition
-       * - alignment was edited at same time as calculation was running
-       */
-      calcMan.workerComplete(this);
+      ap.paintAlignment(true, true);
     }
   }
 
@@ -222,7 +184,6 @@ public class ConsensusThread extends AlignCalcWorker
   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
           ProfilesI hconsensus)
   {
-
     long nseq = getSequences().length;
     AAFrequency.completeConsensus(consensusAnnotation, hconsensus,
             hconsensus.getStartColumn(), hconsensus.getEndColumn() + 1,
index 54b0191..3752841 100644 (file)
@@ -51,69 +51,39 @@ public class ConservationThread extends AlignCalcWorker
   @Override
   public void run()
   {
-    try
+    if ((alignViewport == null) || (calcMan == null)
+            || (alignViewport.isClosed()))
     {
-      calcMan.notifyStart(this); // updatingConservation = true;
-
-      while ((calcMan != null) && (!calcMan.notifyWorking(this)))
-      {
-        try
-        {
-          if (ap != null)
-          {
-            // ap.paintAlignment(false);
-          }
-          Thread.sleep(200);
-        } catch (Exception ex)
-        {
-          ex.printStackTrace();
-        }
-      }
-      if ((alignViewport == null) || (calcMan == null)
-              || (alignViewport.isClosed()))
-      {
-        abortAndDestroy();
-        return;
-      }
-      List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-      AlignmentI alignment = alignViewport.getAlignment();
-      conservation = alignViewport.getAlignmentConservationAnnotation();
-      quality = alignViewport.getAlignmentQualityAnnot();
-      ourAnnot.add(conservation);
-      ourAnnot.add(quality);
-      ourAnnots = ourAnnot;
-      ConsPercGaps = alignViewport.getConsPercGaps();
-      // AlignViewport.UPDATING_CONSERVATION = true;
-
-      if (alignment == null || (alWidth = alignment.getWidth()) < 0)
-      {
-        calcMan.workerComplete(this);
-        // .updatingConservation = false;
-        // AlignViewport.UPDATING_CONSERVATION = false;
+      abortAndDestroy();
+      return;
+    }
+    List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+    AlignmentI alignment = alignViewport.getAlignment();
+    conservation = alignViewport.getAlignmentConservationAnnotation();
+    quality = alignViewport.getAlignmentQualityAnnot();
+    ourAnnot.add(conservation);
+    ourAnnot.add(quality);
+    ourAnnots = ourAnnot;
+    ConsPercGaps = alignViewport.getConsPercGaps();
+    // AlignViewport.UPDATING_CONSERVATION = true;
 
-        return;
-      }
-      try
-      {
-        cons = Conservation.calculateConservation("All",
-                alignment.getSequences(), 0, alWidth - 1, false,
-                ConsPercGaps, quality != null);
-      } catch (IndexOutOfBoundsException x)
-      {
-        // probable race condition. just finish and return without any fuss.
-        calcMan.workerComplete(this);
-        return;
-      }
-      updateResultAnnotation(true);
-    } catch (OutOfMemoryError error)
+    if (alignment == null || (alWidth = alignment.getWidth()) < 0)
     {
-      ap.raiseOOMWarning("calculating conservation", error);
-      calcMan.disableWorker(this);
-      // alignViewport.conservation = null;
-      // this.alignViewport.quality = null;
-
+      // .updatingConservation = false;
+      // AlignViewport.UPDATING_CONSERVATION = false;
+      return;
+    }
+    try
+    {
+      cons = Conservation.calculateConservation("All",
+              alignment.getSequences(), 0, alWidth - 1, false,
+              ConsPercGaps, quality != null);
+    } catch (IndexOutOfBoundsException x)
+    {
+      // probable race condition. just finish and return without any fuss.
+      return;
     }
-    calcMan.workerComplete(this);
+    updateResultAnnotation(true);
 
     if ((alignViewport == null) || (calcMan == null)
             || (alignViewport.isClosed()))
diff --git a/src/jalview/workers/InformationThread.java b/src/jalview/workers/InformationThread.java
new file mode 100644 (file)
index 0000000..c8084b9
--- /dev/null
@@ -0,0 +1,284 @@
+package jalview.workers;
+
+import jalview.analysis.AAFrequency;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class calculates HMM Information Content annotations, based on any HMM
+ * consensus sequences and their HMM models. HMM consensus sequences may be
+ * present for the whole alignment, or subgroups of it.
+ *
+ */
+public class InformationThread extends AlignCalcWorker
+{
+  public static final String HMM_CALC_ID = "HMM";
+
+  /**
+   * Constructor
+   * 
+   * @param alignViewport
+   * @param alignPanel
+   */
+  public InformationThread(AlignViewportI alignViewport,
+          AlignmentViewPanel alignPanel)
+  {
+    super(alignViewport, alignPanel);
+  }
+
+  /**
+   * Recomputes Information annotations for any HMM consensus sequences (for
+   * alignment and/or groups)
+   */
+  @Override
+  public void run()
+  {
+    if (alignViewport.getAlignment().getHmmSequences().isEmpty())
+    {
+      return;
+    }
+    if (alignViewport.isClosed())
+    {
+      abortAndDestroy();
+      return;
+    }
+
+    AlignmentI alignment = alignViewport.getAlignment();
+    int aWidth = alignment == null ? -1 : alignment.getWidth();
+    if (aWidth < 0)
+    {
+      return;
+    }
+
+    /*
+     * compute information profiles for any HMM consensus sequences
+     * for the alignment or sub-groups
+     */
+    computeProfiles(alignment);
+
+    /*
+     * construct the corresponding annotations
+     */
+    updateAnnotation();
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true, true);
+    }
+  }
+
+  /**
+   * Computes HMM profiles for any HMM consensus sequences (for alignment or
+   * subgroups). Any alignment profile computed is stored on the viewport, any
+   * group profile computed is stored on the respective sequence group.
+   * 
+   * @param alignment
+   * @see AlignViewportI#setHmmProfiles(ProfilesI)
+   */
+  protected void computeProfiles(AlignmentI alignment)
+  {
+    int width = alignment.getWidth();
+
+    /*
+     * alignment HMM profile
+     */
+    List<SequenceI> seqs = alignment.getHmmSequences();
+    if (!seqs.isEmpty())
+    {
+      HiddenMarkovModel hmm = seqs.get(0).getHMM();
+      ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
+              0, width, alignViewport.isIgnoreBelowBackground(),
+              alignViewport.isInfoLetterHeight());
+      alignViewport.setHmmProfiles(hmmProfiles);
+    }
+
+    /*
+     * group HMM profiles
+     */
+    List<SequenceGroup> groups = alignment.getGroups();
+    for (SequenceGroup group : groups)
+    {
+      seqs = group.getHmmSequences();
+      if (!seqs.isEmpty())
+      {
+        HiddenMarkovModel hmm = seqs.get(0).getHMM();
+        ProfilesI hmmProfiles = AAFrequency.calculateHMMProfiles(hmm, width,
+                0, width, group.isIgnoreBelowBackground(),
+                group.isUseInfoLetterHeight());
+        group.setHmmProfiles(hmmProfiles);
+      }
+    }
+  }
+
+  /**
+   * gets the sequences on the alignment on the viewport.
+   * 
+   * @return
+   */
+  protected SequenceI[] getSequences()
+  {
+    return alignViewport.getAlignment().getSequencesArray();
+  }
+
+  /**
+   * Get the Gap annotation for the alignment
+   * 
+   * @return
+   */
+  protected AlignmentAnnotation getGapAnnotation()
+  {
+               return alignViewport.getAlignmentGapAnnotation();
+  }
+
+  /**
+   * Computes Information Content annotation for any HMM consensus sequences
+   * (for alignment or groups), and updates (or adds) the annotation to the
+   * sequence and the alignment
+   */
+  @Override
+  public void updateAnnotation()
+  {
+    AlignmentI alignment = alignViewport.getAlignment();
+
+    float maxInformation = 0f;
+    List<AlignmentAnnotation> infos = new ArrayList<>();
+
+    /*
+     * annotation for alignment HMM consensus if present
+     */
+    List<SequenceI> hmmSeqs = alignment.getHmmSequences();
+    if (!hmmSeqs.isEmpty())
+    {
+      ProfilesI profile = alignViewport.getHmmProfiles();
+      float m = updateInformationAnnotation(hmmSeqs.get(0), profile, null,
+              infos);
+      maxInformation = Math.max(maxInformation, m);
+    }
+
+    /*
+     * annotation for group HMM consensus if present
+     */
+    for (SequenceGroup group : alignment.getGroups())
+    {
+      hmmSeqs = group.getHmmSequences();
+      if (!hmmSeqs.isEmpty())
+      {
+        ProfilesI profiles = group.getHmmProfiles();
+        float m = updateInformationAnnotation(hmmSeqs.get(0), profiles,
+                group, infos);
+        maxInformation = Math.max(maxInformation, m);
+      }
+    }
+
+    /*
+     * maxInformation holds the maximum value of information score;
+     * set this as graphMax in all annotations to scale them all the same
+     */
+    for (AlignmentAnnotation ann : infos)
+    {
+      ann.graphMax = maxInformation;
+    }
+  }
+
+  /**
+   * Updates (and first constructs if necessary) an HMM Profile information
+   * content annotation for a sequence. The <code>group</code> argument is null
+   * for the whole alignment annotation, not null for a subgroup annotation. The
+   * updated annotation is added to the <code>infos</code> list. Answers the
+   * maximum information content value of any annotation (for use as a scaling
+   * factor for display).
+   * 
+   * @param seq
+   * @param profile
+   * @param group
+   * @param infos
+   * @return
+   */
+  protected float updateInformationAnnotation(SequenceI seq,
+          ProfilesI profile, SequenceGroup group,
+          List<AlignmentAnnotation> infos)
+  {
+    if (seq == null || profile == null)
+    {
+      return 0f;
+    }
+
+    AlignmentAnnotation ann = findOrCreateAnnotation(seq, group);
+
+    seq.addAlignmentAnnotation(ann);
+    infos.add(ann);
+
+    float max = AAFrequency.completeInformation(ann, profile,
+            profile.getStartColumn(), profile.getEndColumn() + 1);
+
+    return max;
+  }
+
+  /**
+   * A helper method that first searches for the HMM annotation that matches the
+   * group reference (null for the whole alignment annotation). If found, its
+   * sequence reference is updated to the given sequence (the recomputed HMM
+   * consensus sequence). If not found, it is created. This supports both
+   * creating the annotation the first time hmmbuild is run, and updating it if
+   * hmmbuild is re-run.
+   * 
+   * @param seq
+   * @param group
+   * @return
+   */
+  AlignmentAnnotation findOrCreateAnnotation(SequenceI seq,
+          SequenceGroup group)
+  {
+    /*
+     * can't use Alignment.findOrCreateAnnotation here because we 
+     * want to update, rather than match on, the sequence ref
+     */
+    AlignmentAnnotation info = null;
+
+    AlignmentI alignment = alignViewport.getAlignment();
+    AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+    if (anns != null)
+    {
+      for (AlignmentAnnotation ann : anns)
+      {
+        if (HMM_CALC_ID.equals(ann.getCalcId()) && group == ann.groupRef)
+        {
+          info = ann;
+          info.setSequenceRef(seq);
+          info.label = seq.getName(); // in case group name changed!
+          break;
+        }
+      }
+    }
+
+    if (info == null)
+    {
+      int aWidth = alignment.getWidth();
+      String desc = MessageManager
+              .getString("label.information_description");
+      float graphMax = 6.52f; // todo where does this value derive from?
+      info = new AlignmentAnnotation(seq.getName(), desc,
+              new Annotation[aWidth], 0f, graphMax,
+              AlignmentAnnotation.BAR_GRAPH);
+      info.setCalcId(HMM_CALC_ID);
+      info.setSequenceRef(seq);
+      info.groupRef = group;
+      info.hasText = true;
+      alignment.addAnnotation(info);
+    }
+
+    return info;
+  }
+}
index 61ec3d0..3cb6114 100644 (file)
@@ -47,28 +47,6 @@ public class StrucConsensusThread extends AlignCalcWorker
   @Override
   public void run()
   {
-    try
-    {
-      if (calcMan.isPending(this))
-      {
-        return;
-      }
-      calcMan.notifyStart(this);
-      while (!calcMan.notifyWorking(this))
-      {
-        try
-        {
-          if (ap != null)
-          {
-            // ap.paintAlignment(false);
-          }
-
-          Thread.sleep(200);
-        } catch (Exception ex)
-        {
-          ex.printStackTrace();
-        }
-      }
       if (alignViewport.isClosed())
       {
         abortAndDestroy();
@@ -80,7 +58,6 @@ public class StrucConsensusThread extends AlignCalcWorker
 
       if (alignment == null || (aWidth = alignment.getWidth()) < 0)
       {
-        calcMan.workerComplete(this);
         return;
       }
       strucConsensus = alignViewport.getAlignmentStrucConsensusAnnotation();
@@ -109,7 +86,6 @@ public class StrucConsensusThread extends AlignCalcWorker
 
       if (rnaStruc == null || !rnaStruc.isValidStruc())
       {
-        calcMan.workerComplete(this);
         return;
       }
 
@@ -121,27 +97,11 @@ public class StrucConsensusThread extends AlignCalcWorker
                 alignment.getWidth(), hStrucConsensus, true, rnaStruc);
       } catch (ArrayIndexOutOfBoundsException x)
       {
-        calcMan.workerComplete(this);
         return;
       }
       alignViewport.setRnaStructureConsensusHash(hStrucConsensus);
       // TODO AlignmentAnnotation rnaStruc!!!
       updateResultAnnotation(true);
-    } catch (OutOfMemoryError error)
-    {
-      calcMan.disableWorker(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      ap.raiseOOMWarning("calculating RNA structure consensus", error);
-    } finally
-    {
-      calcMan.workerComplete(this);
-      if (ap != null)
-      {
-        ap.paintAlignment(true, true);
-      }
-    }
 
   }
 
index 6746d1a..021382c 100644 (file)
@@ -34,8 +34,16 @@ import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import java.util.ArrayList;
 import java.util.List;
 
-public abstract class AWSThread extends Thread
+import static java.lang.String.format;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Timer;
+
+public abstract class AWSThread
 {
+  
+  private javax.swing.Timer timer;
 
   /**
    * view that this job was associated with
@@ -64,13 +72,15 @@ public abstract class AWSThread extends Thread
 
   /**
    * are there jobs still running in this thread.
+   *
+   * fixme: initialize with an empty array?
    */
   protected boolean jobComplete = false;
 
   /**
    * one or more jobs being managed by this thread.
    */
-  protected AWsJob jobs[] = null;
+  protected AWsJob[] jobs = null;
 
   /**
    * full name of service
@@ -95,113 +105,96 @@ public abstract class AWSThread extends Thread
    */
   private AlignFrame alignFrame;
 
-  /**
-   * generic web service job/subjob poll loop
-   */
-  @Override
-  public void run()
+  public void start()
   {
-    JobStateSummary jstate = null;
     if (jobs == null)
     {
       jobComplete = true;
+      Console.debug(
+          "WebServiceJob poll loop finished with no jobs created.");
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.appendProgressText(
+          MessageManager.getString("info.no_jobs_ran"));
+      wsInfo.setFinishedNoResults();
+      return;
     }
-    while (!jobComplete)
+    timer = new Timer(5000, new ActionListener()
     {
-      jstate = new JobStateSummary();
-      for (int j = 0; j < jobs.length; j++)
+      
+      @Override
+      public void actionPerformed(ActionEvent e)
       {
 
-        if (!jobs[j].submitted && jobs[j].hasValidInput())
-        {
-          StartJob(jobs[j]);
-        }
-
-        if (jobs[j].submitted && !jobs[j].subjobComplete)
+        JobStateSummary jstate = new JobStateSummary();
+        for (final AWsJob job : jobs)
         {
-          try
+          if (!job.submitted && job.hasValidInput())
           {
-            pollJob(jobs[j]);
-            if (!jobs[j].hasResponse())
-            {
-              throw (new Exception(
-                      "Timed out when communicating with server\nTry again later.\n"));
-            }
-            Console.debug("Job " + j + " Result state " + jobs[j].getState()
-                    + "(ServerError=" + jobs[j].isServerError() + ")");
-          } catch (Exception ex)
+            StartJob(job);
+          }
+          Console.debug(format(
+                  "Job %s is %ssubmitted", job, job.submitted ? "" : "not "));
+          if (job.submitted && !job.subjobComplete)
           {
-            // Deal with Transaction exceptions
-            wsInfo.appendProgressText(jobs[j].jobnum, MessageManager
-                    .formatMessage("info.server_exception", new Object[]
-                    { WebServiceName, ex.getMessage() }));
-            // always output the exception's stack trace to the log
-            Console.warn(WebServiceName + " job(" + jobs[j].jobnum
-                    + ") Server exception.");
-            // todo: could limit trace to cause if this is a SOAPFaultException.
-            ex.printStackTrace();
-
-            if (jobs[j].allowedServerExceptions > 0)
+            Console.debug(format(
+                    "Polling Job %s Result state was:%s(ServerError=%b)",
+                    job, job.getState(), job.isServerError()));
+            try
+            {
+              pollJob(job);
+              if (!job.hasResponse())
+                throw new Exception("Timed out when communicating with server. Try again later.");
+              else
+                Console.debug(format("Job %s Result state:%s(ServerError=%b)",
+                        job, job.getState(), job.isServerError()));
+            } catch (Exception exc)
             {
-              jobs[j].allowedServerExceptions--;
-              Console.debug("Sleeping after a server exception.");
-              try
+              // Deal with Transaction exceptions
+              wsInfo.appendProgressText(job.jobnum, MessageManager
+                      .formatMessage("info.server_exception", WebServiceName,
+                          exc.getMessage()));
+              // always output the exception's stack trace to the log
+              Console.warn(format("%s job(%s) Server exception.",
+                      WebServiceName, job.jobnum));
+              exc.printStackTrace();
+              
+              if (job.allowedServerExceptions > 0)
               {
-                Thread.sleep(5000);
-              } catch (InterruptedException ex1)
+                job.allowedServerExceptions--;
+              }
+              else
               {
+                Console.warn(format("Dropping job %s %s", job, job.jobId));
+                job.subjobComplete = true;
+                wsInfo.setStatus(job.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
               }
-            }
-            else
+            } catch (OutOfMemoryError oomerror)
             {
-              Console.warn("Dropping job " + j + " " + jobs[j].jobId);
-              jobs[j].subjobComplete = true;
-              wsInfo.setStatus(jobs[j].jobnum,
-                      WebserviceInfo.STATE_STOPPED_SERVERERROR);
+              jobComplete = true;
+              job.subjobComplete = true;
+              job.clearResponse();
+              wsInfo.setStatus(job.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
+              Console.error(format("Out of memory when retrieving Job %s id:%s/%s",
+                      job, WsUrl, job.jobId), oomerror);
+              new jalview.gui.OOMWarning("retrieving result for " + WebServiceName, oomerror);
+              System.gc();
             }
-          } catch (OutOfMemoryError er)
-          {
-            jobComplete = true;
-            jobs[j].subjobComplete = true;
-            jobs[j].clearResponse(); // may contain out of date result data
-            wsInfo.setStatus(jobs[j].jobnum,
-                    WebserviceInfo.STATE_STOPPED_ERROR);
-            Console.error("Out of memory when retrieving Job " + j + " id:"
-                    + WsUrl + "/" + jobs[j].jobId, er);
-            new jalview.gui.OOMWarning(
-                    "retrieving result for " + WebServiceName, er);
-            System.gc();
           }
+          jstate.updateJobPanelState(wsInfo, OutputHeader, job);
         }
-        jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
-      }
-      // Decide on overall state based on collected jobs[] states
-      updateGlobalStatus(jstate);
-      if (!jobComplete)
-      {
-        try
+        // Decide on overall state based on collected jobs[] states
+        updateGlobalStatus(jstate);
+        if (jobComplete)
         {
-          Thread.sleep(5000);
-        } catch (InterruptedException e)
-        {
-          Console.debug("Interrupted sleep waiting for next job poll.", e);
+          timer.stop();
+          // jobs should never be null at this point
+          parseResult(); // tidy up and make results available to user
+          
         }
-        // System.out.println("I'm alive "+alTitle);
       }
-    }
-    if (jobComplete && jobs != null)
-    {
-      parseResult(); // tidy up and make results available to user
-    }
-    else
-    {
-      Console.debug(
-              "WebServiceJob poll loop finished with no jobs created.");
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.appendProgressText(
-              MessageManager.getString("info.no_jobs_ran"));
-      wsInfo.setFinishedNoResults();
-    }
+    });
+    timer.setInitialDelay(0);
+    timer.start();
   }
 
   protected void updateGlobalStatus(JobStateSummary jstate)
@@ -232,39 +225,10 @@ public abstract class AWSThread extends Thread
     }
   }
 
-  public AWSThread()
-  {
-    super();
-  }
-
-  public AWSThread(Runnable target)
-  {
-    super(target);
-  }
-
-  public AWSThread(String name)
-  {
-    super(name);
-  }
-
-  public AWSThread(ThreadGroup group, Runnable target)
+  
+  public void interrupt()
   {
-    super(group, target);
-  }
-
-  public AWSThread(ThreadGroup group, String name)
-  {
-    super(group, name);
-  }
-
-  public AWSThread(Runnable target, String name)
-  {
-    super(target, name);
-  }
-
-  public AWSThread(ThreadGroup group, Runnable target, String name)
-  {
-    super(group, target, name);
+    timer.stop();
   }
 
   /**
@@ -317,11 +281,6 @@ public abstract class AWSThread extends Thread
     }
   }
 
-  public AWSThread(ThreadGroup group, Runnable target, String name,
-          long stackSize)
-  {
-    super(group, target, name, stackSize);
-  }
 
   /**
    * 
@@ -336,7 +295,7 @@ public abstract class AWSThread extends Thread
    * 
    * @param alignFrame
    *          reference for copying mappings across
-   * @param wsInfo
+   * @param wsinfo
    *          gui attachment point
    * @param input
    *          input data for the calculation
@@ -381,7 +340,7 @@ public abstract class AWSThread extends Thread
               .getCodonFrames();
       if (cf != null)
       {
-        codonframe = new ArrayList<AlignedCodonFrame>();
+        codonframe = new ArrayList<>();
         codonframe.addAll(cf);
       }
     }
index f5f9377..ed88ea9 100644 (file)
  */
 package jalview.ws;
 
+import jalview.analysis.SeqsetUtils.SequenceInfo;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Generic properties for an individual job within a Web Service Client thread.
  * Derived from jalview web services version 1 statuses, and revised for Jws2.
@@ -145,6 +153,12 @@ public abstract class AWsJob
    */
   protected boolean subjobComplete = false;
 
+  protected WsParamSetI preset = null;
+
+  protected List<ArgumentI> arguments = null;
+
+  protected Hashtable<String, SequenceInfo> SeqNames = new Hashtable();
+
   public AWsJob()
   {
   }
@@ -227,4 +241,47 @@ public abstract class AWsJob
     String state = "";
     return state;
   }
+
+  public void setPreset(WsParamSetI jobpreset)
+  {
+    preset = jobpreset;
+  }
+
+  public void setArguments(List<ArgumentI> paramset)
+  {
+    arguments = paramset;
+
+  }
+
+  public boolean isPresetJob()
+  {
+    return preset!=null && arguments==null; 
+  }
+
+  public List<ArgumentI> getArguments()
+  {
+    return arguments;
+  }
+
+  public WsParamSetI getPreset()
+  {
+    return preset;
+  }
+
+  long nextChunk = 0;
+
+  /**
+   * update the record of the last position in the log file read for this job
+   * 
+   * @param nextChunk
+   */
+  public void setnextChunk(long nextChunk)
+  {
+    this.nextChunk = nextChunk;
+  }
+
+  public long getNextChunk()
+  {
+    return nextChunk;
+  }
 }
index 6b86775..478472b 100644 (file)
@@ -26,6 +26,9 @@ import jalview.gui.WebserviceInfo;
  * bookkeeper class for the WebServiceInfo GUI, maintaining records of web
  * service jobs handled by the window and reflecting any status updates.
  * 
+ * TODO: separate from the GUI cleanly since it also holds logic for
+ * interpreting failure states
+ * 
  * @author JimP
  * 
  */
index 48aafca..35a237e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.ext.ensembl.EnsemblGene;
 import jalview.ws.dbsources.EmblCdsSource;
 import jalview.ws.dbsources.EmblSource;
@@ -40,8 +42,39 @@ import java.util.List;
  * This implements the run-time discovery of sequence database clients.
  * 
  */
-public class SequenceFetcher extends ASequenceFetcher
+public class SequenceFetcher extends ASequenceFetcher implements ApplicationSingletonI
 {
+  /*
+   * set a mock fetcher here for testing only - reset to null afterwards
+   */
+  private static SequenceFetcher mockFetcher;
+
+  /**
+   * Set the instance object to use (intended for unit testing with mock
+   * objects).
+   * 
+   * Be sure to reset to null in the tearDown method of any tests!
+   * 
+   * @param sf
+   */
+  public static void setMockFetcher(SequenceFetcher sf)
+  {
+    mockFetcher = sf;
+  }
+  
+  /**
+   * Returns a new SequenceFetcher singleton, or a mock object if one has been
+   * set.
+   * 
+   * @return
+   */
+  public static SequenceFetcher getInstance()
+  {
+    return mockFetcher != null ? mockFetcher
+            : (SequenceFetcher) ApplicationSingletonProvider
+                    .getInstance(SequenceFetcher.class);
+  }
+
   /**
    * Thread safe construction of database proxies TODO: extend to a configurable
    * database plugin mechanism where classes are instantiated by reflection and
diff --git a/src/jalview/ws/SequenceFetcherFactory.java b/src/jalview/ws/SequenceFetcherFactory.java
deleted file mode 100644 (file)
index 9cc4960..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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;
-
-import jalview.ws.seqfetcher.ASequenceFetcher;
-
-public class SequenceFetcherFactory
-{
-
-  private static SequenceFetcher instance;
-
-  /**
-   * Returns a new SequenceFetcher object, or a mock object if one has been set
-   * 
-   * @return
-   */
-  public static ASequenceFetcher getSequenceFetcher()
-  {
-    return instance == null ? new SequenceFetcher() : instance;
-  }
-
-  /**
-   * Set the instance object to use (intended for unit testing with mock
-   * objects).
-   * 
-   * Be sure to reset to null in the tearDown method of any tests!
-   * 
-   * @param sf
-   */
-  public static void setSequenceFetcher(SequenceFetcher sf)
-  {
-    instance = sf;
-  }
-}
diff --git a/src/jalview/ws/ServiceChangeListener.java b/src/jalview/ws/ServiceChangeListener.java
new file mode 100644 (file)
index 0000000..98e15b9
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws;
+
+import java.util.Collection;
+import java.util.EventListener;
+
+import jalview.ws.api.ServiceWithParameters;
+
+@FunctionalInterface
+public interface ServiceChangeListener extends EventListener
+{
+  public void servicesChanged(WSDiscovererI discoverer,
+          Collection<? extends ServiceWithParameters> services);
+}
index 0f5cee8..a68f3f3 100755 (executable)
  */
 package jalview.ws;
 
+import jalview.bin.Cache;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.WebserviceInfo;
+import jalview.gui.WsJobParameters;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.api.UIinfo;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
 
 public abstract class WSClient // implements WSMenuEntryProviderI
 {
@@ -56,6 +70,11 @@ public abstract class WSClient // implements WSMenuEntryProviderI
   protected WebserviceInfo wsInfo;
 
   /**
+   * the root object for the service client
+   */
+  protected UIinfo serviceHandle;
+
+  /**
    * total number of jobs managed by this web service client instance.
    */
   int jobsRunning = 0;
@@ -65,13 +84,121 @@ public abstract class WSClient // implements WSMenuEntryProviderI
    * mappings between abstract interface names and menu entries
    */
   protected java.util.Hashtable ServiceActions;
+
+  /**
+   * alignFrame associated with this client
+   */
+  protected AlignFrame alignFrame;
   {
     ServiceActions = new java.util.Hashtable();
     ServiceActions.put("MsaWS", "Multiple Sequence Alignment");
     ServiceActions.put("SecStrPred", "Secondary Structure Prediction");
   };
 
+  /**
+   * The preset for the job executed by this client (may be null)
+   */
+  protected WsParamSetI preset;
+
+  /**
+   * The parameters for the job executed by this client (may be null)
+   */
+  protected List<ArgumentI> paramset;
+
   public WSClient()
   {
   }
+
+  /**
+   * base constructor for a web service with parameters. Extending classes
+   * should implement this constructor with additional logic to verify that
+   * preset and arguments are compatible with the service being configured.
+   * 
+   * @param _alignFrame
+   * @param preset
+   * @param arguments
+   */
+  public WSClient(AlignFrame _alignFrame, WsParamSetI preset,
+      List<ArgumentI> arguments)
+  {
+    alignFrame = _alignFrame;
+    this.preset = preset;
+    this.paramset = arguments;
+  }
+
+  protected WebserviceInfo setWebService(UIinfo serv, boolean b)
+  {
+    WebServiceName = serv.getName();
+    WebServiceJobTitle = serv.getActionText();
+    WsURL = serv.getHostURL();
+    if (!b)
+    {
+      return new WebserviceInfo(WebServiceJobTitle,
+          WebServiceJobTitle + " using service hosted at "
+              + WsURL + "\n"
+              + (serv.getDescription() != null
+                  ? serv.getDescription()
+                  : ""),
+          false);
+    }
+    return null;
+  }
+
+  /**
+   * called to open a parameter editing dialog for parameterised services
+   * 
+   * @param sh
+   * @param editParams
+   * @return
+   */
+  protected CompletionStage<Boolean> processParams(ServiceWithParameters sh,
+      boolean editParams)
+  {
+    return processParams(sh, editParams, false);
+  }
+
+  protected CompletionStage<Boolean> processParams(ServiceWithParameters sh,
+      boolean editParams, boolean adjustingExisting)
+  {
+
+    if (editParams)
+    {
+      // always do this
+      sh.initParamStore(Desktop.getUserParameterStore());
+
+      WsJobParameters jobParams = (preset == null && paramset != null
+          && paramset.size() > 0)
+              ? new WsJobParameters((ParamDatastoreI) null, sh,
+                  (WsParamSetI) null, paramset)
+              : new WsJobParameters((ParamDatastoreI) null, sh,
+                  preset, (List<ArgumentI>) null);
+      if (adjustingExisting)
+      {
+        jobParams.setName(MessageManager
+            .getString("label.adjusting_parameters_for_calculation"));
+      }
+      var stage = jobParams.showRunDialog();
+      return stage.thenApply((startJob) -> {
+        if (startJob)
+        {
+          WsParamSetI prset = jobParams.getPreset();
+          if (prset == null)
+          {
+            paramset = jobParams.isServiceDefaults() ? null
+                : jobParams.getJobParams();
+            this.preset = null;
+          }
+          else
+          {
+            this.preset = prset; // ((JabaPreset) prset).p;
+            paramset = null; // no user supplied parameters.
+          }
+        }
+        return startJob;
+      });
+
+    }
+    return CompletableFuture.completedFuture(true);
+  }
+
 }
diff --git a/src/jalview/ws/WSDiscovererI.java b/src/jalview/ws/WSDiscovererI.java
new file mode 100644 (file)
index 0000000..e9eedd3
--- /dev/null
@@ -0,0 +1,37 @@
+package jalview.ws;
+
+import jalview.ws.api.ServiceWithParameters;
+
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+public interface WSDiscovererI
+{
+  public static final int STATUS_OK = 1;
+  public static final int STATUS_NO_SERVICES = 0;
+  public static final int STATUS_INVALID = -1;
+  public static final int STATUS_UNKNOWN = -2;
+
+  public void setServiceUrls(List<String> wsUrls);
+
+  public List<String> getServiceUrls();
+
+  public List<ServiceWithParameters> getServices();
+
+  public boolean testServiceUrl(URL url);
+
+  public int getServerStatusFor(String url);
+  
+  public void addServiceChangeListener(ServiceChangeListener listener);
+  
+  public void removeServiceChangeListener(ServiceChangeListener listener);
+
+  public CompletableFuture<WSDiscovererI> startDiscoverer();
+
+  public String getErrorMessages();
+
+  public boolean hasServices();
+
+  public boolean isRunning();
+}
diff --git a/src/jalview/ws/api/CancellableI.java b/src/jalview/ws/api/CancellableI.java
new file mode 100644 (file)
index 0000000..adef2b9
--- /dev/null
@@ -0,0 +1,8 @@
+package jalview.ws.api;
+
+import jalview.ws.gui.WsJob;
+
+public interface CancellableI
+{
+  public boolean cancel(WsJob job);
+}
diff --git a/src/jalview/ws/api/DistanceMatrixResultI.java b/src/jalview/ws/api/DistanceMatrixResultI.java
new file mode 100644 (file)
index 0000000..a40d256
--- /dev/null
@@ -0,0 +1,16 @@
+package jalview.ws.api;
+
+import jalview.math.MatrixI;
+import jalview.ws.params.InvalidArgumentException;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+
+public interface DistanceMatrixResultI
+{
+
+  public MatrixI getDistanceMatrixFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError;
+
+
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/JalviewServiceEndpointProviderI.java b/src/jalview/ws/api/JalviewServiceEndpointProviderI.java
new file mode 100644 (file)
index 0000000..b9b5109
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws.api;
+
+public interface JalviewServiceEndpointProviderI
+{
+
+  /**
+   * 
+   * @return endpoint instance implementing one or more jalview.ws.api
+   *         interfaces
+   */
+  Object getEndpoint();
+
+}
diff --git a/src/jalview/ws/api/JalviewWebServiceI.java b/src/jalview/ws/api/JalviewWebServiceI.java
new file mode 100644 (file)
index 0000000..6dda359
--- /dev/null
@@ -0,0 +1,27 @@
+package jalview.ws.api;
+
+import jalview.gui.WebserviceInfo;
+import jalview.ws.gui.WsJob;
+
+public interface JalviewWebServiceI
+{
+
+  void updateStatus(WsJob job);
+
+  /**
+   * Retrieve any additional log information for the job and update WsJob's
+   * progress. Results won't be retrieved until progress updates have finished.
+   * 
+   * @param job
+   * @return true if Job's stdout/progress panel was added to
+   * @throws Exception
+   */
+  boolean updateJobProgress(WsJob job) throws Exception;
+
+  boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
+          throws Exception, Error;
+
+  boolean handleCollectionException(Exception e, WsJob msjob,
+          WebserviceInfo wsInfo);
+
+}
diff --git a/src/jalview/ws/api/JobId.java b/src/jalview/ws/api/JobId.java
new file mode 100644 (file)
index 0000000..5c3b51d
--- /dev/null
@@ -0,0 +1,67 @@
+package jalview.ws.api;
+
+import java.util.Date;
+
+public class JobId
+{
+  // TODO: JobId could include sequenceI anonymisation stuff
+  // TODO: getProgress() -> input stream to log file for job.
+  private String serviceType;
+
+  private String serviceImpl;
+
+  private String jobId;
+  // TODO: java2script instant
+  private String creationTime=null;
+
+  public JobId(String serviceType, String serviceImpl, String id)
+  {
+    this.serviceType = serviceType;
+    this.serviceImpl = serviceImpl;
+    jobId = id;
+    /*
+     * @j2sIgnore
+     */
+    {
+//      creationTime = Date.from(Instant.now()).toString();
+    }
+    if (creationTime==null)
+    {
+      creationTime = new Date().toString(); // j2s only
+    }
+  }
+
+  @Override
+  public String toString()
+  {
+    return "" + serviceType + ":" + serviceImpl + ":" + jobId + "\nCreated "
+            + creationTime;
+  }
+  /**
+   * a stringified version of the Job Id that can be saved in project.
+   */
+  public String getURI()
+  {
+    return jobId;
+  }
+
+  public String getServiceType()
+  {
+    return serviceType;
+  }
+
+  public String getServiceImpl()
+  {
+    return serviceImpl;
+  }
+
+  public String getJobId()
+  {
+    return jobId;
+  }
+
+  public String getCreationTime()
+  {
+    return creationTime;
+  }
+}
diff --git a/src/jalview/ws/api/MsaI.java b/src/jalview/ws/api/MsaI.java
new file mode 100644 (file)
index 0000000..3c426c0
--- /dev/null
@@ -0,0 +1,36 @@
+package jalview.ws.api;
+
+import jalview.datamodel.SequenceI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+
+/**
+ * MSA analysis interface
+ * 
+ * @author jprocter
+ * 
+ *         Generic job submission/management model: - A service instance
+ *         implements one or more analysis interfaces, a status interface, a
+ *         progress interface, and one or more results interface, plus any
+ *         informational/descriptional interfaces - analysis interfaces return
+ *         JobId or throw exceptions/errors.
+ * 
+ *
+ */
+public interface MsaI
+{
+  /**
+   * Given a set of sequences
+   * 
+   * @param toalign
+   * @param parameters
+   * @param list
+   * @return JobId or exceptions are thrown.
+   * @throws Throwable
+   */
+  public JobId align(List<SequenceI> toalign, WsParamSetI parameters,
+          List<ArgumentI> list)
+          throws Throwable;
+}
diff --git a/src/jalview/ws/api/MsaResultI.java b/src/jalview/ws/api/MsaResultI.java
new file mode 100644 (file)
index 0000000..38c0a0a
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws.api;
+
+import jalview.datamodel.AlignmentI;
+import jalview.ws.params.InvalidArgumentException;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+
+public interface MsaResultI
+{
+  public AlignmentI getAlignmentFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError;
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/MsaWithGuideTreeI.java b/src/jalview/ws/api/MsaWithGuideTreeI.java
new file mode 100644 (file)
index 0000000..6c2ff53
--- /dev/null
@@ -0,0 +1,24 @@
+package jalview.ws.api;
+
+import jalview.analysis.NJTree;
+import jalview.datamodel.SequenceI;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.WsParamSetI;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+import java.util.List;
+
+public interface MsaWithGuideTreeI
+{
+  /**
+   * Given a set of sequences
+   * 
+   * @param toalign
+   * @param parameters
+   * @return JobId or exceptions are thrown.
+   */
+  public JobId align(List<SequenceI> toalign, NJTree guideTree,
+          WsParamSetI parameters)
+          throws InvalidArgumentException, ServerError, IOError;
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/MultipleSequenceAlignmentI.java b/src/jalview/ws/api/MultipleSequenceAlignmentI.java
new file mode 100644 (file)
index 0000000..10b8383
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws.api;
+
+/**
+ * A simple parameterisable multiple sequence alignment service
+ * 
+ * @author jprocter
+ *
+ */
+public interface MultipleSequenceAlignmentI
+        extends JalviewWebServiceI, MsaI, MsaResultI
+{
+
+}
diff --git a/src/jalview/ws/api/SequenceAnnotationServiceI.java b/src/jalview/ws/api/SequenceAnnotationServiceI.java
new file mode 100644 (file)
index 0000000..9ca2d31
--- /dev/null
@@ -0,0 +1,51 @@
+package jalview.ws.api;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SequenceAnnotationServiceI extends JalviewWebServiceI
+{
+
+
+  /**
+   * submit sequences to service with no parameters, or preset or parameter set.
+   * Nb- almost the same as the 'align' method in the Msa service :)
+   * 
+   * @param seqs
+   * @param preset
+   * @param paramset
+   * @return
+   * @throws Throwable
+   */
+  JobId submitToService(List<SequenceI> seqs, WsParamSetI preset,
+          List<ArgumentI> paramset) throws Throwable;
+
+  /**
+   * materialise annotation and features for sequences input to the service
+   * 
+   * @param job
+   * @param seqs
+   *          - features and alignment annotation added to these will be
+   *          imported to the dataset for the alignment
+   * @param featureColours
+   *          - container for feature colours - any defined will be merged with
+   *          viewport
+   * @param featureFilters
+   *          - container for filters - any defined will be merged with viewport
+   * @return sequence and alignment annotation rows that should be made
+   *         visible/updated on alignment
+   * @throws Throwable
+   */
+  List<AlignmentAnnotation> getAnnotationResult(JobId job,
+          List<SequenceI> seqs, Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters) throws Throwable;
+  
+
+}
diff --git a/src/jalview/ws/api/ServiceWithParameters.java b/src/jalview/ws/api/ServiceWithParameters.java
new file mode 100644 (file)
index 0000000..be24716
--- /dev/null
@@ -0,0 +1,112 @@
+package jalview.ws.api;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.gui.AlignFrame;
+import jalview.ws.jws2.MsaWSClient;
+import jalview.ws.jws2.SequenceAnnotationWSClient;
+import jalview.ws.params.ParamManager;
+
+import javax.swing.JMenu;
+
+public abstract class ServiceWithParameters extends UIinfo
+{
+
+  protected jalview.ws.uimodel.AlignAnalysisUIText aaui;
+
+  public ServiceWithParameters(String serviceType, String action,
+          String name, String description, String hosturl)
+  {
+    super(serviceType, action, name, description, hosturl);
+  }
+
+  public abstract void initParamStore(ParamManager userParameterStore);
+
+  public jalview.ws.uimodel.AlignAnalysisUIText getAlignAnalysisUI()
+  {
+    return aaui;
+  }
+
+  public void setAlignAnalysisUI(
+          jalview.ws.uimodel.AlignAnalysisUIText aaui)
+  {
+    this.aaui = aaui;
+  }
+
+  public boolean isInteractiveUpdate()
+  {
+    return aaui != null && aaui.isAA();
+  }
+  // config flags for SeqAnnotationServiceCalcWorker
+
+  public boolean isProteinService()
+  {
+    return aaui == null ? true : aaui.isPr();
+  }
+
+  public boolean isNucleotideService()
+  {
+    return aaui == null ? false : aaui.isNa();
+  }
+
+  public boolean isNeedsAlignedSequences()
+  {
+    return aaui == null ? false : aaui.isNeedsAlignedSeqs();
+  }
+
+  public boolean isAlignmentAnalysis()
+  {
+    return aaui == null ? false : aaui.isAA();
+  }
+
+  public boolean isFilterSymbols()
+  {
+    return aaui != null ? aaui.isFilterSymbols() : true;
+  }
+
+  public int getMinimumInputSequences()
+  {
+    return aaui != null ? aaui.getMinimumSequences() : 1;
+  }
+
+  public String getNameURI()
+  {
+    return "java:" + getName();
+  }
+
+  public String getUri()
+  {
+    // TODO verify that service parameter sets in projects are consistent with
+    // Jalview 2.10.4
+    // this is only valid for Jaba 1.0 - this formula might have to change!
+    return getHostURL()
+            + (getHostURL().lastIndexOf("/") == (getHostURL().length() - 1)
+                    ? ""
+                    : "/")
+            + getName();
+  }
+
+  protected enum ServiceClient
+  {
+    MSAWSCLIENT, SEQUENCEANNOTATIONWSCLIENT;
+  };
+
+  protected ServiceClient style = null;
+
+  public void attachWSMenuEntry(JMenu atpoint, AlignFrame alignFrame)
+  {
+    switch (style)
+    {
+    case MSAWSCLIENT:
+        new MsaWSClient().attachWSMenuEntry(atpoint, this, alignFrame);
+      break;
+    case SEQUENCEANNOTATIONWSCLIENT:
+        new SequenceAnnotationWSClient().attachWSMenuEntry(atpoint, this,
+                alignFrame);
+      break;
+    default:
+      Console.warn("Implementation error ? Service " + getName()
+              + " has Unknown service style " + style);
+    }
+  }
+}
diff --git a/src/jalview/ws/api/TreeResultI.java b/src/jalview/ws/api/TreeResultI.java
new file mode 100644 (file)
index 0000000..1a0c3bd
--- /dev/null
@@ -0,0 +1,14 @@
+package jalview.ws.api;
+
+import jalview.analysis.NJTree;
+import jalview.ws.params.InvalidArgumentException;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+
+public interface TreeResultI
+{
+
+  public NJTree getTreeFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError;
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/UIinfo.java b/src/jalview/ws/api/UIinfo.java
new file mode 100644 (file)
index 0000000..8ef4d5b
--- /dev/null
@@ -0,0 +1,164 @@
+package jalview.ws.api;
+
+import jalview.ws.params.ParamDatastoreI;
+
+/**
+ * Service UI Info { Action, Specific Name of Service, Brief Description }
+ */
+
+public class UIinfo
+{
+  private String ServiceType;
+
+  public UIinfo(String serviceType, String action, String name,
+          String description, String hosturl)
+  {
+    this.setServiceType(serviceType == null ? "" : serviceType);
+    this.Action = action == null ? "" : action;
+    this.description = description == null ? "" : description;
+    this.Name = name == null ? "" : name;
+    this.hostURL = hosturl;
+  }
+
+  /**
+   * The type of analysis the service performs
+   */
+  public String getServiceType()
+  {
+    return ServiceType;
+  }
+
+  public void setServiceType(String serviceType)
+  {
+    ServiceType = serviceType;
+  }
+
+  /**
+   * The action when the service performs the analysis
+   */
+  public String getAction()
+  {
+    return Action;
+  }
+
+  public void setAction(String action)
+  {
+    Action = action;
+  }
+
+  /**
+   * name shown to user
+   * 
+   * @return
+   */
+  public String getName()
+  {
+    return Name;
+  }
+
+  public void setName(String name)
+  {
+    Name = name;
+  }
+
+  /**
+   * Detailed description (may include references, URLs, html,etc)
+   * 
+   * @return
+   */
+  public String getDescription()
+  {
+    return description;
+  }
+
+  public void setDescription(String description)
+  {
+    this.description = description;
+  }
+
+  @Override
+  public boolean equals(Object object)
+  {
+    if (object == null || !(object instanceof UIinfo))
+    {
+      return false;
+    }
+    UIinfo other = (UIinfo) object;
+
+    return (ServiceType == null && other.getServiceType() == null
+            || ServiceType != null && other.getServiceType() != null
+                    && ServiceType.equals(other.getServiceType()))
+            && (hostURL == null && other.getHostURL() == null
+                    || hostURL != null && other.getHostURL() != null
+                            && hostURL.equals(other.getHostURL()))
+            && (Name == null && other.getName() == null
+                    || Name != null && other.getName() != null
+                            && Name.equals(other.getName()))
+            && (Action == null && other.getAction() == null
+                    || Action != null && other.getAction() != null
+                            && Action.equals(other.getAction()))
+            && (description == null && other.getDescription() == null
+                    || description != null && other.getDescription() != null
+                            && description.equals(other.getDescription()));
+  }
+
+  /**
+   * @return short description of what the service will do
+   */
+  public String getActionText()
+  {
+    return getAction() + " with " + getName();
+  }
+
+  String Action;
+
+  String Name;
+
+  String description;
+
+  String hostURL;
+
+  public String getHostURL()
+  {
+    return hostURL;
+  }
+
+  public ParamDatastoreI getParamStore()
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  /**
+   * 
+   * @return true if the service has parameters (ie is instance of
+   *         jalview.ws.api.ServiceWithParameters)
+   */
+  public boolean hasParameters()
+  {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  private String docUrl = null;
+
+  /**
+   * set the URL that will be offered to show documentation for the service
+   * 
+   * @param url
+   */
+  public void setDocumentationUrl(String url)
+  {
+    docUrl = url;
+  }
+
+  public boolean hasDocumentationUrl()
+  {
+    return docUrl != null && docUrl.length() > 7;
+  }
+
+  public String getDocumentationUrl()
+  {
+    return docUrl;
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/WSAnnotationCalcManagerI.java b/src/jalview/ws/api/WSAnnotationCalcManagerI.java
new file mode 100644 (file)
index 0000000..47e3195
--- /dev/null
@@ -0,0 +1,6 @@
+package jalview.ws.api;
+
+public interface WSAnnotationCalcManagerI
+{
+
+}
index d02910c..2e3a444 100644 (file)
@@ -47,6 +47,7 @@ public class EmblCdsSource extends EmblFlatfileSource // was EmblXmlSource
     return getEmblSequenceRecords(DBRefSource.EMBLCDS, queries);
   }
 
+
   /**
    * cDNA for LDHA_CHICK swissprot sequence
    */
index 50ccba2..93c4ad6 100644 (file)
@@ -34,6 +34,7 @@ import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.EmblFlatFile;
 import jalview.io.FileParse;
+import jalview.util.Platform;
 import jalview.ws.ebi.EBIFetchClient;
 
 /**
@@ -44,7 +45,7 @@ import jalview.ws.ebi.EBIFetchClient;
  */
 public abstract class EmblFlatfileSource extends EbiFileRetrievedProxy
 {
-  private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+");
+  private static Regex ACCESSION_REGEX = null;
 
   @Override
   public String getDbVersion()
@@ -61,6 +62,10 @@ public abstract class EmblFlatfileSource extends EbiFileRetrievedProxy
   @Override
   public Regex getAccessionValidator()
   {
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform.newRegex("^[A-Z]+[0-9]+");
+    }
     return ACCESSION_REGEX;
   }
 
index df43bc3..c6cc573 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ws.dbsources;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
 
+
 /**
  * @author JimP
  * 
@@ -55,6 +56,7 @@ public class EmblSource extends EmblFlatfileSource // was EmblXmlSource
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     return getEmblSequenceRecords(DBRefSource.EMBL, queries);
+
   }
 
   /**
index 034ea4f..ba46a6c 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.dbsources;
 
+import java.util.Locale;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
@@ -28,7 +30,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -40,7 +41,6 @@ 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.Console;
@@ -57,6 +57,8 @@ import jalview.util.DBRefUtils;
 import jalview.util.DnaUtils;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.ebi.EBIFetchClient;
 import jalview.xml.binding.embl.EntryType;
 import jalview.xml.binding.embl.EntryType.Feature;
@@ -64,9 +66,19 @@ import jalview.xml.binding.embl.EntryType.Feature.Qualifier;
 import jalview.xml.binding.embl.ROOT;
 import jalview.xml.binding.embl.XrefType;
 
+import com.stevesoft.pat.Regex;
+
+/**
+ * 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
+ */
+@Deprecated
 public abstract class EmblXmlSource extends EbiFileRetrievedProxy
 {
-  private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+");
+  // TODO: delete class or update tyhis validator for 2.12 style Platform.regex
+    private static final Regex ACCESSION_REGEX = Platform.newRegex("^[A-Z]+[0-9]+");
 
   /*
    * JAL-1856 Embl returns this text for query not found
@@ -97,8 +109,8 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     try
     {
       reply = dbFetch.fetchDataAsFile(
-              emprefx.toLowerCase(Locale.ROOT) + ":" + query.trim(),
-              "display=xml", "xml");
+              emprefx.toLowerCase(Locale.ROOT) + ":" + query.trim(), "display=xml",
+              "xml");
     } catch (Exception e)
     {
       stopQuery();
@@ -448,8 +460,9 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
         else
         {
           // final product length truncation check
-          int[] exons2 = adjustForProteinLength(translationLength, exons);
-          dnaToProteinMapping = new Mapping(product, exons2,
+          int[] cdsRanges = adjustForProteinLength(translationLength,
+                  exons);
+          dnaToProteinMapping = new Mapping(product, cdsRanges,
                   new int[]
                   { 1, translationLength }, 3, 1);
           if (product != null)
index bb5c165..d502f74 100644 (file)
@@ -36,6 +36,7 @@ import jalview.io.FormatAdapter;
 import jalview.io.PDBFeatureSettings;
 import jalview.structure.StructureImportSettings;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.ebi.EBIFetchClient;
 
 import java.io.File;
@@ -56,6 +57,8 @@ public class Pdb extends EbiFileRetrievedProxy
 
   private static final int PDB_ID_LENGTH = 4;
 
+  private static Regex ACCESSION_REGEX;
+
   public Pdb()
   {
     super();
@@ -80,7 +83,12 @@ public class Pdb extends EbiFileRetrievedProxy
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("([1-9][0-9A-Za-z]{3}):?([ _A-Za-z0-9]?)");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform
+              .newRegex("([1-9][0-9A-Za-z]{3}):?([ _A-Za-z0-9]?)");
+    }
+    return ACCESSION_REGEX;
   }
 
   /*
index 371eb50..3a6fed2 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.ws.dbsources;
 
 import java.util.Locale;
-
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -32,6 +31,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 import jalview.xml.binding.uniprot.DbReferenceType;
@@ -71,6 +71,7 @@ public class Uniprot extends DbSourceProxyImpl
   private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
 
   private static final String BAR_DELIMITER = "|";
+  private static Regex ACCESSION_REGEX;
 
   /**
    * Constructor
@@ -104,7 +105,12 @@ public class Uniprot extends DbSourceProxyImpl
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform
+              .newRegex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
+    }
+    return ACCESSION_REGEX;
   }
 
   /*
@@ -165,6 +171,7 @@ public class Uniprot extends DbSourceProxyImpl
           al = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
         }
       }
+
       stopQuery();
       return al;
 
@@ -247,8 +254,16 @@ public class Uniprot extends DbSourceProxyImpl
           dbRefs.add(dbr);
         }
       }
-      if ("Ensembl".equals(type))
+      // from 2.11.2.6 - probably see a conflict here
+      if (type != null
+              && type.toLowerCase(Locale.ROOT).startsWith("ensembl"))
       {
+        // remove version
+        String[] vrs = dbref.getId().split("\\.");
+        String version = vrs.length > 1 ? vrs[1]
+                : DBRefSource.UNIPROT + ":" + dbVersion;
+        dbr.setAccessionId(vrs[0]);
+        dbr.setVersion(version);
         /*
          * e.g. Uniprot accession Q9BXM7 has
          * <dbReference type="Ensembl" id="ENST00000321556">
@@ -261,8 +276,12 @@ public class Uniprot extends DbSourceProxyImpl
                 "protein sequence ID");
         if (cdsId != null && cdsId.trim().length() > 0)
         {
+          // remove version
+          String[] cdsVrs = cdsId.split("\\.");
+          String cdsVersion = cdsVrs.length > 1 ? cdsVrs[1]
+                  : DBRefSource.UNIPROT + ":" + dbVersion;
           dbr = new DBRefEntry(DBRefSource.ENSEMBL,
-                  DBRefSource.UNIPROT + ":" + dbVersion, cdsId.trim());
+                  DBRefSource.UNIPROT + ":" + cdsVersion, cdsVrs[0]);
           dbRefs.add(dbr);
         }
       }
diff --git a/src/jalview/ws/ebi/HmmerJSONProcessor.java b/src/jalview/ws/ebi/HmmerJSONProcessor.java
new file mode 100644 (file)
index 0000000..428c498
--- /dev/null
@@ -0,0 +1,331 @@
+package jalview.ws.ebi;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.io.FileParse;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+public class HmmerJSONProcessor
+{
+  /**
+   * result to be annotated. may not be null
+   */
+  AlignmentI resultAl;
+
+  /**
+   * viewport on the alignment. may be null at construction time
+   */
+  AlignmentViewport viewAl = null;
+
+  public HmmerJSONProcessor(AlignmentI searchResult)
+  {
+    resultAl = searchResult;
+  }
+
+  public void parseFrom(FileParse jsonsource) throws IOException,
+          OutOfMemoryError
+  {
+    JSONParser hmmerResultParser = new JSONParser();
+    Object jsonResults = null;
+    try
+    {
+      jsonResults = hmmerResultParser.parse(jsonsource.getReader());
+    } catch (Exception p)
+    {
+      throw new IOException("While parsing from " + jsonsource.getInFile(),
+              p);
+    }
+    if (jsonResults == null)
+    {
+      throw new IOException("No data at" + jsonsource.getInFile());
+    }
+    if (!(jsonResults instanceof JSONObject))
+    {
+      throw new IOException("Unexpected JSON model at "
+              + jsonsource.getInFile());
+    }
+    try
+    {
+      JSONObject hmmsearchr = (JSONObject) ((JSONObject) jsonResults)
+              .get("results");
+      // now process the hits
+      addStatistics((JSONObject) hmmsearchr.get("stats"));
+      JSONArray jsonArray = (JSONArray) hmmsearchr.get("hits");
+      long p = 1;
+      for (Object hit : jsonArray)
+      {
+        JSONObject hmmhit = (JSONObject) hit;
+        addHit(hmmhit, p++);
+      }
+    } catch (ClassCastException q)
+    {
+      throw new IOException("Unexpected JSON model content at "
+              + jsonsource.getInFile(), q);
+    }
+  }
+
+  /**
+   * 
+   * @param object
+   *          - actually a JSONObject key value set of search statistics.
+   */
+  public void addStatistics(JSONObject stats)
+  {
+    for (Object stat : stats.keySet())
+    {
+      String key = (String) stat;
+      Object val = stats.get(key);
+      resultAl.setProperty(key, "" + val);
+    }
+  }
+
+  // encodings for JSON keys
+  /**
+   * score becomes sequence associated AlignmentAnnotation
+   */
+  private String[] score = { "aliId", "ali_IdCount", "bitscore", "ievalue",
+      "aliSim", "aliSimCount", "aliL", "aliSim", "ievalue", "cevalue" };
+
+  /**
+   * attrib becomes numeric or binary attribute for sequence with respect to
+   * this hmmsearch run
+   */
+  private String[] attrib = { "bias", "oasc", "is_included", "is_reported" };
+
+  /**
+   * name of the hmmsearch query
+   */
+  private String[] label = { "alihmmname" // (query label?)},
+  };
+
+  /**
+   * integer attributes for each
+   */
+  private String[] ipos = { "alihmmfrom", "alihmmto" }, pos_l = {
+      "alimline", "alimodel", "alirfline" };
+
+  /**
+   * positional quantitative annotation encoded as strings.
+   */
+  private String[] pos_nscore = { "alippline" };
+
+  //
+  // mapping of keys to types of property on sequence
+  //
+  public void addHit(JSONObject hmmrhit, long p)
+  {
+    String sname = (String) hmmrhit.get("name");
+    SequenceI[] hits = resultAl.findSequenceMatch(sname);
+    if (hits == null)
+    {
+      System.err.println("No seq for " + sname);
+    }
+    double pvalue = (Double) hmmrhit.get("pvalue");
+
+    double evalue = Double.valueOf("" + hmmrhit.get("evalue"));
+    for (Object domainhit : ((JSONArray) hmmrhit.get("domains")))
+    {
+      JSONObject dhit = (JSONObject) domainhit;
+      // dhit.get(key)
+
+      // alihmmfrom,alihmmto alimodel
+      long alihmmfrom = (long) dhit.get("alihmmfrom"), alihmmto = (long) dhit
+              .get("alihmmto"), alisqfrom = (long) dhit.get("alisqfrom"), alisqto = (long) dhit
+              .get("alisqto");
+
+      // alisqfrom,alisqto,aliaseq
+
+      // alippline
+      String aliaseq = (String) dhit.get("aliaseq"), alimodel = (String) dhit
+              .get("alimodel"), ppline = (String) dhit.get("alippline");
+      //
+      int found = 0;
+      SequenceI firsthit = null;
+      for (SequenceI hitseq : hits)
+      {
+        // match alisqfrom,alisqto,seq
+        if (hitseq.getStart() == alisqfrom && hitseq.getEnd() == alisqto)
+        {
+          if (found == 0)
+          {
+            firsthit = hitseq;
+          }
+          found++; // annotated a sequence
+          AlignmentAnnotation alipp = parsePosteriorProb(ppline);
+          AlignmentAnnotation pval = new AlignmentAnnotation("p-value",
+                  "hmmer3 pvalue", pvalue);
+          AlignmentAnnotation eval = new AlignmentAnnotation("e-value",
+                  "hmmer3 evalue", evalue);
+          pval.setCalcId("HMMER3");
+          eval.setCalcId("HMMER3");
+          alipp.setCalcId("HMMER3");
+          hitseq.addAlignmentAnnotation(pval);
+          hitseq.addAlignmentAnnotation(eval);
+          alipp.createSequenceMapping(hitseq, hitseq.getStart(), false);
+          hitseq.addAlignmentAnnotation(alipp);
+          String arch;
+          hitseq.addSequenceFeature(new SequenceFeature(
+                  "Pfam Domain Architecture", (hmmrhit.get("archindex"))
+                          + " " + (arch = (String) hmmrhit.get("arch")), 0,
+                  0,
+                  (hmmrhit.get("archScore") != null ? Integer
+                          .valueOf((String) hmmrhit.get("archScore")) : 0f),
+                  "HMMER3"));
+          addArchGroup(hitseq, arch);
+          alipp.setScore(Double.valueOf("" + dhit.get("bitscore")));
+          alipp.adjustForAlignment();
+          resultAl.addAnnotation(pval);
+          resultAl.addAnnotation(eval);
+          resultAl.addAnnotation(alipp);
+          alipp.validateRangeAndDisplay();
+        }
+      }
+      // look for other sequences represented by this hit and create rep groups
+      // could be in "pdbs", or ..
+      addRedundantSeqGroup(firsthit, alisqfrom, alisqto,
+              (JSONArray) hmmrhit.get("seqs"), true);
+    }
+  }
+
+  /**
+   * series of operations to perform for the viewpanel associated with the
+   * alignment
+   */
+  private List<Runnable> viewOps = new ArrayList<Runnable>();
+
+  public void updateView(AlignmentViewport view)
+  {
+    viewAl = view;
+    for (Runnable op : viewOps)
+    {
+      op.run();
+    }
+  }
+
+  private void addRedundantSeqGroup(final SequenceI firsthit,
+          long alisqfrom, long alisqto, JSONArray others, boolean justDelete)
+  {
+    if (others != null)
+    {
+      final SequenceGroup repgroup = new SequenceGroup();
+      repgroup.setSeqrep(firsthit);
+      repgroup.addOrRemove(firsthit, false);
+      repgroup.setStartRes(0);
+      repgroup.setEndRes(resultAl.getWidth() - 1);
+      for (Object otherseq : others.toArray(new JSONObject[0]))
+      {
+        String repseq = (String) ((JSONObject) otherseq).get("dn");
+        SequenceI[] other = resultAl.findSequenceMatch(repseq);
+        if (other != null && other.length > 0)
+        {
+          if (justDelete)
+          {
+            for (SequenceI oth : other)
+            {
+              resultAl.deleteSequence(oth);
+            }
+            ;
+          }
+          else
+          {
+            int ofound = 0;
+            for (SequenceI oth : other)
+            {
+              if (oth.getStart() == alisqfrom && oth.getEnd() == alisqto)
+              {
+                ofound++;
+                repgroup.addSequence(oth, false);
+              }
+            }
+            if (ofound == 0)
+            {
+              System.err.println("Warn - no match for redundant hit "
+                      + repseq + "/" + alisqfrom + "-" + alisqto);
+            }
+            if (ofound > 1)
+            {
+              System.err
+                      .println("Warn - multiple matches for redundant hit "
+                              + repseq + "/" + alisqfrom + "-" + alisqto);
+            }
+          }
+        }
+      }
+      if (repgroup.getSequences().size() > 1)
+      {
+        // queue a hide operation
+        final HmmerJSONProcessor me = this;
+        viewOps.add(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            me.viewAl.hideRepSequences(firsthit, repgroup);
+          }
+        });
+      }
+    }
+  }
+
+  Map<String, SequenceGroup> groups = new HashMap<String, SequenceGroup>();
+
+  private void addArchGroup(SequenceI seqToAdd, String groupNam)
+  {
+    SequenceGroup sg = groups.get(groupNam);
+    if (sg == null)
+    {
+      sg = new SequenceGroup();
+      sg.setName(groupNam);
+      sg.addSequence(seqToAdd, false);
+      sg.setStartRes(0);
+      sg.setEndRes(resultAl.getWidth() - 1);
+      groups.put(groupNam, sg);
+      resultAl.addGroup(sg);
+    }
+    else
+    {
+      sg.addSequence(seqToAdd, false);
+    }
+  }
+
+  private AlignmentAnnotation parsePosteriorProb(String ppline)
+  {
+    Annotation[] ae = new Annotation[ppline.length()];
+    int spos = 0;
+    for (int i = 0, iSize = ppline.length(); i < iSize; i++)
+    {
+      char pp = ppline.charAt(i);
+      if (pp == '*')
+      {
+        ae[spos++] = new Annotation(10f);
+      }
+      else
+      {
+        if (pp >= '0' && pp <= '9')
+        {
+          ae[spos++] = new Annotation(Integer.valueOf("" + pp));
+        }
+      }
+    }
+    AlignmentAnnotation pprob = new AlignmentAnnotation(
+            "Posterior Probability",
+            "Likelihood of HMM fit at each hit position.", ae);
+    pprob.graph = AlignmentAnnotation.BAR_GRAPH;
+    pprob.visible = false;
+    return pprob;
+  }
+}
diff --git a/src/jalview/ws/ebi/hmmerClient.java b/src/jalview/ws/ebi/hmmerClient.java
new file mode 100644 (file)
index 0000000..41cd0d8
--- /dev/null
@@ -0,0 +1,319 @@
+package jalview.ws.ebi;
+
+import jalview.datamodel.AlignmentI;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileParse;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Matcher;
+
+import org.apache.axis.transport.http.HTTPConstants;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import compbio.util.FileUtil;
+
+public class hmmerClient
+{
+  /**
+   * URLs for ebi api
+   */
+  static String baseUrl = "http://www.ebi.ac.uk/Tools/hmmer",
+          jackH = "/search/jackhmmer", phmmer = "/search/phmmer",
+          hmmscan = "/search/hmmscan", hmmsearch = "/search/hmmsearch";
+
+  static String edseq = ">2abl_A mol:protein length:163  ABL TYROSINE KINASE\nMGPSENDPNLFVALYDFVASGDNTLSITKGEKLRVLGYNHNGEWCEAQTKNGQGWVPSNYITPVNSLEKHS\nWYHGPVSRNAAEYLLSSGINGSFLVRESESSPGQRSISLRYEGRVYHYRINTASDGKLYVSSESRFNTLAE\nLVHHHSTVADGLITTLHYPAP";
+
+  public static void main(String[] args)
+  {
+    String instr = edseq;
+    if (args.length > 0)
+    {
+      try
+      {
+        instr = FileUtil.readFileToString(new File(args[0]));
+      } catch (Exception f)
+      {
+        f.printStackTrace();
+        return;
+      }
+    }
+    String res = new hmmerClient().submitJackhmmerSearch(instr,
+            "jackhmmer", "pdb", 5);
+    if (res == null)
+    {
+      throw new Error("Failed.");
+    }
+    System.out.println("Result\n" + res);
+    return;
+  }
+
+  /**
+   * 
+   * @param input
+   *          - fasta or other formatted sequence or alignment
+   * @param algo
+   *          - jackhmmer
+   * @param db
+   *          - pdb, uniprot, etc.
+   * @param niter
+   *          number of iterations
+   * @return job id
+   */
+  String submitJackhmmerSearch(String input, String algo, String db,
+          int niter)
+  {
+    JSONObject inparam = new JSONObject();
+    HttpPost jackhp = new HttpPost(baseUrl + jackH);
+    String lastiter = null;
+    try
+    {
+      inparam.put("algo", algo);
+      inparam.put("seq", input);
+      inparam.put("seqdb", db);
+      inparam.put("iterations", niter);
+      // #Now POST the request and generate the search job.
+      // dumb json post service
+      jackhp.setHeader("content-type", "application/json");
+      jackhp.setEntity(new StringEntity(inparam.toString()));
+    } catch (Exception f)
+    {
+      f.printStackTrace();
+      return null;
+    }
+    HttpResponse r = null;
+    try
+    {
+      DefaultHttpClient httpCl = new DefaultHttpClient();
+
+      r = httpCl.execute(jackhp);
+
+    } catch (Exception x)
+    {
+      System.err.println("Submit failed.");
+      x.printStackTrace();
+    }
+    if (r.getStatusLine().getStatusCode() != 201)
+    {
+      throw new Error(r.toString());
+    }
+    // get uid for job
+    String jobid = null, redir = null;
+    try
+    {
+      JSONObject res = new JSONObject(EntityUtils.toString(r.getEntity()));
+      jobid = res.getString("job_id");
+
+      Header[] loc;
+      if ((loc = r.getHeaders(HTTPConstants.HEADER_LOCATION)) != null
+              && loc.length > 0)
+      {
+        if (loc.length > 1)
+        {
+          System.err
+                  .println("Ignoring additional "
+                          + (loc.length - 1)
+                          + " location(s) provided in response header ( next one is '"
+                          + loc[1].getValue() + "' )");
+        }
+        redir = loc[0].getValue();
+      }
+    } catch (Exception x)
+    {
+      System.err.println("job id extraction failed.");
+      x.printStackTrace();
+    }
+    int tries = 0;
+    boolean finished = false;
+    JSONObject jobstate = null;
+    do
+    {
+      try
+      {
+        DefaultHttpClient httpCl = new DefaultHttpClient();
+
+        HttpGet jackcheck = new HttpGet(redir);
+        jackcheck.setHeader("content-type", "application/json");
+        r = httpCl.execute(jackcheck);
+        switch (r.getStatusLine().getStatusCode())
+        {
+        case 200:
+          jobstate = new JSONObject(EntityUtils.toString(r.getEntity()));
+          String st = jobstate.getString("status");
+          if ("DONE".equals(st))
+          {
+            finished = true;
+          }
+          if ("ERROR".equals(st))
+          {
+            System.err.println("Error");
+            finished = true;
+          }
+          if ("PEND".equals(st) || "RUN".equals("st"))
+          {
+            JSONArray iters = jobstate.getJSONArray("result");
+            lastiter = iters.getJSONObject(iters.length() - 1).getString(
+                    "uuid");
+            if (lastiter.length() > 0)
+            {
+              java.util.regex.Pattern p = java.util.regex.Pattern
+                      .compile(".+(\\d+)");
+              Matcher m = p.matcher(lastiter);
+              if (m.matches())
+              {
+                System.out.println("On iteration " + m.group(1));
+              }
+            }
+          }
+          break;
+
+        default:
+          tries++;
+          Thread.sleep(2000);
+        }
+      } catch (Exception q)
+      {
+        q.printStackTrace();
+        return null;
+      }
+    } while (!finished && tries < 50);
+
+    if (!finished)
+    {
+      System.err.println("Giving up with job " + jobid + " at " + redir);
+      return null;
+    }
+    // get results
+    // http://www.ebi.ac.uk/Tools/hmmer/download/60048B38-7CEC-11E5-A230-CED6D26C98AD.5/score?format=csv
+    // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 1 2 4.4e-46 2.1e-43
+    // 151.758316040039 0.04 11 151 3 139 1 150 0.94 GROWTH FACTOR BOUND PROTEIN
+    // 2 1cj1_J 1gri_B
+    // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 2 2 1.6e-17 7.9e-15
+    // 58.8796501159668 0.01 7 66 157 215 153 216 0.95 GROWTH FACTOR BOUND
+    // PROTEIN 2 1cj1_J 1gri_B
+    // 4h1o_A 4h1o_A 560 jackhmmer - 163 2.1e-57 197.3 0.0 1 2 7.5e-28 3.6e-25
+    // 92.4921493530273 0.00 65 161 20 122 17 124 0.95 Tyrosine-protein
+    // phosphatase non-receptor typ 4h1o_A
+    //
+    // 4h1o_A 4h1o_A 560 jackhmmer - 163 2.1e-57 197.3 0.0 2 2 7.6e-31 3.7e-28
+    // 102.219146728516 0.03 66 161 127 236 124 238 0.94 Tyrosine-protein
+    // phosphatase non-receptor typ 4h1o_A
+    //
+    // $ua->get( $rootUrl."/results/".$lastIteration->{uuid} . "/score"
+    return lastiter;
+    /*
+     * * #Job should have finished, but we may have converged, so get the last
+     * job. my $results = $json->decode( $response->content ); my $lastIteration
+     * = pop( @{ $results->{result} } ); #Now fetch the results of the last
+     * iteration my $searchResult = $ua->get( $rootUrl."/results/" .
+     * $lastIteration->{uuid} . "/score", 'Accept' => 'application/json' );
+     * unless( $searchResult->status_line eq "200 OK"){ die
+     * "Failed to get search results\n"; }
+     * 
+     * #Decode the content of the full set of results $results = $json->decode(
+     * $searchResult->content ); print
+     * "Matched ".$results->{'results'}->{'stats'}->{'nincluded'}." sequences
+     * ($lastIteration->{uuid})!\n"; #Now do something more interesting with the
+     * results......
+     */
+  }
+
+  /**
+   * retrieve an alignment annotated with scores from JackHmmer
+   * 
+   * @param jobid
+   * @param dataset
+   * @return
+   */
+  AlignmentI retrieveJackhmmerResult(String jobid, AlignmentI dataset)
+          throws OutOfMemoryError, IOException
+  {
+    AlignmentI searchResult = null;
+
+    // get results
+
+    searchResult = new AppletFormatAdapter().readFile(baseUrl
+            + "/download/" + jobid + "/score?format=afa&t=.gz",
+            DataSourceType.URL, FileFormat.Fasta);
+
+    // TODO extract gapped columns as '.' - inserts to query profile
+
+    // TODO match up jackhammer results to dataset.
+
+    // do scores
+    FileParse jsonsource = new FileParse(baseUrl + "/download/" + jobid
+            + "/score?format=json", DataSourceType.URL);
+    if (!jsonsource.isValid())
+    {
+      throw new IOException("Couldn't access scores for Jackhammer results");
+    }
+    readJackhmmerScores(searchResult, jsonsource);
+    return searchResult;
+  }
+
+  /**
+   * // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 1 2 4.4e-46 2.1e-43
+   * 
+   * // 151.758316040039 0.04 11 151 3 139 1 150 0.94 GROWTH FACTOR BOUND
+   * PROTEIN // 2 1cj1_J 1gri_B // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62
+   * 212.4 0.1 2 2 1.6e-17 7.9e-15 // 58.8796501159668 0.01 7 66 157 215 153 216
+   * 0.95 GROWTH FACTOR BOUND // PROTEIN 2 1cj1_J 1gri_B // 4h1o_A 4h1o_A 560
+   * jackhmmer - 163 2.1e-57 197.3 0.0 1 2 7.5e-28 3.6e-25 // 92.4921493530273
+   * 0.00 65 161 20 122 17 124 0.95 Tyrosine-protein // phosphatase non-receptor
+   * typ 4h1o_A
+   */
+  private static String[] _hmmsearchcols = new String[] { "acc", "name", "" };
+
+  private void readJackhmmerScores(AlignmentI searchResult,
+          FileParse jsonsource) throws IOException, OutOfMemoryError
+  {
+    HmmerJSONProcessor hjp = new HmmerJSONProcessor(searchResult);
+    hjp.parseFrom(jsonsource);
+
+    // http://www.ebi.ac.uk/Tools/hmmer/download/60048B38-7CEC-11E5-A230-CED6D26C98AD.5/score?format=csv
+    // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 1 2 4.4e-46 2.1e-43
+    // 151.758316040039 0.04 11 151 3 139 1 150 0.94 GROWTH FACTOR BOUND PROTEIN
+    // 2 1cj1_J 1gri_B
+    // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 2 2 1.6e-17 7.9e-15
+    // 58.8796501159668 0.01 7 66 157 215 153 216 0.95 GROWTH FACTOR BOUND
+    // PROTEIN 2 1cj1_J 1gri_B
+    // 4h1o_A 4h1o_A 560 jackhmmer - 163 2.1e-57 197.3 0.0 1 2 7.5e-28 3.6e-25
+    // 92.4921493530273 0.00 65 161 20 122 17 124 0.95 Tyrosine-protein
+    // phosphatase non-receptor typ 4h1o_A
+    // each line scores a fragment
+    // so for a combined score ?
+
+    /**
+     * for a sequence q sort any t against q according to overallScore(q,t)
+     * maxFragment(q,t) in sequence features parlance: for alignment
+     * s.getFeature("overallScore",q) -> range on q and range on s
+     * 
+     * 
+     */
+
+    // 151.758316040039 0.04 11 151 3 139 1 150 0.94 GROWTH FACTOR BOUND PROTEIN
+    // 2 1cj1_J 1gri_B
+    // 1gri_A 1gri_A 217 jackhmmer - 163 4.7e-62 212.4 0.1 2 2 1.6e-17 7.9e-15
+    // 58.8796501159668 0.01 7 66 157 215 153 216 0.95 GROWTH FACTOR BOUND
+    // PROTEIN 2 1cj1_J 1gri_B
+    // 4h1o_A 4h1o_A 560 jackhmmer - 163 2.1e-57 197.3 0.0 1 2 7.5e-28 3.6e-25
+    // 92.4921493530273 0.00 65 161 20 122 17 124 0.95 Tyrosine-protein
+    // phosphatase non-receptor typ 4h1o_A
+    //
+    // 4h1o_A 4h1o_A 560 jackhmmer - 163 2.1e-57 197.3 0.0 2 2 7.6e-31 3.7e-28
+    // 102.219146728516 0.03 66 161 127 236 124 238 0.94 Tyrosine-protein
+    // phosphatase non-receptor typ 4h1o_A
+
+  }
+
+}
diff --git a/src/jalview/ws/gui/AnnotationWsJob.java b/src/jalview/ws/gui/AnnotationWsJob.java
new file mode 100644 (file)
index 0000000..3381138
--- /dev/null
@@ -0,0 +1,106 @@
+package jalview.ws.gui;
+
+import jalview.api.FeatureRenderer;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
+
+import java.util.List;
+import java.util.Map;
+
+public class AnnotationWsJob extends WsJob
+{
+  /**
+   * sequences (anonymised)
+   */
+  List<SequenceI> seqs;
+
+  /**
+   * mapping to original sequences
+   */
+  Map<String, SequenceI> seqNames;
+
+  /**
+   * first column in the segment of the alignment view that was submitted
+   */
+  int startPos;
+
+  public int getStartPos()
+  {
+    return startPos;
+  }
+
+  public void setStartPos(int startPos)
+  {
+    this.startPos = startPos;
+  }
+
+  /**
+   * outputs
+   */
+  List<AlignmentAnnotation> annotation = null;
+
+  boolean transferSequenceFeatures = false;
+
+  public boolean isTransferSequenceFeatures()
+  {
+    return transferSequenceFeatures;
+  }
+
+  public void setTransferSequenceFeatures(boolean transferSequenceFeatures)
+  {
+    this.transferSequenceFeatures = transferSequenceFeatures;
+  }
+
+  public List<AlignmentAnnotation> getAnnotation()
+  {
+    return annotation;
+  }
+
+  public void setAnnotation(List<AlignmentAnnotation> annotation)
+  {
+    this.annotation = annotation;
+  }
+
+  @Override
+  public boolean hasResults()
+  {
+    return (isSubmitted() && isFinished()
+            && (annotation != null || transferSequenceFeatures));
+  }
+
+  public List<SequenceI> getSeqs()
+  {
+    return seqs;
+  }
+
+  public void setSeqs(List<SequenceI> seqs)
+  {
+    this.seqs = seqs;
+  }
+
+  public Map<String, SequenceI> getSeqNames()
+  {
+    return seqNames;
+  }
+
+  public void setSeqNames(Map<String, SequenceI> seqNames)
+  {
+    this.seqNames = seqNames;
+  }
+
+  /**
+   * configured by the WS framework just before results are collected
+   */
+  FeatureRenderer featureRenderer;
+
+  public void setFeatureRenderer(FeatureRenderer fr)
+  {
+    this.featureRenderer = fr;
+  }
+  public FeatureRenderer getFeatureRenderer()
+  {
+    // TODO Auto-generated method stub
+    return featureRenderer;
+  }
+
+}
diff --git a/src/jalview/ws/gui/MsaWSJob.java b/src/jalview/ws/gui/MsaWSJob.java
new file mode 100644 (file)
index 0000000..d8cae20
--- /dev/null
@@ -0,0 +1,370 @@
+package jalview.ws.gui;
+
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.params.ArgumentI;
+
+import java.util.ArrayList;
+import java.util.Vector;
+
+class MsaWSJob extends WsJob
+{
+  /**
+   * holds basic MSA analysis configuration - todo - encapsulate
+   */
+  private final MsaWSThread msaWSThread;
+
+  long lastChunk = 0;
+
+  /**
+   * input
+   */
+  ArrayList<SequenceI> seqs = new ArrayList<>();
+
+  /**
+   * output
+   */
+  AlignmentI alignment;
+
+  // set if the job didn't get run - then the input is simply returned to the
+  // user
+  private boolean returnInput = false;
+
+  /**
+   * MsaWSJob
+   * 
+   * @param jobNum
+   *          int
+   * @param msaWSThread
+   *          TODO - abstract the properties provided by the thread
+   * @param jobId
+   *          String
+   */
+  public MsaWSJob(MsaWSThread msaWSThread, int jobNum, SequenceI[] inSeqs)
+  {
+    this.msaWSThread = msaWSThread;
+    this.jobnum = jobNum;
+    if (!prepareInput(inSeqs, 2))
+    {
+      submitted = true;
+      subjobComplete = true;
+      returnInput = true;
+    } else
+    {
+      validInput = true;
+    }
+
+  }
+
+  Vector<String[]> emptySeqs = new Vector();
+
+  /**
+   * prepare input sequences for MsaWS service
+   * 
+   * @param seqs
+   *          jalview sequences to be prepared
+   * @param minlen
+   *          minimum number of residues required for this MsaWS service
+   * @return true if seqs contains sequences to be submitted to service.
+   */
+  // TODO: return compbio.seqs list or nothing to indicate validity.
+  private boolean prepareInput(SequenceI[] seqs, int minlen)
+  {
+    // TODO: service specific input data is generated in this method - for
+    // JABAWS it is client-side
+    // prepared, but for Slivka it could be uploaded at this stage.
+
+    int nseqs = 0;
+    if (minlen < 0)
+    {
+      throw new Error(MessageManager.getString(
+              "error.implementation_error_minlen_must_be_greater_zero"));
+    }
+    for (int i = 0; i < seqs.length; i++)
+    {
+      if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
+      {
+        nseqs++;
+      }
+    }
+    boolean valid = nseqs > 1; // need at least two seqs
+    Sequence seq;
+    for (int i = 0, n = 0; i < seqs.length; i++)
+    {
+      String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
+      // for
+      // any
+      // subjob
+      SeqNames.put(newname,
+              jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
+      if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
+      {
+        // make new input sequence with or without gaps
+        seq = new Sequence(newname,
+                (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString()
+                        : AlignSeq.extractGaps(
+                                jalview.util.Comparison.GapChars,
+                                seqs[i].getSequenceAsString()));
+        this.seqs.add(seq);
+      }
+      else
+      {
+        String empty = null;
+        if (seqs[i].getEnd() >= seqs[i].getStart())
+        {
+          empty = (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString()
+                  : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
+                          seqs[i].getSequenceAsString());
+        }
+        emptySeqs.add(new String[] { newname, empty });
+      }
+    }
+    return valid;
+  }
+
+  /**
+   * 
+   * @return true if getAlignment will return a valid alignment result.
+   */
+  @Override
+  public boolean hasResults()
+  {
+    if (subjobComplete && isFinished() && (alignment != null
+            || (emptySeqs != null && emptySeqs.size() > 0)))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * 
+   * get the alignment including any empty sequences in the original order
+   * with original ids. Caller must access the alignment.getMetadata() object
+   * to annotate the final result passsed to the user.
+   * 
+   * @return { SequenceI[], AlignmentOrder }
+   */
+  public Object[] getAlignment()
+  {
+    // TODO: make this generic based on MsaResultI
+    // TODO: decide if the data loss for this return signature is avoidable
+    // (ie should we just return AlignmentI instead ?)
+    if (hasResults())
+    {
+      SequenceI[] alseqs = null;
+      char alseq_gapchar = '-';
+      int alseq_l = 0;
+      alseqs = new SequenceI[alignment.getSequences().size()];
+      if (alignment.getSequences().size() > 0)
+      {
+        for (SequenceI seq : alignment
+                .getSequences())
+        {
+          alseqs[alseq_l++] = new Sequence(seq);
+        }
+        alseq_gapchar = alignment.getGapCharacter();
+
+      }
+      // add in the empty seqs.
+      if (emptySeqs.size() > 0)
+      {
+        SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
+        // get width
+        int i, w = 0;
+        if (alseq_l > 0)
+        {
+          for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
+          {
+            if (w < alseqs[i].getLength())
+            {
+              w = alseqs[i].getLength();
+            }
+            t_alseqs[i] = alseqs[i];
+            alseqs[i] = null;
+          }
+        }
+        // check that aligned width is at least as wide as emptySeqs width.
+        int ow = w, nw = w;
+        for (i = 0, w = emptySeqs.size(); i < w; i++)
+        {
+          String[] es = emptySeqs.get(i);
+          if (es != null && es[1] != null)
+          {
+            int sw = es[1].length();
+            if (nw < sw)
+            {
+              nw = sw;
+            }
+          }
+        }
+        // make a gapped string.
+        StringBuffer insbuff = new StringBuffer(w);
+        for (i = 0; i < nw; i++)
+        {
+          insbuff.append(alseq_gapchar);
+        }
+        if (ow < nw)
+        {
+          for (i = 0; i < alseq_l; i++)
+          {
+            int sw = t_alseqs[i].getLength();
+            if (nw > sw)
+            {
+              // pad at end
+              alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
+                      + insbuff.substring(0, sw - nw));
+            }
+          }
+        }
+        for (i = 0, w = emptySeqs.size(); i < w; i++)
+        {
+          String[] es = emptySeqs.get(i);
+          if (es[1] == null)
+          {
+            t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
+                    insbuff.toString(), 1, 0);
+          }
+          else
+          {
+            if (es[1].length() < nw)
+            {
+              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
+                      es[0],
+                      es[1] + insbuff.substring(0, nw - es[1].length()),
+                      1, 1 + es[1].length());
+            }
+            else
+            {
+              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
+                      es[0], es[1]);
+            }
+          }
+        }
+        alseqs = t_alseqs;
+      }
+      AlignmentOrder msaorder = new AlignmentOrder(alseqs);
+      // always recover the order - makes parseResult()'s life easier.
+      jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
+      // account for any missing sequences
+      jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
+      return new Object[] { alseqs, msaorder };
+    }
+    return null;
+  }
+
+  /**
+   * mark subjob as cancelled and set result object appropriatly
+   */
+  void cancel()
+  {
+    cancelled = true;
+    subjobComplete = true;
+    alignment = null;
+  }
+
+  /**
+   * 
+   * @return boolean true if job can be submitted.
+   */
+  @Override
+  public boolean hasValidInput()
+  {
+    // TODO: get attributes for this MsaWS instance to check if it can do two
+    // sequence alignment.
+    // TODO: check type of sequences are valid for this service
+    if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
+    {
+      return true;
+    }
+    return false;
+  }
+
+  StringBuffer jobProgress = new StringBuffer();
+
+  @Override
+  public void setStatus(String string)
+  {
+    jobProgress.setLength(0);
+    jobProgress.append(string);
+  }
+
+  @Override
+  public String getStatus()
+  {
+    return jobProgress.toString();
+  }
+
+  @Override
+  public boolean hasStatus()
+  {
+    return jobProgress != null;
+  }
+
+  /**
+   * @return the lastChunk
+   */
+  public long getLastChunk()
+  {
+    return lastChunk;
+  }
+
+  /**
+   * @param lastChunk
+   *          the lastChunk to set
+   */
+  public void setLastChunk(long lastChunk)
+  {
+    this.lastChunk = lastChunk;
+  }
+
+  String alignmentProgram = null;
+
+  public String getAlignmentProgram()
+  {
+    return alignmentProgram;
+  }
+
+  public boolean hasArguments()
+  {
+    return (arguments != null && arguments.size() > 0)
+            || (preset != null && preset instanceof JabaWsParamSet);
+  }
+
+  /**
+   * add a progess header to status string containing presets/args used
+   */
+  public void addInitialStatus()
+  {
+    // TODO: decide if it is useful to report 'JABAWS format' argument lists
+    // rather than generic Jalview service arguments
+    if (preset != null)
+    {
+      jobProgress.append(
+              "Using " + (preset.isModifiable() ? "Server" : "User")
+                      + "Preset: " + preset.getName());
+      for (ArgumentI opt : preset.getArguments())
+      {
+        jobProgress.append(opt.getName() + " " + opt.getValue() + "\n");
+      }
+    }
+    else
+    {
+      if (arguments != null && arguments.size() > 0)
+      {
+        jobProgress.append("With custom parameters : \n");
+        // merge arguments with preset's own arguments.
+        for (ArgumentI opt : arguments)
+        {
+          jobProgress.append(opt.getName() + " " + opt.getValue() + "\n");
+        }
+      }
+      jobProgress.append("\nJob Output:\n");
+    }
+  }
+}
\ No newline at end of file
similarity index 53%
rename from src/jalview/ws/jws2/MsaWSThread.java
rename to src/jalview/ws/gui/MsaWSThread.java
index ee0fbc5..9298fb5 100644 (file)
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.jws2;
+package jalview.ws.gui;
 
-import jalview.analysis.AlignSeq;
+import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.SplitFrame;
 import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
+import jalview.ws.AWSThread;
 import jalview.ws.AWsJob;
 import jalview.ws.JobStateSummary;
 import jalview.ws.WSClientI;
-import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.gui.WsJob.JobState;
+import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
 
 import java.util.ArrayList;
-import java.util.Hashtable;
 import java.util.List;
-import java.util.Map;
-import java.util.Vector;
 
 import javax.swing.JInternalFrame;
 
-import compbio.data.msa.MsaWS;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.Preset;
-
-class MsaWSThread extends AWS2Thread implements WSClientI
+public class MsaWSThread extends AWSThread implements WSClientI
 {
   boolean submitGaps = false; // pass sequences including gaps to alignment
 
@@ -64,375 +59,6 @@ class MsaWSThread extends AWS2Thread implements WSClientI
 
   // order
 
-  class MsaWSJob extends JWs2Job
-  {
-    long lastChunk = 0;
-
-    WsParamSetI preset = null;
-
-    List<Argument> arguments = null;
-
-    /**
-     * input
-     */
-    ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
-
-    /**
-     * output
-     */
-    compbio.data.sequence.Alignment alignment;
-
-    // set if the job didn't get run - then the input is simply returned to the
-    // user
-    private boolean returnInput = false;
-
-    /**
-     * MsaWSJob
-     * 
-     * @param jobNum
-     *          int
-     * @param jobId
-     *          String
-     */
-    public MsaWSJob(int jobNum, SequenceI[] inSeqs)
-    {
-      this.jobnum = jobNum;
-      if (!prepareInput(inSeqs, 2))
-      {
-        submitted = true;
-        subjobComplete = true;
-        returnInput = true;
-      }
-
-    }
-
-    Hashtable<String, Map> SeqNames = new Hashtable();
-
-    Vector<String[]> emptySeqs = new Vector();
-
-    /**
-     * prepare input sequences for MsaWS service
-     * 
-     * @param seqs
-     *          jalview sequences to be prepared
-     * @param minlen
-     *          minimum number of residues required for this MsaWS service
-     * @return true if seqs contains sequences to be submitted to service.
-     */
-    // TODO: return compbio.seqs list or nothing to indicate validity.
-    private boolean prepareInput(SequenceI[] seqs, int minlen)
-    {
-      int nseqs = 0;
-      if (minlen < 0)
-      {
-        throw new Error(MessageManager.getString(
-                "error.implementation_error_minlen_must_be_greater_zero"));
-      }
-      for (int i = 0; i < seqs.length; i++)
-      {
-        if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
-        {
-          nseqs++;
-        }
-      }
-      boolean valid = nseqs > 1; // need at least two seqs
-      compbio.data.sequence.FastaSequence seq;
-      for (int i = 0, n = 0; i < seqs.length; i++)
-      {
-
-        String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
-        // for
-        // any
-        // subjob
-        SeqNames.put(newname,
-                jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
-        if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
-        {
-          // make new input sequence with or without gaps
-          seq = new compbio.data.sequence.FastaSequence(newname,
-                  (submitGaps) ? seqs[i].getSequenceAsString()
-                          : AlignSeq.extractGaps(
-                                  jalview.util.Comparison.GapChars,
-                                  seqs[i].getSequenceAsString()));
-          this.seqs.add(seq);
-        }
-        else
-        {
-          String empty = null;
-          if (seqs[i].getEnd() >= seqs[i].getStart())
-          {
-            empty = (submitGaps) ? seqs[i].getSequenceAsString()
-                    : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
-                            seqs[i].getSequenceAsString());
-          }
-          emptySeqs.add(new String[] { newname, empty });
-        }
-      }
-      return valid;
-    }
-
-    /**
-     * 
-     * @return true if getAlignment will return a valid alignment result.
-     */
-    @Override
-    public boolean hasResults()
-    {
-      if (subjobComplete && isFinished() && (alignment != null
-              || (emptySeqs != null && emptySeqs.size() > 0)))
-      {
-        return true;
-      }
-      return false;
-    }
-
-    /**
-     * 
-     * get the alignment including any empty sequences in the original order
-     * with original ids. Caller must access the alignment.getMetadata() object
-     * to annotate the final result passsed to the user.
-     * 
-     * @return { SequenceI[], AlignmentOrder }
-     */
-    public Object[] getAlignment()
-    {
-      // is this a generic subjob or a Jws2 specific Object[] return signature
-      if (hasResults())
-      {
-        SequenceI[] alseqs = null;
-        char alseq_gapchar = '-';
-        int alseq_l = 0;
-        if (alignment.getSequences().size() > 0)
-        {
-          alseqs = new SequenceI[alignment.getSequences().size()];
-          for (compbio.data.sequence.FastaSequence seq : alignment
-                  .getSequences())
-          {
-            alseqs[alseq_l++] = new Sequence(seq.getId(),
-                    seq.getSequence());
-          }
-          alseq_gapchar = alignment.getMetadata().getGapchar();
-
-        }
-        // add in the empty seqs.
-        if (emptySeqs.size() > 0)
-        {
-          SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
-          // get width
-          int i, w = 0;
-          if (alseq_l > 0)
-          {
-            for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
-            {
-              if (w < alseqs[i].getLength())
-              {
-                w = alseqs[i].getLength();
-              }
-              t_alseqs[i] = alseqs[i];
-              alseqs[i] = null;
-            }
-          }
-          // check that aligned width is at least as wide as emptySeqs width.
-          int ow = w, nw = w;
-          for (i = 0, w = emptySeqs.size(); i < w; i++)
-          {
-            String[] es = emptySeqs.get(i);
-            if (es != null && es[1] != null)
-            {
-              int sw = es[1].length();
-              if (nw < sw)
-              {
-                nw = sw;
-              }
-            }
-          }
-          // make a gapped string.
-          StringBuffer insbuff = new StringBuffer(w);
-          for (i = 0; i < nw; i++)
-          {
-            insbuff.append(alseq_gapchar);
-          }
-          if (ow < nw)
-          {
-            for (i = 0; i < alseq_l; i++)
-            {
-              int sw = t_alseqs[i].getLength();
-              if (nw > sw)
-              {
-                // pad at end
-                alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
-                        + insbuff.substring(0, sw - nw));
-              }
-            }
-          }
-          for (i = 0, w = emptySeqs.size(); i < w; i++)
-          {
-            String[] es = emptySeqs.get(i);
-            if (es[1] == null)
-            {
-              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
-                      insbuff.toString(), 1, 0);
-            }
-            else
-            {
-              if (es[1].length() < nw)
-              {
-                t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
-                        es[0],
-                        es[1] + insbuff.substring(0, nw - es[1].length()),
-                        1, 1 + es[1].length());
-              }
-              else
-              {
-                t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
-                        es[0], es[1]);
-              }
-            }
-          }
-          alseqs = t_alseqs;
-        }
-        AlignmentOrder msaorder = new AlignmentOrder(alseqs);
-        // always recover the order - makes parseResult()'s life easier.
-        jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
-        // account for any missing sequences
-        jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
-        return new Object[] { alseqs, msaorder };
-      }
-      return null;
-    }
-
-    /**
-     * mark subjob as cancelled and set result object appropriatly
-     */
-    void cancel()
-    {
-      cancelled = true;
-      subjobComplete = true;
-      alignment = null;
-    }
-
-    /**
-     * 
-     * @return boolean true if job can be submitted.
-     */
-    @Override
-    public boolean hasValidInput()
-    {
-      // TODO: get attributes for this MsaWS instance to check if it can do two
-      // sequence alignment.
-      if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
-      {
-        return true;
-      }
-      return false;
-    }
-
-    StringBuffer jobProgress = new StringBuffer();
-
-    public void setStatus(String string)
-    {
-      jobProgress.setLength(0);
-      jobProgress.append(string);
-    }
-
-    @Override
-    public String getStatus()
-    {
-      return jobProgress.toString();
-    }
-
-    @Override
-    public boolean hasStatus()
-    {
-      return jobProgress != null;
-    }
-
-    /**
-     * @return the lastChunk
-     */
-    public long getLastChunk()
-    {
-      return lastChunk;
-    }
-
-    /**
-     * @param lastChunk
-     *          the lastChunk to set
-     */
-    public void setLastChunk(long lastChunk)
-    {
-      this.lastChunk = lastChunk;
-    }
-
-    String alignmentProgram = null;
-
-    public String getAlignmentProgram()
-    {
-      return alignmentProgram;
-    }
-
-    public boolean hasArguments()
-    {
-      return (arguments != null && arguments.size() > 0)
-              || (preset != null && preset instanceof JabaWsParamSet);
-    }
-
-    public List<Argument> getJabaArguments()
-    {
-      List<Argument> newargs = new ArrayList<Argument>();
-      if (preset != null && preset instanceof JabaWsParamSet)
-      {
-        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-      }
-      if (arguments != null && arguments.size() > 0)
-      {
-        newargs.addAll(arguments);
-      }
-      return newargs;
-    }
-
-    /**
-     * add a progess header to status string containing presets/args used
-     */
-    public void addInitialStatus()
-    {
-      if (preset != null)
-      {
-        jobProgress.append("Using "
-                + (preset instanceof JabaPreset ? "Server" : "User")
-                + "Preset: " + preset.getName());
-        if (preset instanceof JabaWsParamSet)
-        {
-          for (Argument opt : ((JabaWsParamSet) preset).getjabaArguments())
-          {
-            jobProgress.append(
-                    opt.getName() + " " + opt.getDefaultValue() + "\n");
-          }
-        }
-      }
-      if (arguments != null && arguments.size() > 0)
-      {
-        jobProgress.append("With custom parameters : \n");
-        // merge arguments with preset's own arguments.
-        for (Argument opt : arguments)
-        {
-          jobProgress.append(
-                  opt.getName() + " " + opt.getDefaultValue() + "\n");
-        }
-      }
-      jobProgress.append("\nJob Output:\n");
-    }
-
-    public boolean isPresetJob()
-    {
-      return preset != null && preset instanceof JabaPreset;
-    }
-
-    public Preset getServerPreset()
-    {
-      return (isPresetJob()) ? ((JabaPreset) preset).p : null;
-    }
-  }
 
   String alTitle; // name which will be used to form new alignment window.
 
@@ -440,8 +66,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
 
   // associated.
 
-  @SuppressWarnings("unchecked")
-  MsaWS server = null;
+  MultipleSequenceAlignmentI server = null;
 
   /**
    * set basic options for this (group) of Msa jobs
@@ -451,7 +76,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
    * @param presorder
    *          boolean
    */
-  private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
+  private MsaWSThread(MultipleSequenceAlignmentI server, String wsUrl,
+          WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, AlignmentView alview,
           String wsname, boolean subgaps, boolean presorder)
   {
@@ -462,7 +88,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
   }
 
   /**
-   * create one or more Msa jobs to align visible seuqences in _msa
+   * create one or more Msa jobs to align visible sequences in _msa
    * 
    * @param title
    *          String
@@ -475,7 +101,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
    * @param seqset
    *          Alignment
    */
-  MsaWSThread(MsaWS server2, WsParamSetI preset, List<Argument> paramset,
+  public MsaWSThread(MultipleSequenceAlignmentI server2, WsParamSetI preset,
+          List<ArgumentI> paramset,
           String wsUrl, WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, String wsname, String title,
           AlignmentView _msa, boolean subgaps, boolean presorder,
@@ -490,23 +117,23 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     if (conmsa != null)
     {
       int nvalid = 0, njobs = conmsa.length;
-      jobs = new MsaWSJob[njobs];
+      jobs = new AWsJob[njobs];
       for (int j = 0; j < njobs; j++)
       {
         if (j != 0)
         {
-          jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
+          jobs[j] = new MsaWSJob(this, wsinfo.addJobPane(), conmsa[j]);
         }
         else
         {
-          jobs[j] = new MsaWSJob(0, conmsa[j]);
+          jobs[j] = new MsaWSJob(this, 0, conmsa[j]);
         }
-        if (((MsaWSJob) jobs[j]).hasValidInput())
+        if (jobs[j].hasValidInput())
         {
           nvalid++;
         }
-        ((MsaWSJob) jobs[j]).preset = preset;
-        ((MsaWSJob) jobs[j]).arguments = paramset;
+        jobs[j].setPreset(preset);
+        jobs[j].setArguments(paramset);
         ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
         if (njobs > 0)
         {
@@ -533,12 +160,14 @@ class MsaWSThread extends AWS2Thread implements WSClientI
   @Override
   public boolean isCancellable()
   {
-    return true;
+    return server instanceof CancellableI;
   }
 
   @Override
   public void cancelJob()
   {
+    // TODO decide if when some jobs are not cancellable to shut down the thread
+    // anyhow ?
     if (!jobComplete && jobs != null)
     {
       boolean cancelled = true;
@@ -549,13 +178,11 @@ class MsaWSThread extends AWS2Thread implements WSClientI
           String cancelledMessage = "";
           try
           {
-            boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
-            if (true) // cancelledJob || true)
+            CancellableI service = (CancellableI) server;
+            boolean cancelledJob = service.cancel((WsJob) jobs[job]);
+            if (cancelledJob)
             {
               // CANCELLED_JOB
-              // if the Jaba server indicates the job can't be cancelled, its
-              // because its running on the server's local execution engine
-              // so we just close the window anyway.
               cancelledMessage = "Job cancelled.";
               ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
                                                // ugliness -
@@ -615,42 +242,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     // this is standard code, but since the interface doesn't comprise of a
     // basic one that implements (getJobStatus, pullExecStatistics) we have to
     // repeat the code for all jw2s services.
-    j.setjobStatus(server.getJobStatus(job.getJobId()));
-    updateJobProgress(j);
-  }
-
-  /**
-   * 
-   * @param j
-   * @return true if more job progress data was available
-   * @throws Exception
-   */
-  protected boolean updateJobProgress(MsaWSJob j) throws Exception
-  {
-    StringBuffer response = j.jobProgress;
-    long lastchunk = j.getLastChunk();
-    boolean changed = false;
-    do
-    {
-      j.setLastChunk(lastchunk);
-      ChunkHolder chunk = server.pullExecStatistics(j.getJobId(),
-              lastchunk);
-      if (chunk != null)
-      {
-        changed |= chunk.getChunk().length() > 0;
-        response.append(chunk.getChunk());
-        lastchunk = chunk.getNextPosition();
-        try
-        {
-          Thread.sleep(50);
-        } catch (InterruptedException x)
-        {
-        }
-        ;
-      }
-      ;
-    } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
-    return changed;
+    server.updateStatus(j);
+    server.updateJobProgress(j);
   }
 
   @Override
@@ -679,25 +272,38 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     if (j.seqs == null || j.seqs.size() == 0)
     {
       // special case - selection consisted entirely of empty sequences...
-      j.setjobStatus(JobStatus.FINISHED);
+      j.setState(JobState.FINISHED);
       j.setStatus(MessageManager.getString("label.empty_alignment_job"));
     }
     try
     {
       j.addInitialStatus(); // list the presets/parameters used for the job in
                             // status
-      if (j.isPresetJob())
-      {
-        j.setJobId(server.presetAlign(j.seqs, j.getServerPreset()));
-      }
-      else if (j.hasArguments())
+      try
       {
-        j.setJobId(server.customAlign(j.seqs, j.getJabaArguments()));
-      }
-      else
+        JobId jobHandle = server.align(j.seqs, j.getPreset(),
+                j.getArguments());
+        if (jobHandle != null)
+        {
+          j.setJobHandle(jobHandle);
+        }
+
+      } catch (Throwable throwable)
       {
-        j.setJobId(server.align(j.seqs));
+        Console.error("failed to send the job to the alignment server", throwable);
+        if (!server.handleSubmitError(throwable, j, wsInfo))
+        {
+          if (throwable instanceof Exception)
+          {
+            throw ((Exception) throwable);
+          }
+          if (throwable instanceof Error)
+          {
+            throw ((Error) throwable);
+          }
+        }
       }
+      ///// generic
 
       if (j.getJobId() != null)
       {
@@ -713,40 +319,11 @@ class MsaWSThread extends AWS2Thread implements WSClientI
                 new String[]
                 { WsUrl }));
       }
-    } catch (compbio.metadata.UnsupportedRuntimeException _lex)
-    {
-      lex = _lex;
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_server_doesnt_support_program",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.service_not_supported"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      wsInfo.setStatus(j.getJobnum(),
-              WebserviceInfo.STATE_STOPPED_SERVERERROR);
-    } catch (compbio.metadata.LimitExceededException _lex)
-    {
-      lex = _lex;
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.input_is_too_big"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-    } catch (compbio.metadata.WrongParameterException _lex)
-    {
-      lex = _lex;
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.invalid_job_param_set"));
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_incorrect_param_setting",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-    } catch (Error e)
+    }
+    //// jabaws specific
+
+    //// generic
+    catch (Error e)
     {
       // For unexpected errors
       System.err.println(WebServiceName
@@ -802,7 +379,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
           {
             try
             {
-              jpchanged = updateJobProgress(msjob);
+              jpchanged = server.updateJobProgress(msjob);
               jpex = false;
               if (jpchanged)
               {
@@ -843,25 +420,18 @@ class MsaWSThread extends AWS2Thread implements WSClientI
             System.out.println("*** End of status");
 
           }
+          ///// jabaws specific(ish) Get Result from Server when available
           try
           {
-            msjob.alignment = server.getResult(msjob.getJobId());
-          } catch (compbio.metadata.ResultNotAvailableException e)
-          {
-            // job has failed for some reason - probably due to invalid
-            // parameters
-            Console.debug(
-                    "Results not available for finished job - marking as broken job.",
-                    e);
-            msjob.jobProgress.append(
-                    "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
-                            + e.getLocalizedMessage());
-            msjob.setjobStatus(JobStatus.FAILED);
+            msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
           } catch (Exception e)
           {
-            Console.error("Couldn't get Alignment for job.", e);
-            // TODO: Increment count and retry ?
-            msjob.setjobStatus(JobStatus.UNDEFINED);
+            if (!server.handleCollectionException(e, msjob, wsInfo))
+            {
+              Console.error("Couldn't get Alignment for job.", e);
+              // TODO: Increment count and retry ?
+              msjob.setState(JobState.SERVERERROR);
+            }
           }
         }
         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
@@ -869,19 +439,6 @@ class MsaWSThread extends AWS2Thread implements WSClientI
                 && jobs[j].hasResults())
         {
           results++;
-          compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
-          if (alignment != null)
-          {
-            // server.close(jobs[j].getJobnum());
-            // wsInfo.appendProgressText(jobs[j].getJobnum(),
-            // "\nAlignment Object Method Notes\n");
-            // wsInfo.appendProgressText(jobs[j].getJobnum(),
-            // "Calculated with
-            // "+alignment.getMetadata().getProgram().toString());
-            // JBPNote The returned files from a webservice could be
-            // hidden behind icons in the monitor window that,
-            // when clicked, pop up their corresponding data
-          }
         }
       }
     } catch (Exception ex)
@@ -931,7 +488,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
   void displayResults(boolean newFrame)
   {
     // view input or result data for each block
-    List<AlignmentOrder> alorders = new ArrayList<AlignmentOrder>();
+    List<AlignmentOrder> alorders = new ArrayList<>();
     SequenceI[][] results = new SequenceI[jobs.length][];
     AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
     String lastProgram = null;
@@ -1077,7 +634,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     else
     {
       // construct a non-redundant ordering set
-      List<String> names = new ArrayList<String>();
+      List<String> names = new ArrayList<>();
       for (int i = 0, l = alorders.size(); i < l; i++)
       {
         String orderName = " Region " + i;
diff --git a/src/jalview/ws/gui/WsJob.java b/src/jalview/ws/gui/WsJob.java
new file mode 100644 (file)
index 0000000..e5af8c1
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * 
+ */
+package jalview.ws.gui;
+
+import jalview.ws.AWsJob;
+import jalview.ws.api.JobId;
+
+/**
+ * Bean that holds state for a job
+ * 
+ * @author jprocter
+ *
+ */
+public class WsJob extends AWsJob
+{
+
+  public enum JobState
+  {
+    INVALID, READY, SUBMITTED, QUEUED, RUNNING, FINISHED, BROKEN, FAILED,
+    UNKNOWN, SERVERERROR, CANCELLED;
+  };
+
+  JobState state = JobState.UNKNOWN;
+
+  boolean hasResults = false, validInput = false;
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasResults()
+   */
+  @Override
+  public boolean hasResults()
+  {
+    return hasResults;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasValidInput()
+   */
+  @Override
+  public boolean hasValidInput()
+  {
+    return validInput;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isRunning()
+   */
+  @Override
+  public boolean isRunning()
+  {
+    return JobState.RUNNING.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isQueued()
+   */
+  @Override
+  public boolean isQueued()
+  {
+    return JobState.QUEUED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isFinished()
+   */
+  @Override
+  public boolean isFinished()
+  {
+    // TODO isSubjobComplete and finished flags mean same thing ?
+    return JobState.FINISHED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isFailed()
+   */
+  @Override
+  public boolean isFailed()
+  {
+    return JobState.FAILED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isBroken()
+   */
+  @Override
+  public boolean isBroken()
+  {
+    return JobState.BROKEN.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isServerError()
+   */
+  @Override
+  public boolean isServerError()
+  {
+    return JobState.SERVERERROR.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasStatus()
+   */
+  @Override
+  public boolean hasStatus()
+  {
+    return status != null && status.length() > 0;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#getStatus()
+   */
+  @Override
+  public String getStatus()
+  {
+    return status;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasResponse()
+   */
+  @Override
+  public boolean hasResponse()
+  {
+    // TODO Auto-generated method stub
+    return hasStatus();
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#clearResponse()
+   */
+  @Override
+  public void clearResponse()
+  {
+    status = "";
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#getState()
+   */
+  @Override
+  public String getState()
+  {
+    return state.toString();
+  }
+
+  /**
+   * @return the current JobState
+   */
+  public JobState getJobState()
+  {
+    return state;
+  }
+
+  /**
+   * set the job state
+   * 
+   * @param state
+   */
+  public void setState(JobState state)
+  {
+    this.state = state;
+  }
+
+  String status = "";
+
+  /**
+   * Set the log for this job
+   * 
+   * @parag log
+   */
+  public void setStatus(String log)
+  {
+    status = log;
+
+  }
+
+  /*
+   * bean holding submission info for a next-gen ws job 
+   */
+  JobId jobHandle = null;
+
+  /**
+   * stash the handle for the job and mark it as submitted
+   * 
+   * @param align
+   */
+  public void setJobHandle(JobId align)
+  {
+    jobHandle = align;
+    setJobId(jobHandle.getJobId());
+    submitted = true;
+
+  }
+
+  public JobId getJobHandle()
+  {
+    return jobHandle;
+  }
+
+}
index 66569af..7d1b0f5 100644 (file)
@@ -29,9 +29,9 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.james.mime4j.MimeException;
-import org.apache.james.mime4j.descriptor.BodyDescriptor;
 import org.apache.james.mime4j.parser.ContentHandler;
-import org.apache.james.mime4j.parser.Field;
+import org.apache.james.mime4j.stream.BodyDescriptor;
+import org.apache.james.mime4j.stream.Field;
 
 /**
  * ContentHandler for parsing mime encoded messages into Jalview objects. TODO:
@@ -71,7 +71,7 @@ public class JalviewMimeContentHandler implements ContentHandler
   /**
    * sources for data to be parsed
    */
-  List<DataProvider> dataItems = new ArrayList<DataProvider>();
+  List<DataProvider> dataItems = new ArrayList<>();
 
   @Override
   public void body(BodyDescriptor arg0, InputStream arg1)
@@ -176,5 +176,4 @@ public class JalviewMimeContentHandler implements ContentHandler
     // TODO Auto-generated method stub
     return null;
   }
-
 }
index 69e47a3..a5b73da 100644 (file)
  */
 package jalview.ws.jws1;
 
-import jalview.bin.Cache;
-import jalview.bin.Console;
-import jalview.gui.JvOptionPane;
-import jalview.util.MessageManager;
 
 import java.net.URL;
 import java.util.Hashtable;
@@ -35,9 +31,24 @@ import ext.vamsas.IRegistryServiceLocator;
 import ext.vamsas.RegistryServiceSoapBindingStub;
 import ext.vamsas.ServiceHandle;
 import ext.vamsas.ServiceHandles;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.gui.JvOptionPane;
+import jalview.util.MessageManager;
 
-public class Discoverer implements Runnable
+public class Discoverer implements Runnable, ApplicationSingletonI
 {
+  public static Discoverer getInstance()
+  {
+    return (Discoverer) ApplicationSingletonProvider.getInstance(Discoverer.class);
+  }
+
+  private Discoverer()
+  {
+    // use getInstance()
+  }
   ext.vamsas.IRegistry registry; // the root registry service.
 
   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
@@ -112,20 +123,24 @@ public class Discoverer implements Runnable
     return server;
   }
 
-  static private java.net.URL RootServiceURL = null;
+  private java.net.URL RootServiceURL = null;
 
-  static public Vector<URL> ServiceURLList = null;
+  private Vector<URL> ServiceURLList = null;
 
-  static private boolean reallyDiscoverServices = true;
+  public Vector<URL> getServiceURLList() {
+    return ServiceURLList;
+  }
+  
+  private boolean reallyDiscoverServices = true;
 
-  public static java.util.Hashtable<String, Vector<ServiceHandle>> services = null;
+  private java.util.Hashtable<String, Vector<ServiceHandle>> services = null;
   // stored by
   // abstractServiceType
   // string
 
-  public static java.util.Vector<ServiceHandle> serviceList = null;
+  public java.util.Vector<ServiceHandle> serviceList = null;
 
-  static private Vector<URL> getDiscoveryURLS()
+  private Vector<URL> getDiscoveryURLS()
   {
     Vector<URL> urls = new Vector<>();
     String RootServiceURLs = Cache.getDefault("DISCOVERY_URLS",
@@ -176,10 +191,18 @@ public class Discoverer implements Runnable
    */
   static public void doDiscovery()
   {
-    Console.debug("(Re)-Initialising the discovery URL list.");
+    getInstance().discovery();
+  }
+
+  private void discovery()
+  {
+    Console
+            .debug("(Re)-Initialising the discovery URL list.");
     try
     {
-      reallyDiscoverServices = Cache.getDefault("DISCOVERY_START", false);
+      Discoverer d = getInstance();
+      reallyDiscoverServices = Cache
+              .getDefault("DISCOVERY_START", false);
       if (reallyDiscoverServices)
       {
         ServiceURLList = getDiscoveryURLS();
@@ -244,9 +267,9 @@ public class Discoverer implements Runnable
       // JBPNote - should do this a better way!
       if (f.getFaultReason().indexOf("(407)") > -1)
       {
-        if (jalview.gui.Desktop.desktop != null)
+        if (jalview.gui.Desktop.getDesktopPane() != null)
         {
-          JvOptionPane.showMessageDialog(jalview.gui.Desktop.desktop,
+          JvOptionPane.showMessageDialog(jalview.gui.Desktop.getDesktopPane(),
                   MessageManager.getString("label.set_proxy_settings"),
                   MessageManager
                           .getString("label.proxy_authorization_failed"),
@@ -282,7 +305,7 @@ public class Discoverer implements Runnable
    *          Hashtable
    * @return boolean
    */
-  static private boolean buildServiceLists(ServiceHandle[] sh,
+  private boolean buildServiceLists(ServiceHandle[] sh,
           Vector<ServiceHandle> cat,
           Hashtable<String, Vector<ServiceHandle>> sscat)
   {
@@ -343,7 +366,8 @@ public class Discoverer implements Runnable
     }
     while (s_url < ServiceURLList.size())
     {
-      if ((sh = getServices(ServiceURLList.get(s_url))) != null)
+      if ((sh = getServices(
+              ServiceURLList.get(s_url))) != null)
       {
 
         buildServiceLists(sh, cat, sscat);
@@ -373,26 +397,40 @@ public class Discoverer implements Runnable
   @Override
   public void run()
   {
-    final Discoverer discoverer = this;
-    Thread discoverThread = new Thread()
-    {
-      @Override
-      public void run()
-      {
-        Discoverer.doDiscovery();
-        discoverer.discoverServices();
-      }
-    };
-    discoverThread.start();
+    Console.info("Discovering jws1 services");
+    Discoverer.doDiscovery();
+    discoverServices();
   }
 
   /**
    * binding service abstract name to handler class
    */
-  private static Hashtable<String, WS1Client> serviceClientBindings;
+  private Hashtable<String, WS1Client> serviceClientBindings;
 
   public static WS1Client getServiceClient(ServiceHandle sh)
   {
+    return getInstance().getClient(sh);
+  }
+  
+  /**
+   * notes on discovery service 1. need to allow multiple discovery source urls.
+   * 2. user interface to add/control list of urls in preferences notes on
+   * wsclient discovery 1. need a classpath property with list of additional
+   * plugin directories 2. optional config to cite specific bindings between
+   * class name and Abstract service name. 3. precedence for automatic discovery
+   * by using getAbstractName for WSClient - user added plugins override default
+   * plugins ? notes on wsclient gui code for gui attachment now moved to
+   * wsclient implementation. Needs more abstraction but approach seems to work.
+   * is it possible to 'generalise' the data retrieval calls further ? current
+   * methods are very specific (gatherForMSA or gatherForSeqOrMsaSecStrPred),
+   * new methods for conservation (group or alignment), treecalc (aligned
+   * profile), seqannot (sequences selected from dataset, annotation back to
+   * dataset).
+   * 
+   */
+
+  private WS1Client getClient(ServiceHandle sh)
+  {
     if (serviceClientBindings == null)
     {
       // get a list from Config or create below
@@ -401,7 +439,8 @@ public class Discoverer implements Runnable
       serviceClientBindings.put("SecStrPred", new JPredClient());
       serviceClientBindings.put("SeqSearch", new SeqSearchWSClient());
     }
-    WS1Client instance = serviceClientBindings.get(sh.getAbstractName());
+    WS1Client instance = serviceClientBindings
+            .get(sh.getAbstractName());
     if (instance == null)
     {
       System.err.println(
@@ -414,20 +453,8 @@ public class Discoverer implements Runnable
     }
     return instance;
   }
-  /**
-   * notes on discovery service 1. need to allow multiple discovery source urls.
-   * 2. user interface to add/control list of urls in preferences notes on
-   * wsclient discovery 1. need a classpath property with list of additional
-   * plugin directories 2. optional config to cite specific bindings between
-   * class name and Abstract service name. 3. precedence for automatic discovery
-   * by using getAbstractName for WSClient - user added plugins override default
-   * plugins ? notes on wsclient gui code for gui attachment now moved to
-   * wsclient implementation. Needs more abstraction but approach seems to work.
-   * is it possible to 'generalise' the data retrieval calls further ? current
-   * methods are very specific (gatherForMSA or gatherForSeqOrMsaSecStrPred),
-   * new methods for conservation (group or alignment), treecalc (aligned
-   * profile), seqannot (sequences selected from dataset, annotation back to
-   * dataset).
-   * 
-   */
+  public static Hashtable<String, Vector<ServiceHandle>> getServices()
+  {
+    return getInstance().services;
+  }
 }
index cc73d24..a6227e3 100644 (file)
@@ -21,8 +21,8 @@
 package jalview.ws.jws1;
 
 import java.util.Locale;
-
 import jalview.analysis.AlignSeq;
+import jalview.analysis.SeqsetUtils.SequenceInfo;
 import jalview.bin.Console;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SeqCigar;
@@ -36,6 +36,7 @@ import jalview.util.MessageManager;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Hashtable;
+import java.util.Map;
 
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
@@ -129,8 +130,8 @@ public class JPredClient extends WS1Client
         aln[i] = msf[i].getSeq('-');
       }
 
-      Hashtable SequenceInfo = jalview.analysis.SeqsetUtils.uniquify(aln,
-              true);
+      Map<String, SequenceInfo> SequenceInfo = 
+          jalview.analysis.SeqsetUtils.uniquify(aln, true);
       if (viewonly)
       {
         // Remove hidden regions from sequence objects.
@@ -163,7 +164,7 @@ public class JPredClient extends WS1Client
               + (viewonly ? "visible " : "") + "sequence " + seq.getName()
               + " from " + title;
       String seqname = seq.getName();
-      Hashtable SequenceInfo = jalview.analysis.SeqsetUtils
+      SequenceInfo SequenceInfo = jalview.analysis.SeqsetUtils
               .SeqCharacterHash(seq);
       if (viewonly)
       {
@@ -251,8 +252,8 @@ public class JPredClient extends WS1Client
       aln[i] = new jalview.datamodel.Sequence(msf[i]);
     }
 
-    Hashtable SequenceInfo = jalview.analysis.SeqsetUtils.uniquify(aln,
-            true);
+    Map<String, SequenceInfo> SequenceInfo = 
+        jalview.analysis.SeqsetUtils.uniquify(aln, true);
 
     Jpred server = locateWebService();
     if (server == null)
@@ -280,7 +281,7 @@ public class JPredClient extends WS1Client
     String altitle = "JPred prediction for sequence " + seq.getName()
             + " from " + title;
 
-    Hashtable SequenceInfo = jalview.analysis.SeqsetUtils
+    SequenceInfo SequenceInfo = jalview.analysis.SeqsetUtils
             .SeqCharacterHash(seq);
 
     Jpred server = locateWebService();
@@ -306,7 +307,7 @@ public class JPredClient extends WS1Client
     WsURL = "http://www.compbio.dundee.ac.uk/JalviewWS/services/jpred";
 
     WebserviceInfo wsInfo = new WebserviceInfo(WebServiceJobTitle,
-            WebServiceReference, true);
+            WebServiceReference, Desktop.FRAME_MAKE_VISIBLE);
 
     return wsInfo;
   }
@@ -325,7 +326,7 @@ public class JPredClient extends WS1Client
 
     } catch (Exception ex)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.secondary_structure_prediction_service_couldnt_be_located",
                       new String[]
index a39945e..06f80c4 100644 (file)
@@ -22,6 +22,7 @@ package jalview.ws.jws1;
 
 import jalview.analysis.AlignSeq;
 import jalview.analysis.SeqsetUtils;
+import jalview.analysis.SeqsetUtils.SequenceInfo;
 import jalview.bin.Console;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -47,6 +48,7 @@ import jalview.ws.WSClientI;
 
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 import vamsas.objects.simple.JpredResult;
 
@@ -66,7 +68,7 @@ class JPredThread extends JWS1Thread implements WSClientI
 
     vamsas.objects.simple.Msfalignment msa;
 
-    java.util.Hashtable SequenceInfo = null;
+    Object SequenceInfo = null;
 
     int msaIndex = 0; // the position of the original sequence in the array of
 
@@ -156,7 +158,7 @@ class JPredThread extends JWS1Thread implements WSClientI
             {
               sqs[i] = al.getSequenceAt(i);
             }
-            if (!SeqsetUtils.deuniquify(SequenceInfo, sqs))
+            if (!SeqsetUtils.deuniquify((Map<String, SequenceInfo>)SequenceInfo, sqs))
             {
               throw (new Exception(MessageManager.getString(
                       "exception.couldnt_recover_sequence_properties_for_alignment")));
@@ -211,7 +213,7 @@ class JPredThread extends JWS1Thread implements WSClientI
         }
 
         if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash(
-                al.getSequenceAt(FirstSeq), SequenceInfo))
+                al.getSequenceAt(FirstSeq), (SequenceInfo)SequenceInfo))
         {
           throw (new Exception(MessageManager.getString(
                   "exception.couldnt_recover_sequence_props_for_jnet_query")));
@@ -332,7 +334,7 @@ class JPredThread extends JWS1Thread implements WSClientI
       }
     }
 
-    public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap)
+    public JPredJob(Object SequenceInfo, SequenceI seq, int[] delMap)
     {
       super();
       this.predMap = delMap;
@@ -351,7 +353,7 @@ class JPredThread extends JWS1Thread implements WSClientI
       }
     }
 
-    public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap)
+    public JPredJob(Object SequenceInfo, SequenceI[] msf, int[] delMap)
     {
       this(SequenceInfo, msf[0], delMap);
       if (sequence != null)
@@ -387,7 +389,7 @@ class JPredThread extends JWS1Thread implements WSClientI
   }
 
   JPredThread(WebserviceInfo wsinfo, String altitle,
-          ext.vamsas.Jpred server, String wsurl, Hashtable SequenceInfo,
+          ext.vamsas.Jpred server, String wsurl, SequenceInfo SequenceInfo,
           SequenceI seq, int[] delMap, AlignmentView alview,
           AlignFrame alframe)
   {
@@ -406,9 +408,9 @@ class JPredThread extends JWS1Thread implements WSClientI
   }
 
   JPredThread(WebserviceInfo wsinfo, String altitle,
-          ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf,
-          int[] delMap, AlignmentView alview, AlignFrame alframe,
-          String wsurl)
+          ext.vamsas.Jpred server, Map<String, SequenceInfo> SequenceInfo,
+          SequenceI[] msf, int[] delMap, AlignmentView alview, 
+          AlignFrame alframe, String wsurl)
   {
     this(wsinfo, altitle, server, wsurl, alview, alframe);
     JPredJob job = new JPredJob(SequenceInfo, msf, delMap);
index dad314b..bb0e43d 100644 (file)
@@ -53,8 +53,6 @@ public class MsaWSClient extends WS1Client
    */
   ext.vamsas.MuscleWS server;
 
-  AlignFrame alignFrame;
-
   /**
    * Creates a new MsaWSClient object that uses a service given by an externally
    * retrieved ServiceHandle
@@ -80,7 +78,7 @@ public class MsaWSClient extends WS1Client
     alignFrame = _alignFrame;
     if (!sh.getAbstractName().equals("MsaWS"))
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_msa_service",
                       new String[]
@@ -93,7 +91,7 @@ public class MsaWSClient extends WS1Client
 
     if ((wsInfo = setWebService(sh)) == null)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), MessageManager
               .formatMessage("label.msa_service_is_unknown", new String[]
               { sh.getName() }),
               MessageManager.getString("label.internal_jalview_error"),
index e027038..97efe9f 100644 (file)
@@ -36,7 +36,6 @@ import jalview.ws.AWsJob;
 import jalview.ws.JobStateSummary;
 import jalview.ws.WSClientI;
 
-import java.util.Hashtable;
 import java.util.Vector;
 
 import vamsas.objects.simple.MsaResult;
@@ -78,8 +77,6 @@ class MsaWSThread extends JWS1Thread implements WSClientI
 
     }
 
-    Hashtable SeqNames = new Hashtable();
-
     Vector emptySeqs = new Vector();
 
     /**
index 0c67063..04fb4b9 100644 (file)
@@ -57,8 +57,6 @@ public class SeqSearchWSClient extends WS1Client
    */
   ext.vamsas.SeqSearchI server;
 
-  AlignFrame alignFrame;
-
   /**
    * Creates a new MsaWSClient object that uses a service given by an externally
    * retrieved ServiceHandle
@@ -85,7 +83,7 @@ public class SeqSearchWSClient extends WS1Client
     // name to service client name
     if (!sh.getAbstractName().equals(this.getServiceActionKey()))
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_seq_search_service",
                       new String[]
@@ -98,7 +96,7 @@ public class SeqSearchWSClient extends WS1Client
 
     if ((wsInfo = setWebService(sh)) == null)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.seq_search_service_is_unknown", new String[]
                       { sh.getName() }),
index 0f28230..faba80c 100644 (file)
@@ -37,7 +37,6 @@ import jalview.ws.JobStateSummary;
 import jalview.ws.WSClientI;
 
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.Map;
 import java.util.Vector;
 
@@ -77,8 +76,6 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
 
     }
 
-    Hashtable SeqNames = new Hashtable();
-
     Vector emptySeqs = new Vector();
 
     /**
index 6fa41fc..7bffe43 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.ws.jws1;
 
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
 import jalview.ws.WSClient;
@@ -92,8 +93,8 @@ public abstract class WS1Client extends WSClient
     WebserviceInfo wsInfo = null;
     if (!headless)
     {
-      wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference,
-              true);
+      wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference, 
+              Desktop.FRAME_MAKE_VISIBLE);
     }
     return wsInfo;
   }
diff --git a/src/jalview/ws/jws2/AAConClient.java b/src/jalview/ws/jws2/AAConClient.java
deleted file mode 100644 (file)
index 327864a..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.jws2;
-
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.gui.AlignFrame;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-import jalview.ws.uimodel.AlignAnalysisUIText;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import compbio.data.sequence.FastaSequence;
-import compbio.data.sequence.Score;
-import compbio.metadata.Argument;
-
-public class AAConClient extends JabawsCalcWorker
-{
-
-  public AAConClient(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<Argument> paramset)
-  {
-    super(service, alignFrame, preset, paramset);
-    submitGaps = true;
-    alignedSeqs = true;
-    nucleotidesAllowed = false;
-    proteinAllowed = true;
-    filterNonStandardResidues = true;
-    gapMap = new boolean[0];
-    initViewportParams();
-  }
-
-  @Override
-  public String getServiceActionText()
-  {
-    return "calculating Amino acid consensus using AACon service";
-  }
-
-  /**
-   * update the consensus annotation from the sequence profile data using
-   * current visualization settings.
-   */
-
-  @Override
-  public void updateResultAnnotation(boolean immediate)
-  {
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
-    {
-      Map<String, TreeSet<Score>> scoremap = scoremanager.asMap();
-      int alWidth = alignViewport.getAlignment().getWidth();
-      ArrayList<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-      for (String score : scoremap.keySet())
-      {
-        Set<Score> scores = scoremap.get(score);
-        for (Score scr : scores)
-        {
-          if (scr.getRanges() != null && scr.getRanges().size() > 0)
-          {
-            /**
-             * annotation in range annotation = findOrCreate(scr.getMethod(),
-             * true, null, null); Annotation[] elm = new Annotation[alWidth];
-             * Iterator<Float> vals = scr.getScores().iterator(); for (Range rng
-             * : scr.getRanges()) { float val = vals.next().floatValue(); for
-             * (int i = rng.from; i <= rng.to; i++) { elm[i] = new
-             * Annotation("", "", ' ', val); } } annotation.annotations = elm;
-             * annotation.validateRangeAndDisplay();
-             */
-          }
-          else
-          {
-            createAnnotationRowsForScores(ourAnnot, getCalcId(), alWidth,
-                    scr);
-          }
-        }
-      }
-
-      if (ourAnnot.size() > 0)
-      {
-        updateOurAnnots(ourAnnot);
-      }
-    }
-  }
-
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
-  {
-    return (seqs.size() > 1);
-  }
-
-  @Override
-  public String getCalcId()
-  {
-    return CALC_ID;
-  }
-
-  private static String CALC_ID = "jabaws2.AACon";
-
-  public static AlignAnalysisUIText getAlignAnalysisUITest()
-  {
-    return new AlignAnalysisUIText(
-            compbio.ws.client.Services.AAConWS.toString(),
-            jalview.ws.jws2.AAConClient.class, CALC_ID, false, true, true,
-            MessageManager.getString("label.aacon_calculations"),
-            MessageManager.getString("tooltip.aacon_calculations"),
-            MessageManager.getString("label.aacon_settings"),
-            MessageManager.getString("tooltip.aacon_settings"));
-  }
-}
diff --git a/src/jalview/ws/jws2/AADisorderClient.java b/src/jalview/ws/jws2/AADisorderClient.java
deleted file mode 100644 (file)
index a706896..0000000
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * 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.jws2;
-
-import jalview.api.FeatureColourI;
-import jalview.bin.Console;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.GraphLine;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.schemes.FeatureColour;
-import jalview.util.ColorUtils;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import compbio.data.sequence.FastaSequence;
-import compbio.data.sequence.Range;
-import compbio.data.sequence.Score;
-import compbio.data.sequence.ScoreManager.ScoreHolder;
-import compbio.metadata.Argument;
-
-public class AADisorderClient extends JabawsCalcWorker
-{
-
-  private static final String THRESHOLD = "THRESHOLD";
-
-  private static final String RANGE = "RANGE";
-
-  String typeName;
-
-  String methodName;
-
-  String groupName;
-
-  AlignFrame af;
-
-  public AADisorderClient(Jws2Instance sh, AlignFrame alignFrame,
-          WsParamSetI thePreset, List<Argument> paramset)
-  {
-    super(sh, alignFrame, thePreset, paramset);
-    af = alignFrame;
-    typeName = sh.action;
-    methodName = sh.serviceType;
-
-    submitGaps = false;
-    alignedSeqs = false;
-    nucleotidesAllowed = false;
-    proteinAllowed = true;
-    bySequence = true;
-  }
-
-  @Override
-  public String getServiceActionText()
-  {
-    return "Submitting amino acid sequences for disorder prediction.";
-  }
-
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
-  {
-    return (seqs.size() > 0);
-  }
-
-  private static Map<String, Map<String, String[]>> featureMap;
-
-  private static Map<String, Map<String, Map<String, Object>>> annotMap;
-
-  private static String DONTCOMBINE = "DONTCOMBINE";
-
-  private static String INVISIBLE = "INVISIBLE";
-  static
-  {
-    // TODO: turn this into some kind of configuration file that's a bit easier
-    // to edit
-    featureMap = new HashMap<>();
-    Map<String, String[]> fmap;
-    featureMap.put(compbio.ws.client.Services.IUPredWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("Glob",
-            new String[]
-            { "Globular Domain", "Predicted globular domain" });
-    featureMap.put(compbio.ws.client.Services.JronnWS.toString(),
-            fmap = new HashMap<>());
-    featureMap.put(compbio.ws.client.Services.DisemblWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("REM465", new String[] { "REM465", "Missing density" });
-    fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" });
-    fmap.put("COILS", new String[] { "COILS", "Random coil" });
-    featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("GlobDoms",
-            new String[]
-            { "Globular Domain", "Predicted globular domain" });
-    fmap.put("Disorder",
-            new String[]
-            { "Protein Disorder", "Probable unstructured peptide region" });
-    Map<String, Map<String, Object>> amap;
-    annotMap = new HashMap<>();
-    annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
-            amap = new HashMap<>());
-    amap.put("Dydx", new HashMap<String, Object>());
-    amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE);
-    amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 });
-    amap.get("Dydx").put(RANGE, new float[] { -1, +1 });
-
-    amap.put("SmoothedScore", new HashMap<String, Object>());
-    amap.get("SmoothedScore").put(INVISIBLE, INVISIBLE);
-    amap.put("RawScore", new HashMap<String, Object>());
-    amap.get("RawScore").put(INVISIBLE, INVISIBLE);
-    annotMap.put(compbio.ws.client.Services.DisemblWS.toString(),
-            amap = new HashMap<>());
-    amap.put("COILS", new HashMap<String, Object>());
-    amap.put("HOTLOOPS", new HashMap<String, Object>());
-    amap.put("REM465", new HashMap<String, Object>());
-    amap.get("COILS").put(THRESHOLD, new double[] { 1, 0.516 });
-    amap.get("COILS").put(RANGE, new float[] { 0, 1 });
-
-    amap.get("HOTLOOPS").put(THRESHOLD, new double[] { 1, 0.6 });
-    amap.get("HOTLOOPS").put(RANGE, new float[] { 0, 1 });
-    amap.get("REM465").put(THRESHOLD, new double[] { 1, 0.1204 });
-    amap.get("REM465").put(RANGE, new float[] { 0, 1 });
-
-    annotMap.put(compbio.ws.client.Services.IUPredWS.toString(),
-            amap = new HashMap<>());
-    amap.put("Long", new HashMap<String, Object>());
-    amap.put("Short", new HashMap<String, Object>());
-    amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("Long").put(RANGE, new float[] { 0, 1 });
-    amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("Short").put(RANGE, new float[] { 0, 1 });
-    annotMap.put(compbio.ws.client.Services.JronnWS.toString(),
-            amap = new HashMap<>());
-    amap.put("JRonn", new HashMap<String, Object>());
-    amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("JRonn").put(RANGE, new float[] { 0, 1 });
-  }
-
-  @Override
-  public void updateResultAnnotation(boolean immediate)
-  {
-
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
-    {
-      Map<String, String[]> featureTypeMap = featureMap
-              .get(service.serviceType);
-      Map<String, Map<String, Object>> annotTypeMap = annotMap
-              .get(service.serviceType);
-      boolean dispFeatures = false;
-      Map<String, Object> fc = new Hashtable<>();
-      List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-      /**
-       * grouping for any annotation rows created
-       */
-      int graphGroup = 1;
-      if (alignViewport.getAlignment().getAlignmentAnnotation() != null)
-      {
-        for (AlignmentAnnotation ala : alignViewport.getAlignment()
-                .getAlignmentAnnotation())
-        {
-          if (ala.graphGroup > graphGroup)
-          {
-            graphGroup = ala.graphGroup;
-          }
-        }
-      }
-
-      for (String seqId : seqNames.keySet())
-      {
-        boolean sameGroup = false;
-        SequenceI dseq, aseq, seq = seqNames.get(seqId);
-        int base = seq.findPosition(start) - 1;
-        aseq = seq;
-        while ((dseq = seq).getDatasetSequence() != null)
-        {
-          seq = seq.getDatasetSequence();
-        }
-        ScoreHolder scores = null;
-        try
-        {
-          scores = scoremanager.getAnnotationForSequence(seqId);
-        } catch (Exception q)
-        {
-          Console.info("Couldn't recover disorder prediction for sequence "
-                  + seq.getName() + "(Prediction name was " + seqId + ")"
-                  + "\nSee http://issues.jalview.org/browse/JAL-1319 for one possible reason why disorder predictions might fail.");
-        }
-        float last = Float.NaN, val = Float.NaN;
-        int lastAnnot = ourAnnot.size();
-        if (scores != null && scores.scores != null)
-        {
-          for (Score scr : scores.scores)
-          {
-
-            if (scr.getRanges() != null && scr.getRanges().size() > 0)
-            {
-              Iterator<Float> vals = scr.getScores().iterator();
-              // make features on sequence
-              for (Range rn : scr.getRanges())
-              {
-
-                SequenceFeature sf;
-                String[] type = featureTypeMap.get(scr.getMethod());
-                if (type == null)
-                {
-                  // create a default type for this feature
-                  type = new String[] {
-                      typeName + " (" + scr.getMethod() + ")",
-                      service.getActionText() };
-                }
-                if (vals.hasNext())
-                {
-                  val = vals.next().floatValue();
-                  sf = new SequenceFeature(type[0], type[1], base + rn.from,
-                          base + rn.to, val, methodName);
-                }
-                else
-                {
-                  sf = new SequenceFeature(type[0], type[1], base + rn.from,
-                          base + rn.to, methodName);
-                }
-                dseq.addSequenceFeature(sf);
-                if (last != val && !Float.isNaN(last))
-                {
-                  fc.put(sf.getType(), sf);
-                }
-                last = val;
-                dispFeatures = true;
-              }
-            }
-            else
-            {
-              if (scr.getScores().size() == 0)
-              {
-                continue;
-              }
-              String typename, calcName;
-              AlignmentAnnotation annot = createAnnotationRowsForScores(
-                      ourAnnot,
-                      typename = service.serviceType + " ("
-                              + scr.getMethod() + ")",
-                      calcName = service.getServiceTypeURI() + "/"
-                              + scr.getMethod(),
-                      aseq, base + 1, scr);
-              annot.graph = AlignmentAnnotation.LINE_GRAPH;
-
-              Map<String, Object> styleMap = (annotTypeMap == null) ? null
-                      : annotTypeMap.get(scr.getMethod());
-
-              annot.visible = (styleMap == null
-                      || styleMap.get(INVISIBLE) == null);
-              double[] thrsh = (styleMap == null) ? null
-                      : (double[]) styleMap.get(THRESHOLD);
-              float[] range = (styleMap == null) ? null
-                      : (float[]) styleMap.get(RANGE);
-              if (range != null)
-              {
-                annot.graphMin = range[0];
-                annot.graphMax = range[1];
-              }
-              if (styleMap == null || styleMap.get(DONTCOMBINE) == null)
-              {
-                {
-                  if (!sameGroup)
-                  {
-                    graphGroup++;
-                    sameGroup = true;
-                  }
-
-                  annot.graphGroup = graphGroup;
-                }
-              }
-
-              annot.description = "<html>" + service.getActionText()
-                      + " - raw scores";
-              if (thrsh != null)
-              {
-                String threshNote = (thrsh[0] > 0 ? "Above " : "Below ")
-                        + thrsh[1] + " indicates disorder";
-                annot.threshold = new GraphLine((float) thrsh[1],
-                        threshNote, Color.red);
-                annot.description += "<br/>" + threshNote;
-              }
-              annot.description += "</html>";
-              Color col = ColorUtils
-                      .createColourFromName(typeName + scr.getMethod());
-              for (int p = 0, ps = annot.annotations.length; p < ps; p++)
-              {
-                if (annot.annotations[p] != null)
-                {
-                  annot.annotations[p].colour = col;
-                }
-              }
-              annot._linecolour = col;
-              // finally, update any dataset annotation
-              replaceAnnotationOnAlignmentWith(annot, typename, calcName,
-                      aseq);
-            }
-          }
-        }
-        if (lastAnnot + 1 == ourAnnot.size())
-        {
-          // remove singleton alignment annotation row
-          ourAnnot.get(lastAnnot).graphGroup = -1;
-        }
-      }
-      {
-        if (dispFeatures)
-        {
-          jalview.api.FeatureRenderer fr = ((jalview.gui.AlignmentPanel) ap)
-                  .cloneFeatureRenderer();
-          for (String ft : fc.keySet())
-          {
-            FeatureColourI gc = fr.getFeatureStyle(ft);
-            if (gc.isSimpleColour())
-            {
-              // set graduated color as fading to white for minimum, and
-              // autoscaling to values on alignment
-              FeatureColourI ggc = new FeatureColour(gc.getColour(),
-                      Color.white, gc.getColour(), Color.white,
-                      Float.MIN_VALUE, Float.MAX_VALUE);
-              ggc.setAutoScaled(true);
-              fr.setColour(ft, ggc);
-            }
-          }
-          // TODO: JAL-1150 - create sequence feature settings API for defining
-          // styles and enabling/disabling feature overlay on alignment panel
-          ((jalview.gui.AlignmentPanel) ap).updateFeatureRendererFrom(fr);
-          if (af.alignPanel == ap)
-          {
-            // only do this if the alignFrame is currently showing this view.
-            af.setShowSeqFeatures(true);
-          }
-        }
-        if (ourAnnot.size() > 0)
-        {
-          // Modify the visible annotation on the alignment viewport with the
-          // new alignment annotation rows created.
-          updateOurAnnots(ourAnnot);
-          ap.adjustAnnotationHeight();
-          ap.paintAlignment(true, true);
-        }
-      }
-    }
-  }
-
-  @Override
-  public String getCalcId()
-  {
-    // Disorder predictions are not dynamically updated so we return null
-    return null;
-  }
-
-}
diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java
deleted file mode 100644 (file)
index 9d56de4..0000000
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- * 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.jws2;
-
-import jalview.analysis.AlignSeq;
-import jalview.analysis.SeqsetUtils;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Console;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AnnotatedCollectionI;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.IProgressIndicator;
-import jalview.gui.IProgressIndicatorHandler;
-import jalview.schemes.ResidueProperties;
-import jalview.workers.AlignCalcWorker;
-import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.dm.JabaWsParamSet;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import compbio.data.sequence.FastaSequence;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.JobSubmissionException;
-import compbio.metadata.Option;
-import compbio.metadata.ResultNotAvailableException;
-
-public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
-{
-
-  protected Jws2Instance service;
-
-  protected WsParamSetI preset;
-
-  protected List<Argument> arguments;
-
-  protected IProgressIndicator guiProgress;
-
-  protected boolean submitGaps = true;
-
-  /**
-   * by default, we filter out non-standard residues before submission
-   */
-  protected boolean filterNonStandardResidues = true;
-
-  /**
-   * Recover any existing parameters for this service
-   */
-  protected void initViewportParams()
-  {
-    if (getCalcId() != null)
-    {
-      ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
-              getCalcId(),
-              new AAConSettings(true, service, this.preset,
-                      (arguments != null)
-                              ? JabaParamStore.getJwsArgsfromJaba(arguments)
-                              : null),
-              true);
-    }
-  }
-
-  /**
-   * 
-   * @return null or a string used to recover all annotation generated by this
-   *         worker
-   */
-  public abstract String getCalcId();
-
-  public WsParamSetI getPreset()
-  {
-    return preset;
-  }
-
-  public List<Argument> getArguments()
-  {
-    return arguments;
-  }
-
-  /**
-   * reconfigure and restart the AAConClient. This method will spawn a new
-   * thread that will wait until any current jobs are finished, modify the
-   * parameters and restart the conservation calculation with the new values.
-   * 
-   * @param newpreset
-   * @param newarguments
-   */
-  public void updateParameters(final WsParamSetI newpreset,
-          final List<Argument> newarguments)
-  {
-    preset = newpreset;
-    arguments = newarguments;
-    calcMan.startWorker(this);
-    initViewportParams();
-  }
-
-  public List<Option> getJabaArguments()
-  {
-    List<Option> newargs = new ArrayList<>();
-    if (preset != null && preset instanceof JabaWsParamSet)
-    {
-      newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-    }
-    if (arguments != null && arguments.size() > 0)
-    {
-      for (Argument rg : arguments)
-      {
-        if (Option.class.isAssignableFrom(rg.getClass()))
-        {
-          newargs.add((Option) rg);
-        }
-      }
-    }
-    return newargs;
-  }
-
-  protected boolean alignedSeqs = true;
-
-  protected boolean nucleotidesAllowed = false;
-
-  protected boolean proteinAllowed = false;
-
-  /**
-   * record sequences for mapping result back to afterwards
-   */
-  protected boolean bySequence = false;
-
-  protected Map<String, SequenceI> seqNames;
-
-  protected boolean[] gapMap;
-
-  int realw;
-
-  protected int start;
-
-  int end;
-
-  public AbstractJabaCalcWorker(AlignViewportI alignViewport,
-          AlignmentViewPanel alignPanel)
-  {
-    super(alignViewport, alignPanel);
-  }
-
-  public AbstractJabaCalcWorker(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<Argument> paramset)
-  {
-    this(alignFrame.getCurrentView(), alignFrame.alignPanel);
-    this.guiProgress = alignFrame;
-    this.preset = preset;
-    this.arguments = paramset;
-    this.service = service;
-  }
-
-  /**
-   * 
-   * @return true if the submission thread should attempt to submit data
-   */
-  abstract boolean hasService();
-
-  volatile String rslt = "JOB NOT DEFINED";
-
-  @Override
-  public void run()
-  {
-    if (!hasService())
-    {
-      return;
-    }
-    long progressId = -1;
-
-    int serverErrorsLeft = 3;
-
-    StringBuffer msg = new StringBuffer();
-    try
-    {
-      if (checkDone())
-      {
-        return;
-      }
-      List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(
-              alignViewport.getAlignment(),
-              bySequence ? alignViewport.getSelectionGroup() : null);
-
-      if (seqs == null || !checkValidInputSeqs(true, seqs))
-      {
-        calcMan.workerComplete(this);
-        return;
-      }
-
-      AlignmentAnnotation[] aa = alignViewport.getAlignment()
-              .getAlignmentAnnotation();
-      if (guiProgress != null)
-      {
-        guiProgress.setProgressBar("JABA " + getServiceActionText(),
-                progressId = System.currentTimeMillis());
-      }
-      rslt = submitToService(seqs);
-      if (guiProgress != null)
-      {
-        guiProgress.registerHandler(progressId,
-                new IProgressIndicatorHandler()
-                {
-
-                  @Override
-                  public boolean cancelActivity(long id)
-                  {
-                    cancelCurrentJob();
-                    return true;
-                  }
-
-                  @Override
-                  public boolean canCancel()
-                  {
-                    return true;
-                  }
-                });
-      }
-      boolean finished = false;
-      long rpos = 0;
-      do
-      {
-        JobStatus status = getJobStatus(rslt);
-        if (status.equals(JobStatus.FINISHED))
-        {
-          finished = true;
-        }
-        if (calcMan.isPending(this) && isInteractiveUpdate())
-        {
-          finished = true;
-          // cancel this job and yield to the new job
-          try
-          {
-            if (cancelJob(rslt))
-            {
-              System.err.println("Cancelled AACon job: " + rslt);
-            }
-            else
-            {
-              System.err.println("FAILED TO CANCEL AACon job: " + rslt);
-            }
-
-          } catch (Exception x)
-          {
-
-          }
-          rslt = "CANCELLED JOB";
-          return;
-        }
-        long cpos;
-        ChunkHolder stats = null;
-        do
-        {
-          cpos = rpos;
-          boolean retry = false;
-          do
-          {
-            try
-            {
-              stats = pullExecStatistics(rslt, rpos);
-            } catch (Exception x)
-            {
-
-              if (x.getMessage().contains(
-                      "Position in a file could not be negative!"))
-              {
-                // squash index out of bounds exception- seems to happen for
-                // disorder predictors which don't (apparently) produce any
-                // progress information and JABA server throws an exception
-                // because progress length is -1.
-                stats = null;
-              }
-              else
-              {
-                if (--serverErrorsLeft > 0)
-                {
-                  retry = true;
-                  try
-                  {
-                    Thread.sleep(200);
-                  } catch (InterruptedException q)
-                  {
-                  }
-                  ;
-                }
-                else
-                {
-                  throw x;
-                }
-              }
-            }
-          } while (retry);
-          if (stats != null)
-          {
-            System.out.print(stats.getChunk());
-            msg.append(stats);
-            rpos = stats.getNextPosition();
-          }
-        } while (stats != null && rpos > cpos);
-
-        if (!finished && status.equals(JobStatus.FAILED))
-        {
-          try
-          {
-            Thread.sleep(200);
-          } catch (InterruptedException x)
-          {
-          }
-          ;
-        }
-      } while (!finished);
-      if (serverErrorsLeft > 0)
-      {
-        try
-        {
-          Thread.sleep(200);
-        } catch (InterruptedException x)
-        {
-        }
-        if (collectAnnotationResultsFor(rslt))
-        {
-          Console.debug("Updating result annotation from Job " + rslt
-                  + " at " + service.getUri());
-          updateResultAnnotation(true);
-          ap.adjustAnnotationHeight();
-        }
-      }
-    }
-
-    catch (JobSubmissionException x)
-    {
-
-      System.err.println(
-              "submission error with " + getServiceActionText() + " :");
-      x.printStackTrace();
-      calcMan.disableWorker(this);
-    } catch (ResultNotAvailableException x)
-    {
-      System.err.println("collection error:\nJob ID: " + rslt);
-      x.printStackTrace();
-      calcMan.disableWorker(this);
-
-    } catch (OutOfMemoryError error)
-    {
-      calcMan.disableWorker(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      ap.raiseOOMWarning(getServiceActionText(), error);
-    } catch (Exception x)
-    {
-      calcMan.disableWorker(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      System.err
-              .println("Blacklisting worker due to unexpected exception:");
-      x.printStackTrace();
-    } finally
-    {
-
-      calcMan.workerComplete(this);
-      if (ap != null)
-      {
-        calcMan.workerComplete(this);
-        if (guiProgress != null && progressId != -1)
-        {
-          guiProgress.setProgressBar("", progressId);
-        }
-        // TODO: may not need to paintAlignment again !
-        ap.paintAlignment(false, false);
-      }
-      if (msg.length() > 0)
-      {
-        // TODO: stash message somewhere in annotation or alignment view.
-        // code below shows result in a text box popup
-        /*
-         * jalview.gui.CutAndPasteTransfer cap = new
-         * jalview.gui.CutAndPasteTransfer(); cap.setText(msg.toString());
-         * jalview.gui.Desktop.addInternalFrame(cap,
-         * "Job Status for "+getServiceActionText(), 600, 400);
-         */
-      }
-    }
-
-  }
-
-  /**
-   * validate input for dynamic/non-dynamic update context
-   * 
-   * @param dynamic
-   * @param seqs
-   * @return true if input is valid
-   */
-  abstract boolean checkValidInputSeqs(boolean dynamic,
-          List<FastaSequence> seqs);
-
-  abstract String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException;
-
-  abstract boolean cancelJob(String rslt) throws Exception;
-
-  abstract JobStatus getJobStatus(String rslt) throws Exception;
-
-  abstract ChunkHolder pullExecStatistics(String rslt, long rpos);
-
-  abstract boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException;
-
-  public void cancelCurrentJob()
-  {
-    try
-    {
-      String id = rslt;
-      if (cancelJob(rslt))
-      {
-        System.err.println("Cancelled job " + id);
-      }
-      else
-      {
-        System.err.println("Job " + id + " couldn't be cancelled.");
-      }
-    } catch (Exception q)
-    {
-      q.printStackTrace();
-    }
-  }
-
-  /**
-   * Interactive updating. Analysis calculations that work on the currently
-   * displayed alignment data should cancel existing jobs when the input data
-   * has changed.
-   * 
-   * @return true if a running job should be cancelled because new input data is
-   *         available for analysis
-   */
-  abstract boolean isInteractiveUpdate();
-
-  public List<FastaSequence> getInputSequences(AlignmentI alignment,
-          AnnotatedCollectionI inputSeqs)
-  {
-    if (alignment == null || alignment.getWidth() <= 0
-            || alignment.getSequences() == null || alignment.isNucleotide()
-                    ? !nucleotidesAllowed
-                    : !proteinAllowed)
-    {
-      return null;
-    }
-    if (inputSeqs == null || inputSeqs.getWidth() <= 0
-            || inputSeqs.getSequences() == null
-            || inputSeqs.getSequences().size() < 1)
-    {
-      inputSeqs = alignment;
-    }
-
-    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
-
-    int minlen = 10;
-    int ln = -1;
-    if (bySequence)
-    {
-      seqNames = new HashMap<>();
-    }
-    gapMap = new boolean[0];
-    start = inputSeqs.getStartRes();
-    end = inputSeqs.getEndRes();
-
-    for (SequenceI sq : (inputSeqs.getSequences()))
-    {
-      if (bySequence
-              ? sq.findPosition(end + 1)
-                      - sq.findPosition(start + 1) > minlen - 1
-              : sq.getEnd() - sq.getStart() > minlen - 1)
-      {
-        String newname = SeqsetUtils.unique_name(seqs.size() + 1);
-        // make new input sequence with or without gaps
-        if (seqNames != null)
-        {
-          seqNames.put(newname, sq);
-        }
-        FastaSequence seq;
-        if (submitGaps)
-        {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
-                  sq.getSequenceAsString()));
-          if (gapMap == null || gapMap.length < seq.getSequence().length())
-          {
-            boolean[] tg = gapMap;
-            gapMap = new boolean[seq.getLength()];
-            System.arraycopy(tg, 0, gapMap, 0, tg.length);
-            for (int p = tg.length; p < gapMap.length; p++)
-            {
-              gapMap[p] = false; // init as a gap
-            }
-          }
-          for (int apos : sq.gapMap())
-          {
-            char sqc = sq.getCharAt(apos);
-            if (!filterNonStandardResidues
-                    || (sq.isProtein() ? ResidueProperties.aaIndex[sqc] < 20
-                            : ResidueProperties.nucleotideIndex[sqc] < 5))
-            {
-              gapMap[apos] = true; // aligned and real amino acid residue
-            }
-            ;
-          }
-        }
-        else
-        {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
-                  AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
-                          sq.getSequenceAsString(start, end + 1))));
-        }
-        if (seq.getSequence().length() > ln)
-        {
-          ln = seq.getSequence().length();
-        }
-      }
-    }
-    if (alignedSeqs && submitGaps)
-    {
-      realw = 0;
-      for (int i = 0; i < gapMap.length; i++)
-      {
-        if (gapMap[i])
-        {
-          realw++;
-        }
-      }
-      // try real hard to return something submittable
-      // TODO: some of AAcon measures need a minimum of two or three amino
-      // acids at each position, and AAcon doesn't gracefully degrade.
-      for (int p = 0; p < seqs.size(); p++)
-      {
-        FastaSequence sq = seqs.get(p);
-        int l = sq.getSequence().length();
-        // strip gapped columns
-        char[] padded = new char[realw],
-                orig = sq.getSequence().toCharArray();
-        for (int i = 0, pp = 0; i < realw; pp++)
-        {
-          if (gapMap[pp])
-          {
-            if (orig.length > pp)
-            {
-              padded[i++] = orig[pp];
-            }
-            else
-            {
-              padded[i++] = '-';
-            }
-          }
-        }
-        seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
-                new String(padded)));
-      }
-    }
-    return seqs;
-  }
-
-  @Override
-  public void updateAnnotation()
-  {
-    updateResultAnnotation(false);
-  }
-
-  public abstract void updateResultAnnotation(boolean immediate);
-
-  public abstract String getServiceActionText();
-
-  /**
-   * notify manager that we have started, and wait for a free calculation slot
-   * 
-   * @return true if slot is obtained and work still valid, false if another
-   *         thread has done our work for us.
-   */
-  protected boolean checkDone()
-  {
-    calcMan.notifyStart(this);
-    ap.paintAlignment(false, false);
-    while (!calcMan.notifyWorking(this))
-    {
-      if (calcMan.isWorking(this))
-      {
-        return true;
-      }
-      try
-      {
-        if (ap != null)
-        {
-          ap.paintAlignment(false, false);
-        }
-
-        Thread.sleep(200);
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      }
-    }
-    if (alignViewport.isClosed())
-    {
-      abortAndDestroy();
-      return true;
-    }
-    return false;
-  }
-
-  protected void updateOurAnnots(List<AlignmentAnnotation> ourAnnot)
-  {
-    List<AlignmentAnnotation> our = ourAnnots;
-    ourAnnots = ourAnnot;
-    AlignmentI alignment = alignViewport.getAlignment();
-    if (our != null)
-    {
-      if (our.size() > 0)
-      {
-        for (AlignmentAnnotation an : our)
-        {
-          if (!ourAnnots.contains(an))
-          {
-            // remove the old annotation
-            alignment.deleteAnnotation(an);
-          }
-        }
-      }
-      our.clear();
-
-      ap.adjustAnnotationHeight();
-    }
-  }
-
-}
index 5e034cd..929c5e2 100644 (file)
@@ -47,7 +47,7 @@ import compbio.metadata.RunnerConfig;
 public class JabaParamStore implements ParamDatastoreI
 {
 
-  Hashtable<String, JabaWsParamSet> editedParams = new Hashtable<String, JabaWsParamSet>();
+  Hashtable<String, JabaWsParamSet> editedParams = new Hashtable<>();
 
   private Jws2Instance service;
 
@@ -101,7 +101,7 @@ public class JabaParamStore implements ParamDatastoreI
     List<WsParamSetI> prefs = new ArrayList<>();
     if (servicePresets == null)
     {
-      servicePresets = new Hashtable<String, JabaPreset>();
+      servicePresets = new Hashtable<>();
       PresetManager prman;
       if ((prman = service.getPresets()) != null)
       {
@@ -148,8 +148,8 @@ public class JabaParamStore implements ParamDatastoreI
   public static List<ArgumentI> getJwsArgsfromJaba(List jabargs,
           boolean sortByOpt)
   {
-    List<ArgumentI> rgs = new ArrayList<ArgumentI>();
-    List<String> rgnames = new ArrayList<String>();
+    List<ArgumentI> rgs = new ArrayList<>();
+    List<String> rgnames = new ArrayList<>();
     for (Object rg : jabargs)
     {
       ArgumentI narg = null;
@@ -309,7 +309,7 @@ public class JabaParamStore implements ParamDatastoreI
     boolean found = false;
     for (String url : urls)
     {
-      if (service.getServiceTypeURI().equals(url)
+      if (service.getNameURI().equals(url)
               || service.getUri().equalsIgnoreCase(url))
       {
         found = true;
@@ -334,7 +334,7 @@ public class JabaParamStore implements ParamDatastoreI
     wsp.setDescription(descr);
     wsp.setApplicableUrls(urls.clone());
 
-    List<String> lines = new ArrayList<String>();
+    List<String> lines = new ArrayList<>();
     StringTokenizer st = new StringTokenizer(parameterfile, "\n");
     while (st.hasMoreTokens())
     {
index 981b288..b6a18dd 100644 (file)
@@ -100,4 +100,9 @@ public class JabaPreset implements WsParamSetI
     throw new Error(MessageManager
             .getString("error.cannot_set_params_for_ws_preset"));
   }
+
+  public Preset getJabaPreset()
+  {
+    return p;
+  }
 }
diff --git a/src/jalview/ws/jws2/JabaWsParamTest.java b/src/jalview/ws/jws2/JabaWsParamTest.java
new file mode 100644 (file)
index 0000000..bef7e29
--- /dev/null
@@ -0,0 +1,264 @@
+package jalview.ws.jws2;
+
+import jalview.gui.WsJobParameters;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+
+import java.awt.BorderLayout;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import compbio.metadata.Option;
+import compbio.metadata.Parameter;
+import compbio.metadata.Preset;
+import compbio.metadata.PresetManager;
+
+public class JabaWsParamTest
+{
+
+  /**
+   * testing method - grab a service and parameter set and show the window
+   * 
+   * @param args
+   */
+  public static void main(String[] args)
+  {
+    jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
+            .getInstance();
+    int p = 0;
+    if (args.length > 0)
+    {
+      Vector<String> services = new Vector<>();
+      services.addElement(args[p++]);
+      Jws2Discoverer.getInstance().setServiceUrls(services);
+    }
+    try
+    {
+      disc.run();
+    } catch (Exception e)
+    {
+      System.err.println("Aborting. Problem discovering services.");
+      e.printStackTrace();
+      return;
+    }
+    Jws2Instance lastserv = null;
+    for (ServiceWithParameters service : disc.getServices())
+    {
+      // this will fail for non-JABAWS services !
+      lastserv = (Jws2Instance) service;
+      if (p >= args.length || service.getName().equalsIgnoreCase(args[p]))
+      {
+        if (lastserv != null)
+        {
+          List<Preset> prl = null;
+          Preset pr = null;
+          if (++p < args.length)
+          {
+            PresetManager prman = lastserv.getPresets();
+            if (prman != null)
+            {
+              pr = prman.getPresetByName(args[p]);
+              if (pr == null)
+              {
+                // just grab the last preset.
+                prl = prman.getPresets();
+              }
+            }
+          }
+          else
+          {
+            PresetManager prman = lastserv.getPresets();
+            if (prman != null)
+            {
+              prl = prman.getPresets();
+            }
+          }
+          Iterator<Preset> en = (prl == null) ? null : prl.iterator();
+          while (en != null && en.hasNext())
+          {
+            if (en != null)
+            {
+              if (!en.hasNext())
+              {
+                en = prl.iterator();
+              }
+              pr = en.next();
+            }
+            {
+              System.out.println("Testing opts dupes for "
+                      + lastserv.getUri() + " : " + lastserv.getActionText()
+                      + ":" + pr.getName());
+              List<Option> rg = lastserv.getRunnerConfig().getOptions();
+              for (Option o : rg)
+              {
+                try
+                {
+                  Option cpy = jalview.ws.jws2.ParameterUtils.copyOption(o);
+                } catch (Exception e)
+                {
+                  System.err.println("Failed to copy " + o.getName());
+                  e.printStackTrace();
+                } catch (Error e)
+                {
+                  System.err.println("Failed to copy " + o.getName());
+                  e.printStackTrace();
+                }
+              }
+            }
+            {
+              System.out.println("Testing param dupes:");
+              List<Parameter> rg = lastserv.getRunnerConfig()
+                      .getParameters();
+              for (Parameter o : rg)
+              {
+                try
+                {
+                  Parameter cpy = jalview.ws.jws2.ParameterUtils
+                          .copyParameter(o);
+                } catch (Exception e)
+                {
+                  System.err.println("Failed to copy " + o.getName());
+                  e.printStackTrace();
+                } catch (Error e)
+                {
+                  System.err.println("Failed to copy " + o.getName());
+                  e.printStackTrace();
+                }
+              }
+            }
+            {
+              System.out.println("Testing param write:");
+              List<String> writeparam = null, readparam = null;
+              try
+              {
+                writeparam = jalview.ws.jws2.ParameterUtils
+                        .writeParameterSet(
+                                pr.getArguments(lastserv.getRunnerConfig()),
+                                " ");
+                System.out.println("Testing param read :");
+                List<Option> pset = jalview.ws.jws2.ParameterUtils
+                        .processParameters(writeparam,
+                                lastserv.getRunnerConfig(), " ");
+                readparam = jalview.ws.jws2.ParameterUtils
+                        .writeParameterSet(pset, " ");
+                Iterator<String> o = pr.getOptions().iterator(),
+                        s = writeparam.iterator(), t = readparam.iterator();
+                boolean failed = false;
+                while (s.hasNext() && t.hasNext())
+                {
+                  String on = o.next(), sn = s.next(), st = t.next();
+                  if (!sn.equals(st))
+                  {
+                    System.out.println(
+                            "Original was " + on + " Phase 1 wrote " + sn
+                                    + "\tPhase 2 wrote " + st);
+                    failed = true;
+                  }
+                }
+                if (failed)
+                {
+                  System.out.println(
+                          "Original parameters:\n" + pr.getOptions());
+                  System.out.println(
+                          "Wrote parameters in first set:\n" + writeparam);
+                  System.out.println(
+                          "Wrote parameters in second set:\n" + readparam);
+
+                }
+              } catch (Exception e)
+              {
+                e.printStackTrace();
+              }
+            }
+            WsJobParameters pgui = new WsJobParameters(null, lastserv,
+                    new JabaPreset(lastserv, pr), null);
+            JFrame jf = new JFrame(MessageManager
+                    .formatMessage("label.ws_parameters_for", new String[]
+                    { lastserv.getActionText() }));
+            JPanel cont = new JPanel(new BorderLayout());
+            pgui.validate();
+            cont.setPreferredSize(pgui.getPreferredSize());
+            cont.add(pgui, BorderLayout.CENTER);
+            jf.setLayout(new BorderLayout());
+            jf.add(cont, BorderLayout.CENTER);
+            jf.validate();
+            final Thread thr = Thread.currentThread();
+            jf.addWindowListener(new WindowListener()
+            {
+
+              @Override
+              public void windowActivated(WindowEvent e)
+              {
+                // TODO Auto-generated method stub
+
+              }
+
+              @Override
+              public void windowClosed(WindowEvent e)
+              {
+              }
+
+              @Override
+              public void windowClosing(WindowEvent e)
+              {
+                thr.interrupt();
+
+              }
+
+              @Override
+              public void windowDeactivated(WindowEvent e)
+              {
+                // TODO Auto-generated method stub
+
+              }
+
+              @Override
+              public void windowDeiconified(WindowEvent e)
+              {
+                // TODO Auto-generated method stub
+
+              }
+
+              @Override
+              public void windowIconified(WindowEvent e)
+              {
+                // TODO Auto-generated method stub
+
+              }
+
+              @Override
+              public void windowOpened(WindowEvent e)
+              {
+                // TODO Auto-generated method stub
+
+              }
+
+            });
+            jf.setVisible(true);
+            boolean inter = false;
+            while (!inter)
+            {
+              try
+              {
+                Thread.sleep(10000);
+              } catch (Exception e)
+              {
+                inter = true;
+              }
+              ;
+            }
+            jf.dispose();
+          }
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/jalview/ws/jws2/JabawsCalcWorker.java b/src/jalview/ws/jws2/JabawsCalcWorker.java
deleted file mode 100644 (file)
index fc36205..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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.jws2;
-
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-
-import java.util.Iterator;
-import java.util.List;
-
-import compbio.data.msa.SequenceAnnotation;
-import compbio.data.sequence.Score;
-import compbio.data.sequence.ScoreManager;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.JobSubmissionException;
-import compbio.metadata.ResultNotAvailableException;
-import compbio.metadata.WrongParameterException;
-
-public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
-{
-
-  @SuppressWarnings("unchecked")
-  protected SequenceAnnotation aaservice;
-
-  protected ScoreManager scoremanager;
-
-  public JabawsCalcWorker(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<Argument> paramset)
-  {
-    super(service, alignFrame, preset, paramset);
-    aaservice = (SequenceAnnotation) service.service;
-  }
-
-  @Override
-  ChunkHolder pullExecStatistics(String rslt, long rpos)
-  {
-    return aaservice.pullExecStatistics(rslt, rpos);
-  }
-
-  @Override
-  boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException
-  {
-    scoremanager = aaservice.getAnnotation(rslt);
-    if (scoremanager != null)
-    {
-      return true;
-    }
-    return false;
-  }
-
-  @Override
-  boolean cancelJob(String rslt) throws Exception
-  {
-    return aaservice.cancelJob(rslt);
-  }
-
-  @Override
-  protected JobStatus getJobStatus(String rslt) throws Exception
-  {
-    return aaservice.getJobStatus(rslt);
-  }
-
-  @Override
-  boolean hasService()
-  {
-    return aaservice != null;
-  }
-
-  @Override
-  protected boolean isInteractiveUpdate()
-  {
-    return this instanceof AAConClient;
-  }
-
-  @Override
-  protected String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException
-  {
-    String rslt;
-    if (preset == null && arguments == null)
-    {
-      rslt = aaservice.analize(seqs);
-    }
-    else
-    {
-      try
-      {
-        rslt = aaservice.customAnalize(seqs, getJabaArguments());
-      } catch (WrongParameterException x)
-      {
-        throw new JobSubmissionException(MessageManager.getString(
-                "exception.jobsubmission_invalid_params_set"), x);
-
-      }
-    }
-    return rslt;
-  }
-
-  protected void createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
-          Score scr)
-  {
-    // simple annotation row
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
-                    null);
-    if (alWidth == gapMap.length) // scr.getScores().size())
-    {
-      constructAnnotationFromScore(annotation, 0, alWidth, scr);
-      ourAnnot.add(annotation);
-    }
-  }
-
-  protected AlignmentAnnotation createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String typeName,
-          String calcId, SequenceI dseq, int base, Score scr)
-  {
-    System.out.println("Creating annotation on dseq:" + dseq.getStart()
-            + " base is " + base + " and length=" + dseq.getLength()
-            + " == " + scr.getScores().size());
-    // AlignmentAnnotation annotation = new AlignmentAnnotation(
-    // scr.getMethod(), typeName, new Annotation[]
-    // {}, 0, -1, AlignmentAnnotation.LINE_GRAPH);
-    // annotation.setCalcId(calcId);
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
-    constructAnnotationFromScore(annotation, 0, dseq.getLength(), scr);
-    annotation.createSequenceMapping(dseq, base, false);
-    annotation.adjustForAlignment();
-    dseq.addAlignmentAnnotation(annotation);
-    ourAnnot.add(annotation);
-    return annotation;
-  }
-
-  protected void replaceAnnotationOnAlignmentWith(
-          AlignmentAnnotation newAnnot, String typeName, String calcId,
-          SequenceI aSeq)
-  {
-    SequenceI dsseq = aSeq.getDatasetSequence();
-    while (dsseq.getDatasetSequence() != null)
-    {
-      dsseq = dsseq.getDatasetSequence();
-    }
-    // look for same annotation on dataset and lift this one over
-    List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
-            typeName);
-    if (dsan != null && dsan.size() > 0)
-    {
-      for (AlignmentAnnotation dssan : dsan)
-      {
-        dsseq.removeAlignmentAnnotation(dssan);
-      }
-    }
-    AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
-    dsseq.addAlignmentAnnotation(dssan);
-    dssan.adjustForAlignment();
-  }
-
-  private void constructAnnotationFromScore(AlignmentAnnotation annotation,
-          int base, int alWidth, Score scr)
-  {
-    Annotation[] elm = new Annotation[alWidth];
-    Iterator<Float> vals = scr.getScores().iterator();
-    float m = 0f, x = 0f;
-    for (int i = 0; vals.hasNext(); i++)
-    {
-      float val = vals.next().floatValue();
-      if (i == 0)
-      {
-        m = val;
-        x = val;
-      }
-      else
-      {
-        if (m > val)
-        {
-          m = val;
-        }
-        ;
-        if (x < val)
-        {
-          x = val;
-        }
-      }
-      // if we're at a gapped column then skip to next ungapped position
-      if (gapMap != null && gapMap.length > 0)
-      {
-        while (!gapMap[i])
-        {
-          elm[i++] = new Annotation("", "", ' ', Float.NaN);
-        }
-      }
-      elm[i] = new Annotation("", "" + val, ' ', val);
-    }
-
-    annotation.annotations = elm;
-    annotation.belowAlignment = true;
-    if (x < 0)
-    {
-      x = 0;
-    }
-    x += (x - m) * 0.1;
-    annotation.graphMax = x;
-    annotation.graphMin = m;
-    annotation.validateRangeAndDisplay();
-  }
-
-}
diff --git a/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java b/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java
deleted file mode 100644 (file)
index ee36d4a..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * 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.jws2;
-
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-
-import java.util.Iterator;
-import java.util.List;
-
-import compbio.data.msa.MsaWS;
-import compbio.data.sequence.Alignment;
-import compbio.data.sequence.Score;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.JobSubmissionException;
-import compbio.metadata.ResultNotAvailableException;
-import compbio.metadata.WrongParameterException;
-
-public abstract class JabawsMsaInterfaceAlignCalcWorker
-        extends AbstractJabaCalcWorker
-{
-
-  @SuppressWarnings("unchecked")
-  protected MsaWS msaservice;
-
-  protected Alignment msascoreset;
-
-  public JabawsMsaInterfaceAlignCalcWorker(AlignViewportI alignViewport,
-          AlignmentViewPanel alignPanel)
-  {
-    super(alignViewport, alignPanel);
-  }
-
-  public JabawsMsaInterfaceAlignCalcWorker(Jws2Instance service,
-          AlignFrame alignFrame, WsParamSetI preset,
-          List<Argument> paramset)
-  {
-    this(alignFrame.getCurrentView(), alignFrame.alignPanel);
-    this.guiProgress = alignFrame;
-    this.preset = preset;
-    this.arguments = paramset;
-    this.service = service;
-    msaservice = (MsaWS) service.service;
-
-  }
-
-  @Override
-  ChunkHolder pullExecStatistics(String rslt, long rpos)
-  {
-    return msaservice.pullExecStatistics(rslt, rpos);
-  }
-
-  @Override
-  boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException
-  {
-    msascoreset = msaservice.getResult(rslt);
-    if (msascoreset != null)
-    {
-      return true;
-    }
-    return false;
-  }
-
-  @Override
-  boolean cancelJob(String rslt) throws Exception
-  {
-    return msaservice.cancelJob(rslt);
-  }
-
-  @Override
-  protected JobStatus getJobStatus(String rslt) throws Exception
-  {
-    return msaservice.getJobStatus(rslt);
-  }
-
-  @Override
-  boolean hasService()
-  {
-    return msaservice != null;
-  }
-
-  @Override
-  protected boolean isInteractiveUpdate()
-  {
-    return false; // this instanceof AAConClient;
-  }
-
-  @Override
-  protected String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException
-  {
-    String rslt;
-    if (preset == null && arguments == null)
-    {
-      rslt = msaservice.align(seqs);
-    }
-    else
-    {
-      try
-      {
-        rslt = msaservice.customAlign(seqs, getJabaArguments());
-      } catch (WrongParameterException x)
-      {
-        throw new JobSubmissionException(MessageManager.getString(
-                "exception.jobsubmission_invalid_params_set"), x);
-      }
-    }
-    return rslt;
-  }
-
-  protected void createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
-          Score scr)
-  {
-    // simple annotation row
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
-                    null);
-    if (alWidth == gapMap.length) // scr.getScores().size())
-    {
-      constructAnnotationFromScore(annotation, 0, alWidth, scr);
-      ourAnnot.add(annotation);
-    }
-  }
-
-  protected AlignmentAnnotation createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String typeName,
-          String calcId, SequenceI dseq, int base, Score scr)
-  {
-    System.out.println("Creating annotation on dseq:" + dseq.getStart()
-            + " base is " + base + " and length=" + dseq.getLength()
-            + " == " + scr.getScores().size());
-    // AlignmentAnnotation annotation = new AlignmentAnnotation(
-    // scr.getMethod(), typeName, new Annotation[]
-    // {}, 0, -1, AlignmentAnnotation.LINE_GRAPH);
-    // annotation.setCalcId(calcId);
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
-    constructAnnotationFromScore(annotation, 0, dseq.getLength(), scr);
-    annotation.createSequenceMapping(dseq, base, false);
-    annotation.adjustForAlignment();
-    dseq.addAlignmentAnnotation(annotation);
-    ourAnnot.add(annotation);
-    return annotation;
-  }
-
-  private void constructAnnotationFromScore(AlignmentAnnotation annotation,
-          int base, int alWidth, Score scr)
-  {
-    Annotation[] elm = new Annotation[alWidth];
-    Iterator<Float> vals = scr.getScores().iterator();
-    float m = 0f, x = 0f;
-    for (int i = 0; vals.hasNext(); i++)
-    {
-      float val = vals.next().floatValue();
-      if (i == 0)
-      {
-        m = val;
-        x = val;
-      }
-      else
-      {
-        if (m > val)
-        {
-          m = val;
-        }
-        ;
-        if (x < val)
-        {
-          x = val;
-        }
-      }
-      // if we're at a gapped column then skip to next ungapped position
-      if (gapMap != null && gapMap.length > 0)
-      {
-        while (!gapMap[i])
-        {
-          elm[i++] = new Annotation("", "", ' ', Float.NaN);
-        }
-      }
-      elm[i] = new Annotation("", "" + val, ' ', val);
-    }
-
-    annotation.annotations = elm;
-    annotation.belowAlignment = true;
-    if (x < 0)
-    {
-      x = 0;
-    }
-    x += (x - m) * 0.1;
-    annotation.graphMax = x;
-    annotation.graphMin = m;
-    annotation.validateRangeAndDisplay();
-  }
-
-}
index 2f996bd..ab047d4 100644 (file)
  */
 package jalview.ws.jws2;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import jalview.gui.AlignFrame;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
 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.Console;
-import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.WebserviceInfo;
-import jalview.gui.WsJobParameters;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.dm.JabaWsParamSet;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-import jalview.ws.uimodel.AlignAnalysisUIText;
 
 /**
  * provides metadata for a jabaws2 service instance - resolves names, etc.
@@ -53,87 +36,19 @@ import jalview.ws.uimodel.AlignAnalysisUIText;
  */
 public abstract class Jws2Client extends jalview.ws.WSClient
 {
-  protected AlignFrame alignFrame;
-
-  protected WsParamSetI preset;
-
-  protected List<Argument> paramset;
+  /**
+   * instantiate a new service client. preset and arguments are assumed to be
+   * valid for the service
+   * 
+   * @param _alignFrame
+   * @param preset
+   * @param arguments
+   */
 
   public Jws2Client(AlignFrame _alignFrame, WsParamSetI preset,
-          List<Argument> arguments)
-  {
-    alignFrame = _alignFrame;
-    this.preset = preset;
-    if (preset != null)
-    {
-      if (!((preset instanceof JabaPreset)
-              || preset instanceof JabaWsParamSet))
-      {
-        /*
-         * { this.preset = ((JabaPreset) preset).p; } else if (preset instanceof
-         * JabaWsParamSet) { List<Argument> newargs = new ArrayList<Argument>();
-         * JabaWsParamSet pset = ((JabaWsParamSet) preset); for (Option opt :
-         * pset.getjabaArguments()) { newargs.add(opt); } if (arguments != null
-         * && arguments.size() > 0) { // merge arguments with preset's own
-         * arguments. for (Argument opt : arguments) { newargs.add(opt); } }
-         * paramset = newargs; } else {
-         */
-        throw new Error(MessageManager.getString(
-                "error.implementation_error_can_only_instantiate_jaba_param_sets"));
-      }
-    }
-    else
-    {
-      // just provided with a bunch of arguments
-      this.paramset = arguments;
-    }
-  }
-
-  boolean processParams(Jws2Instance sh, boolean editParams)
+          List<ArgumentI> arguments)
   {
-    return processParams(sh, editParams, false);
-  }
-
-  protected boolean processParams(Jws2Instance sh, boolean editParams,
-          boolean adjustingExisting)
-  {
-
-    if (editParams)
-    {
-      if (sh.paramStore == null)
-      {
-        sh.paramStore = new JabaParamStore(sh,
-                Desktop.getUserParameterStore());
-      }
-      WsJobParameters jobParams = (preset == null && paramset != null
-              && paramset.size() > 0)
-                      ? new WsJobParameters(null, sh, null, paramset)
-                      : new WsJobParameters(sh, preset);
-      if (adjustingExisting)
-      {
-        jobParams.setName(MessageManager
-                .getString("label.adjusting_parameters_for_calculation"));
-      }
-      if (!jobParams.showRunDialog())
-      {
-        return false;
-      }
-      WsParamSetI prset = jobParams.getPreset();
-      if (prset == null)
-      {
-        paramset =
-                /* JAL-3739 always take values from input form */
-                /* jobParams.isServiceDefaults() ? null : */
-                JabaParamStore.getJabafromJwsArgs(jobParams.getJobParams());
-        this.preset = null;
-      }
-      else
-      {
-        this.preset = prset; // ((JabaPreset) prset).p;
-        paramset = null; // no user supplied parameters.
-      }
-    }
-    return true;
+    super(_alignFrame, preset, arguments);
 
   }
 
@@ -142,23 +57,6 @@ public abstract class Jws2Client extends jalview.ws.WSClient
     // anonymous constructor - used for headless method calls only
   }
 
-  protected WebserviceInfo setWebService(Jws2Instance serv, boolean b)
-  {
-    // serviceHandle = serv;
-    String serviceInstance = serv.action; // serv.service.getClass().getName();
-    WebServiceName = serv.serviceType;
-    WebServiceJobTitle = serv.getActionText();
-    WsURL = serv.hosturl;
-    if (!b)
-    {
-      return new WebserviceInfo(WebServiceJobTitle,
-              WebServiceJobTitle + " using service hosted at "
-                      + serv.hosturl + "\n"
-                      + (serv.description != null ? serv.description : ""),
-              false);
-    }
-    return null;
-  }
 
   /*
    * Jws2Instance serviceHandle; (non-Javadoc)
@@ -178,254 +76,9 @@ public abstract class Jws2Client extends jalview.ws.WSClient
    * @param service
    * @param alignFrame
    */
-  abstract void attachWSMenuEntry(JMenu wsmenu, final Jws2Instance service,
+  abstract void attachWSMenuEntry(JMenu wsmenu,
+          final ServiceWithParameters service,
           final AlignFrame alignFrame);
 
-  protected boolean registerAAConWSInstance(final JMenu wsmenu,
-          final Jws2Instance service, final AlignFrame alignFrame)
-  {
-    final AlignAnalysisUIText aaui = service.getAlignAnalysisUI(); // null ; //
-                                                                   // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
-    if (aaui == null)
-    {
-      // not an instantaneous calculation GUI type service
-      return false;
-    }
-    // create the instaneous calculation GUI bits and update state if existing
-    // GUI elements already present
-
-    JCheckBoxMenuItem _aaConEnabled = null;
-    for (int i = 0; i < wsmenu.getItemCount(); i++)
-    {
-      JMenuItem item = wsmenu.getItem(i);
-      if (item instanceof JCheckBoxMenuItem
-              && item.getText().equals(aaui.getAAconToggle()))
-      {
-        _aaConEnabled = (JCheckBoxMenuItem) item;
-      }
-    }
-    // is there an aaCon worker already present - if so, set it to use the
-    // given service handle
-    {
-      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-              .getCalcManager()
-              .getRegisteredWorkersOfClass(aaui.getClient());
-      if (aaconClient != null && aaconClient.size() > 0)
-      {
-        AbstractJabaCalcWorker worker = (AbstractJabaCalcWorker) aaconClient
-                .get(0);
-        if (!worker.service.hosturl.equals(service.hosturl))
-        {
-          // javax.swing.SwingUtilities.invokeLater(new Runnable()
-          {
-            // @Override
-            // public void run()
-            {
-              removeCurrentAAConWorkerFor(aaui, alignFrame);
-              buildCurrentAAConWorkerFor(aaui, alignFrame, service);
-            }
-          } // );
-        }
-      }
-    }
-
-    // is there a service already registered ? there shouldn't be if we are
-    // being called correctly
-    if (_aaConEnabled == null)
-    {
-      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
-              aaui.getAAconToggle());
-
-      aaConEnabled.setToolTipText(
-              JvSwingUtils.wrapTooltip(true, aaui.getAAconToggleTooltip()));
-      aaConEnabled.addActionListener(new ActionListener()
-      {
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            removeCurrentAAConWorkerFor(aaui, alignFrame);
-          }
-          else
-          {
-            buildCurrentAAConWorkerFor(aaui, alignFrame);
-
-          }
-        }
-
-      });
-      wsmenu.add(aaConEnabled);
-      final JMenuItem modifyParams = new JMenuItem(
-              aaui.getAAeditSettings());
-      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
-              aaui.getAAeditSettingsTooltip()));
-      modifyParams.addActionListener(new ActionListener()
-      {
-
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          showAAConAnnotationSettingsFor(aaui, alignFrame);
-        }
-      });
-      wsmenu.add(modifyParams);
-      wsmenu.addMenuListener(new MenuListener()
-      {
-
-        @Override
-        public void menuSelected(MenuEvent arg0)
-        {
-          // TODO: refactor to the implementing class.
-          if (alignFrame.getViewport().getAlignment().isNucleotide()
-                  ? aaui.isNa()
-                  : aaui.isPr())
-          {
-            aaConEnabled.setEnabled(true);
-            modifyParams.setEnabled(true);
-          }
-          else
-          {
-            aaConEnabled.setEnabled(false);
-            modifyParams.setEnabled(false);
-          }
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            aaConEnabled.setSelected(true);
-          }
-          else
-          {
-            aaConEnabled.setSelected(false);
-          }
-        }
-
-        @Override
-        public void menuDeselected(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void menuCanceled(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-      });
-
-    }
-    return true;
-  }
-
-  private static void showAAConAnnotationSettingsFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    // could actually do a class search for this too
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui);
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, true);
-
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
-          Jws2Instance service)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui, service);
-    }
-    else
-    {
-      if (service != null
-              && !fave.getService().hosturl.equals(service.hosturl))
-      {
-        Console.debug("Changing AACon service to " + service.hosturl
-                + " from " + fave.getService().hosturl);
-        fave.setService(service);
-      }
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, false);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui)
-  {
-    return createDefaultAAConSettings(aaui, null);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui, Jws2Instance service)
-  {
-    if (service != null)
-    {
-      if (!service.serviceType.toString()
-              .equals(compbio.ws.client.Services.AAConWS.toString()))
-      {
-        Console.warn(
-                "Ignoring invalid preferred service for AACon calculations (service type was "
-                        + service.serviceType + ")");
-        service = null;
-      }
-      else
-      {
-        // check service is actually in the list of currently avaialable
-        // services
-        if (!Jws2Discoverer.getDiscoverer().getServices().contains(service))
-        {
-          // it isn't ..
-          service = null;
-        }
-      }
-    }
-    if (service == null)
-    {
-      // get the default service for AACon
-      service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null,
-              aaui.getServiceType());
-    }
-    if (service == null)
-    {
-      // TODO raise dialog box explaining error, and/or open the JABA
-      // preferences menu.
-      throw new Error(
-              MessageManager.getString("error.no_aacon_service_found"));
-    }
-    return new AAConSettings(true, service, null, null);
-  }
-
-  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
-          AlignFrame alignFrame)
-  {
-    alignFrame.getViewport().getCalcManager()
-            .removeRegisteredWorkersOfClass(aaui.getClient());
-  }
 
 }
diff --git a/src/jalview/ws/jws2/Jws2ClientFactory.java b/src/jalview/ws/jws2/Jws2ClientFactory.java
new file mode 100644 (file)
index 0000000..4d78bd7
--- /dev/null
@@ -0,0 +1,293 @@
+package jalview.ws.jws2;
+
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.AutoCalcSetting;
+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;
+
+public class Jws2ClientFactory
+{
+  static boolean registerAAConWSInstance(final JMenu wsmenu,
+          final ServiceWithParameters service, final AlignFrame alignFrame)
+  {
+    final AlignAnalysisUIText aaui = service.getAlignAnalysisUI(); // null
+                                                                        // ; //
+    // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
+    if (aaui == null)
+    {
+      // not an instantaneous calculation GUI type service
+      return false;
+    }
+    // create the instaneous calculation GUI bits and update state if existing
+    // GUI elements already present
+
+    JCheckBoxMenuItem _aaConEnabled = null;
+    for (int i = 0; i < wsmenu.getItemCount(); i++)
+    {
+      JMenuItem item = wsmenu.getItem(i);
+      if (item instanceof JCheckBoxMenuItem
+              && item.getText().equals(aaui.getAAconToggle()))
+      {
+        _aaConEnabled = (JCheckBoxMenuItem) item;
+      }
+    }
+    // is there an aaCon worker already present - if so, set it to use the
+    // given service handle
+    {
+      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+              .getCalcManager()
+              .getWorkersOfClass(aaui.getClient());
+      if (aaconClient != null && aaconClient.size() > 0)
+      {
+        SeqAnnotationServiceCalcWorker worker = (SeqAnnotationServiceCalcWorker) aaconClient
+                .get(0);
+        if (!worker.service.getHostURL().equals(service.getHostURL()))
+        {
+          // javax.swing.SwingUtilities.invokeLater(new Runnable()
+          {
+            // @Override
+            // public void run()
+            {
+              removeCurrentAAConWorkerFor(aaui, alignFrame);
+              buildCurrentAAConWorkerFor(aaui, alignFrame, service);
+            }
+          } // );
+        }
+      }
+    }
+
+    // is there a service already registered ? there shouldn't be if we are
+    // being called correctly
+    if (_aaConEnabled == null)
+    {
+      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
+              aaui.getAAconToggle());
+
+      aaConEnabled.setToolTipText(
+              JvSwingUtils.wrapTooltip(true, aaui.getAAconToggleTooltip()));
+      aaConEnabled.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getWorkersOfClass(SeqAnnotationServiceCalcWorker.class);
+          if (aaconClient != null)
+          {
+            for (AlignCalcWorkerI worker : aaconClient)
+            {
+              if (((SeqAnnotationServiceCalcWorker) worker).getService()
+                      .getClass().equals(aaui.getClient()))
+              {
+                removeCurrentAAConWorkerFor(aaui, alignFrame);
+                return;
+              }
+            }
+          }
+          buildCurrentAAConWorkerFor(aaui, alignFrame);
+        }
+
+      });
+      wsmenu.add(aaConEnabled);
+      final JMenuItem modifyParams = new JMenuItem(
+              aaui.getAAeditSettings());
+      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              aaui.getAAeditSettingsTooltip()));
+      modifyParams.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+          showAAConAnnotationSettingsFor(aaui, alignFrame);
+        }
+      });
+      wsmenu.add(modifyParams);
+      wsmenu.addMenuListener(new MenuListener()
+      {
+
+        @Override
+        public void menuSelected(MenuEvent arg0)
+        {
+          // TODO: refactor to the implementing class.
+          if (alignFrame.getViewport().getAlignment().isNucleotide()
+                  ? aaui.isNa()
+                  : aaui.isPr())
+          {
+            aaConEnabled.setEnabled(true);
+            modifyParams.setEnabled(true);
+          }
+          else
+          {
+            aaConEnabled.setEnabled(false);
+            modifyParams.setEnabled(false);
+            return;
+          }
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getWorkersOfClass(SeqAnnotationServiceCalcWorker.class);
+
+          boolean serviceEnabled = false;
+          if (aaconClient != null)
+          {
+            // NB code duplicatino again!
+            for (AlignCalcWorkerI _worker : aaconClient)
+            {
+              SeqAnnotationServiceCalcWorker worker = (SeqAnnotationServiceCalcWorker) _worker;
+              // this could be cleaner ?
+              if (worker.hasService()
+                      && aaui.getClient()
+                              .equals(worker.getService().getClass()))
+              {
+                serviceEnabled = true;
+              }
+            }
+          }
+          aaConEnabled.setSelected(serviceEnabled);
+        }
+
+        @Override
+        public void menuDeselected(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void menuCanceled(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+      });
+
+    }
+    return true;
+  }
+
+  private static void showAAConAnnotationSettingsFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    // could actually do a class search for this too
+    AutoCalcSetting fave = alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui);
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, true);
+
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
+          ServiceWithParameters service)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    AutoCalcSetting fave = alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui, service);
+    }
+    else
+    {
+      if (service != null && !fave.getService().getHostURL()
+              .equals(service.getHostURL()))
+      {
+        Console.debug("Changing AACon service to " + service.getHostURL()
+                + " from " + fave.getService().getHostURL());
+        fave.setService(service);
+      }
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, false);
+  }
+
+  private static AutoCalcSetting createDefaultAAConSettings(
+          AlignAnalysisUIText aaui)
+  {
+    return createDefaultAAConSettings(aaui, null);
+  }
+
+  private static AutoCalcSetting createDefaultAAConSettings(
+          AlignAnalysisUIText aaui, ServiceWithParameters service)
+  {
+    if (service != null)
+    {
+      // if (!service.getServiceType()
+      // .equals(compbio.ws.client.Services.AAConWS.toString()))
+      // {
+      // Console.warn(
+      // "Ignoring invalid preferred service for AACon calculations (service
+      // type was "
+      // + service.getServiceType() + ")");
+      // service = null;
+      // }
+      // else
+      {
+        // check service is actually in the list of currently avaialable
+        // services
+        if (!PreferredServiceRegistry.getRegistry().contains(service))
+        {
+          // it isn't ..
+          service = null;
+        }
+      }
+    }
+    if (service == null)
+    {
+      // get the default service for AACon
+      service = PreferredServiceRegistry.getRegistry().getPreferredServiceFor(null,
+              aaui.getServiceType());
+    }
+    if (service == null)
+    {
+      // TODO raise dialog box explaining error, and/or open the JABA
+      // preferences menu.
+      throw new Error(
+              MessageManager.getString("error.no_aacon_service_found"));
+    }
+    return service instanceof Jws2Instance
+            ? new AAConSettings(true, service, null, null)
+            : new AutoCalcSetting(service, null, null, true);
+  }
+
+  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
+          AlignFrame alignFrame)
+  {
+    alignFrame.getViewport().getCalcManager()
+            .removeWorkersOfClass(aaui.getClient());
+  }
+}
index b6b4b2e..56a4b77 100644 (file)
@@ -22,35 +22,35 @@ package jalview.ws.jws2;
 
 import jalview.bin.Cache;
 import jalview.bin.Console;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.JvSwingUtils;
 import jalview.util.MessageManager;
-import jalview.ws.WSMenuEntryProviderI;
+import jalview.ws.ServiceChangeListener;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ParamDatastoreI;
 
-import java.awt.Color;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.Vector;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 
 import javax.swing.JMenu;
-import javax.swing.JMenuItem;
 
 import compbio.ws.client.Services;
 
@@ -61,8 +61,18 @@ import compbio.ws.client.Services;
  * @author JimP
  * 
  */
-public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
+public class Jws2Discoverer implements WSDiscovererI, Runnable, ApplicationSingletonI
 {
+  /**
+   * Returns the singleton instance of this class.
+   * 
+   * @return
+   */
+  public static Jws2Discoverer getInstance()
+  {
+    return (Jws2Discoverer) ApplicationSingletonProvider
+            .getInstance(Jws2Discoverer.class);
+  }
   public static final String COMPBIO_JABAWS = "http://www.compbio.dundee.ac.uk/jabaws";
 
   /*
@@ -71,20 +81,15 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
   private final static String JWS2HOSTURLS = "JWS2HOSTURLS";
 
   /*
-   * Singleton instance
-   */
-  private static Jws2Discoverer discoverer;
-
-  /*
    * Override for testing only
    */
-  private static List<String> testUrls = null;
+  private List<String> testUrls = null;
 
   // preferred url has precedence over others
   private String preferredUrl;
 
-  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
-          this);
+  
+  private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
 
   private Vector<String> invalidServiceUrls = null;
 
@@ -96,7 +101,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
 
   private volatile boolean aborted = false;
 
-  private Thread oldthread = null;
+  
+  private volatile Thread oldthread = null;
 
   /**
    * holds list of services.
@@ -110,28 +116,24 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
   {
   }
 
-  /**
-   * change listeners are notified of "services" property changes
-   * 
-   * @param listener
-   *          to be added that consumes new services Hashtable object.
-   */
-  public void addPropertyChangeListener(
-          java.beans.PropertyChangeListener listener)
+  @Override
+  public void addServiceChangeListener(ServiceChangeListener listener)
   {
-    changeSupport.addPropertyChangeListener(listener);
+    serviceListeners.add(listener);
   }
 
-  /**
-   * 
-   * 
-   * @param listener
-   *          to be removed
-   */
-  public void removePropertyChangeListener(
-          java.beans.PropertyChangeListener listener)
+  @Override
+  public void removeServiceChangeListener(ServiceChangeListener listener)
+  {
+    serviceListeners.remove(listener);
+  }
+
+  private void notifyServiceListeners(List<? extends ServiceWithParameters> services) 
   {
-    changeSupport.removePropertyChangeListener(listener);
+    if (services == null) services = this.services;
+    for (var listener : serviceListeners) {
+      listener.servicesChanged(this, services);
+    }
   }
 
   /**
@@ -186,13 +188,11 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
       ignoredServices.add(ignored);
     }
 
-    changeSupport.firePropertyChange("services", services,
-            new Vector<Jws2Instance>());
+    notifyServiceListeners(Collections.emptyList());
     oldthread = Thread.currentThread();
     try
     {
-      Class foo = getClass().getClassLoader()
-              .loadClass("compbio.ws.client.Jws2Client");
+      getClass().getClassLoader().loadClass("compbio.ws.client.Jws2Client");
     } catch (ClassNotFoundException e)
     {
       System.err.println(
@@ -281,14 +281,14 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
         for (Jws2Instance svc : services)
         {
           svcs[ipos] = svc;
-          spos[ipos++] = 1000 * svcUrls.indexOf(svc.getHost()) + 1
-                  + svctypes.indexOf(svc.serviceType);
+          spos[ipos++] = 1000 * svcUrls.indexOf(svc.getHostURL()) + 1
+                  + svctypes.indexOf(svc.getName());
         }
         jalview.util.QuickSort.sort(spos, svcs);
         services = new Vector<>();
         for (Jws2Instance svc : svcs)
         {
-          if (!ignoredServices.contains(svc.serviceType))
+          if (!ignoredServices.contains(svc.getName()))
           {
             services.add(svc);
           }
@@ -297,8 +297,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     }
     oldthread = null;
     running = false;
-    changeSupport.firePropertyChange("services", new Vector<Jws2Instance>(),
-            services);
+    notifyServiceListeners(services);
   }
 
   /**
@@ -335,292 +334,43 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
   }
 
   /**
-   * attach all available web services to the appropriate submenu in the given
-   * JMenu
-   */
-  @Override
-  public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
-  {
-    // dynamically regenerate service list.
-    populateWSMenuEntry(wsmenu, alignFrame, null);
-  }
-
-  private boolean isRecalculable(String action)
-  {
-    return (action != null && action.equalsIgnoreCase("conservation"));
-  }
-
-  private void populateWSMenuEntry(JMenu jws2al,
-          final AlignFrame alignFrame, String typeFilter)
-  {
-    if (running || services == null || services.size() == 0)
-    {
-      return;
-    }
-
-    /**
-     * eventually, JWS2 services will appear under the same align/etc submenus.
-     * for moment we keep them separate.
-     */
-    JMenu atpoint;
-    List<Jws2Instance> enumerableServices = new ArrayList<>();
-    // jws2al.removeAll();
-    Map<String, Jws2Instance> preferredHosts = new HashMap<>();
-    Map<String, List<Jws2Instance>> alternates = new HashMap<>();
-    for (Jws2Instance service : services.toArray(new Jws2Instance[0]))
-    {
-      if (!isRecalculable(service.action))
-      {
-        // add 'one shot' services to be displayed using the classic menu
-        // structure
-        enumerableServices.add(service);
-      }
-      else
-      {
-        if (!preferredHosts.containsKey(service.serviceType))
-        {
-          Jws2Instance preferredInstance = getPreferredServiceFor(
-                  alignFrame, service.serviceType);
-          if (preferredInstance != null)
-          {
-            preferredHosts.put(service.serviceType, preferredInstance);
-          }
-          else
-          {
-            preferredHosts.put(service.serviceType, service);
-          }
-        }
-        List<Jws2Instance> ph = alternates.get(service.serviceType);
-        if (preferredHosts.get(service.serviceType) != service)
-        {
-          if (ph == null)
-          {
-            ph = new ArrayList<>();
-          }
-          ph.add(service);
-          alternates.put(service.serviceType, ph);
-        }
-      }
-
-    }
-
-    // create GUI element for classic services
-    addEnumeratedServices(jws2al, alignFrame, enumerableServices);
-    // and the instantaneous services
-    for (final Jws2Instance service : preferredHosts.values())
-    {
-      atpoint = JvSwingUtils.findOrCreateMenu(jws2al, service.action);
-      JMenuItem hitm;
-      if (atpoint.getItemCount() > 1)
-      {
-        // previous service of this type already present
-        atpoint.addSeparator();
-      }
-      atpoint.add(hitm = new JMenuItem(service.getHost()));
-      hitm.setForeground(Color.blue);
-      hitm.addActionListener(new ActionListener()
-      {
-
-        @Override
-        public void actionPerformed(ActionEvent e)
-        {
-          Desktop.showUrl(service.getHost());
-        }
-      });
-      hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
-              MessageManager.getString("label.open_jabaws_web_page")));
-
-      service.attachWSMenuEntry(atpoint, alignFrame);
-      if (alternates.containsKey(service.serviceType))
-      {
-        atpoint.add(hitm = new JMenu(
-                MessageManager.getString("label.switch_server")));
-        hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
-                MessageManager.getString("label.choose_jabaws_server")));
-        for (final Jws2Instance sv : alternates.get(service.serviceType))
-        {
-          JMenuItem itm;
-          hitm.add(itm = new JMenuItem(sv.getHost()));
-          itm.setForeground(Color.blue);
-          itm.addActionListener(new ActionListener()
-          {
-
-            @Override
-            public void actionPerformed(ActionEvent arg0)
-            {
-              new Thread(new Runnable()
-              {
-                @Override
-                public void run()
-                {
-                  setPreferredServiceFor(alignFrame, sv.serviceType,
-                          sv.action, sv);
-                  changeSupport.firePropertyChange("services",
-                          new Vector<Jws2Instance>(), services);
-                }
-              }).start();
-
-            }
-          });
-        }
-      }
-    }
-  }
-
-  /**
-   * add services using the Java 2.5/2.6/2.7 system which optionally creates
-   * submenus to index by host and service program type
-   */
-  private void addEnumeratedServices(final JMenu jws2al,
-          final AlignFrame alignFrame,
-          List<Jws2Instance> enumerableServices)
-  {
-    boolean byhost = Cache.getDefault("WSMENU_BYHOST", false),
-            bytype = Cache.getDefault("WSMENU_BYTYPE", false);
-    /**
-     * eventually, JWS2 services will appear under the same align/etc submenus.
-     * for moment we keep them separate.
-     */
-    JMenu atpoint;
-
-    List<String> hostLabels = new ArrayList<>();
-    Hashtable<String, String> lasthostFor = new Hashtable<>();
-    Hashtable<String, ArrayList<Jws2Instance>> hosts = new Hashtable<>();
-    ArrayList<String> hostlist = new ArrayList<>();
-    for (Jws2Instance service : enumerableServices)
-    {
-      ArrayList<Jws2Instance> hostservices = hosts.get(service.getHost());
-      if (hostservices == null)
-      {
-        hosts.put(service.getHost(), hostservices = new ArrayList<>());
-        hostlist.add(service.getHost());
-      }
-      hostservices.add(service);
-    }
-    // now add hosts in order of the given array
-    for (String host : hostlist)
-    {
-      Jws2Instance orderedsvcs[] = hosts.get(host)
-              .toArray(new Jws2Instance[1]);
-      String sortbytype[] = new String[orderedsvcs.length];
-      for (int i = 0; i < sortbytype.length; i++)
-      {
-        sortbytype[i] = orderedsvcs[i].serviceType;
-      }
-      jalview.util.QuickSort.sort(sortbytype, orderedsvcs);
-      for (final Jws2Instance service : orderedsvcs)
-      {
-        atpoint = JvSwingUtils.findOrCreateMenu(jws2al, service.action);
-        String type = service.serviceType;
-        if (byhost)
-        {
-          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, host);
-          if (atpoint.getToolTipText() == null)
-          {
-            atpoint.setToolTipText(MessageManager
-                    .formatMessage("label.services_at", new String[]
-                    { host }));
-          }
-        }
-        if (bytype)
-        {
-          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, type);
-          if (atpoint.getToolTipText() == null)
-          {
-            atpoint.setToolTipText(service.getActionText());
-          }
-        }
-        if (!byhost && !hostLabels.contains(
-                host + service.serviceType + service.getActionText()))
-        // !hostLabels.contains(host + (bytype ?
-        // service.serviceType+service.getActionText() : "")))
-        {
-          // add a marker indicating where this service is hosted
-          // relies on services from the same host being listed in a
-          // contiguous
-          // group
-          JMenuItem hitm;
-          if (hostLabels.contains(host))
-          {
-            atpoint.addSeparator();
-          }
-          else
-          {
-            hostLabels.add(host);
-          }
-          if (lasthostFor.get(service.action) == null
-                  || !lasthostFor.get(service.action).equals(host))
-          {
-            atpoint.add(hitm = new JMenuItem(host));
-            hitm.setForeground(Color.blue);
-            hitm.addActionListener(new ActionListener()
-            {
-
-              @Override
-              public void actionPerformed(ActionEvent e)
-              {
-                Desktop.showUrl(service.getHost());
-              }
-            });
-            hitm.setToolTipText(
-                    JvSwingUtils.wrapTooltip(true, MessageManager
-                            .getString("label.open_jabaws_web_page")));
-            lasthostFor.put(service.action, host);
-          }
-          hostLabels.add(
-                  host + service.serviceType + service.getActionText());
-        }
-
-        service.attachWSMenuEntry(atpoint, alignFrame);
-      }
-    }
-  }
-
-  /**
    * 
    * @param args
    * @j2sIgnore
    */
   public static void main(String[] args)
   {
+    Jws2Discoverer instance = getInstance();
     if (args.length > 0)
     {
-      testUrls = new ArrayList<>();
+      instance.testUrls = new ArrayList<>();
       for (String url : args)
       {
-        testUrls.add(url);
+        instance.testUrls.add(url);
       }
     }
-    Thread runner = getDiscoverer()
-            .startDiscoverer(new PropertyChangeListener()
-            {
-
-              @Override
-              public void propertyChange(PropertyChangeEvent evt)
-              {
-                if (getDiscoverer().services != null)
-                {
-                  System.out.println("Changesupport: There are now "
-                          + getDiscoverer().services.size() + " services");
-                  int i = 1;
-                  for (Jws2Instance instance : getDiscoverer().services)
-                  {
-                    System.out.println("Service " + i++ + " "
-                            + instance.getClass() + "@" + instance.getHost()
-                            + ": " + instance.getActionText());
-                  }
-
-                }
-              }
-            });
-    while (runner.isAlive())
-    {
-      try
-      {
-        Thread.sleep(50);
-      } catch (InterruptedException e)
+    var discoverer = getInstance();
+    discoverer.addServiceChangeListener((_discoverer, _services) -> {
+      if (discoverer.services != null)
       {
+        System.out.println("Changesupport: There are now "
+                + discoverer.services.size() + " services");
+        int i = 1;
+        for (ServiceWithParameters s_instance : discoverer.services)
+        {
+          System.out.println(
+                  "Service " + i++ + " " + s_instance.getClass()
+                          + "@" + s_instance.getHostURL() + ": "
+                          + s_instance.getActionText());
+        }
+
       }
+    });
+    try
+    {
+      discoverer.startDiscoverer().get();
+    } catch (InterruptedException | ExecutionException e)
+    {
     }
     try
     {
@@ -630,30 +380,20 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     }
   }
 
-  /**
-   * Returns the singleton instance of this class.
-   * 
-   * @return
-   */
-  public static Jws2Discoverer getDiscoverer()
-  {
-    if (discoverer == null)
-    {
-      discoverer = new Jws2Discoverer();
-    }
-    return discoverer;
-  }
 
+  @Override
   public boolean hasServices()
   {
     return !running && services != null && services.size() > 0;
   }
 
+  @Override
   public boolean isRunning()
   {
     return running;
   }
 
+  @Override
   public void setServiceUrls(List<String> wsUrls)
   {
     if (wsUrls != null && !wsUrls.isEmpty())
@@ -680,6 +420,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * 
    * @return
    */
+  @Override
   public List<String> getServiceUrls()
   {
     if (testUrls != null)
@@ -733,7 +474,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return urls;
   }
 
-  public Vector<Jws2Instance> getServices()
+  @Override
+  public Vector<ServiceWithParameters> getServices()
   {
     return (services == null) ? new Vector<>() : new Vector<>(services);
   }
@@ -744,7 +486,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param foo
    * @return
    */
-  public static boolean testServiceUrl(URL foo)
+  @Override
+  public boolean testServiceUrl(URL foo)
   {
     try
     {
@@ -792,7 +535,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param changeSupport2
    * @return new thread
    */
-  public Thread startDiscoverer(PropertyChangeListener changeSupport2)
+  @Override
+  public CompletableFuture<WSDiscovererI> startDiscoverer()
   {
     /*    if (restart())
         {
@@ -808,10 +552,12 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     {
       setAborted(true);
     }
-    addPropertyChangeListener(changeSupport2);
-    Thread thr = new Thread(this);
-    thr.start();
-    return thr;
+    CompletableFuture<WSDiscovererI> task = CompletableFuture
+            .supplyAsync(() -> {
+              run();
+              return Jws2Discoverer.this;
+            });
+    return task;
   }
 
   /**
@@ -873,6 +619,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @return a human readable report of any problems with the service URLs used
    *         for discovery
    */
+  @Override
   public String getErrorMessages()
   {
     if (!isRunning() && !isAborted())
@@ -921,129 +668,22 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return null;
   }
 
+  @Override
   public int getServerStatusFor(String url)
   {
     if (validServiceUrls != null && validServiceUrls.contains(url))
     {
-      return 1;
+      return STATUS_OK;
     }
     if (urlsWithoutServices != null && urlsWithoutServices.contains(url))
     {
-      return 0;
+      return STATUS_NO_SERVICES;
     }
     if (invalidServiceUrls != null && invalidServiceUrls.contains(url))
     {
-      return -1;
+      return STATUS_INVALID;
     }
-    return -2;
-  }
-
-  /**
-   * pick the user's preferred service based on a set of URLs (jaba server
-   * locations) and service URIs (specifying version and service interface
-   * class)
-   * 
-   * @param serviceURL
-   * @return null or best match for given uri/ls.
-   */
-  public Jws2Instance getPreferredServiceFor(String[] serviceURLs)
-  {
-    HashSet<String> urls = new HashSet<>();
-    urls.addAll(Arrays.asList(serviceURLs));
-    Jws2Instance match = null;
-    if (services != null)
-    {
-      for (Jws2Instance svc : services)
-      {
-        if (urls.contains(svc.getServiceTypeURI()))
-        {
-          if (match == null)
-          {
-            // for moment we always pick service from server ordered first in
-            // user's preferences
-            match = svc;
-          }
-          if (urls.contains(svc.getUri()))
-          {
-            // stop and return - we've matched type URI and URI for service
-            // endpoint
-            return svc;
-          }
-        }
-      }
-    }
-    return match;
-  }
-
-  Map<String, Map<String, String>> preferredServiceMap = new HashMap<>();
-
-  /**
-   * get current preferred service of the given type, or global default
-   * 
-   * @param af
-   *          null or a specific alignFrame
-   * @param serviceType
-   *          Jws2Instance.serviceType for service
-   * @return null if no service of this type is available, the preferred service
-   *         for the serviceType and af if specified and if defined.
-   */
-  public Jws2Instance getPreferredServiceFor(AlignFrame af,
-          String serviceType)
-  {
-    String serviceurl = null;
-    synchronized (preferredServiceMap)
-    {
-      String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
-      Map<String, String> prefmap = preferredServiceMap.get(afid);
-      if (afid.length() > 0 && prefmap == null)
-      {
-        // recover global setting, if any
-        prefmap = preferredServiceMap.get("");
-      }
-      if (prefmap != null)
-      {
-        serviceurl = prefmap.get(serviceType);
-      }
-
-    }
-    Jws2Instance response = null;
-    for (Jws2Instance svc : services)
-    {
-      if (svc.serviceType.equals(serviceType))
-      {
-        if (serviceurl == null || serviceurl.equals(svc.getHost()))
-        {
-          response = svc;
-          break;
-        }
-      }
-    }
-    return response;
-  }
-
-  public void setPreferredServiceFor(AlignFrame af, String serviceType,
-          String serviceAction, Jws2Instance selectedServer)
-  {
-    String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
-    if (preferredServiceMap == null)
-    {
-      preferredServiceMap = new HashMap<>();
-    }
-    Map<String, String> prefmap = preferredServiceMap.get(afid);
-    if (prefmap == null)
-    {
-      prefmap = new HashMap<>();
-      preferredServiceMap.put(afid, prefmap);
-    }
-    prefmap.put(serviceType, selectedServer.getHost());
-    prefmap.put(serviceAction, selectedServer.getHost());
-  }
-
-  public void setPreferredServiceFor(String serviceType,
-          String serviceAction, Jws2Instance selectedServer)
-  {
-    setPreferredServiceFor(null, serviceType, serviceAction,
-            selectedServer);
+    return STATUS_UNKNOWN;
   }
 
   /**
index 9caa803..40583ab 100644 (file)
  */
 package jalview.ws.jws2;
 
-import java.util.Locale;
+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.WSMenuEntryProviderI;
+import jalview.ws.api.JalviewServiceEndpointProviderI;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.gui.MsaWSThread;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.util.List;
+import java.util.Locale;
 
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 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!
+ * MsaWSClient
  * 
- * @author $author$
+ * Instantiates web service menu items for multiple alignment services, and
+ * holds logic for constructing a web service thread.
+ * 
+ * TODO remove dependency on Jws2Client methods for creating AACon service UI
+ * elements.
+ * 
+ * @author Jim Procter et al
  * @version $Revision$
  */
-public class MsaWSClient extends Jws2Client
+public class MsaWSClient extends Jws2Client implements WSMenuEntryProviderI
 {
   /**
-   * server is a WSDL2Java generated stub for an archetypal MsaWSI service.
+   * server is a proxy class implementing the core methods for submitting,
+   * monitoring and retrieving results from a multiple sequence alignment
+   * service
    */
-  MsaWS server;
+  MultipleSequenceAlignmentI server;
 
-  public MsaWSClient(Jws2Instance sh, String altitle,
+  public MsaWSClient(ServiceWithParameters sh, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
           boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
@@ -67,7 +78,8 @@ public class MsaWSClient extends Jws2Client
     // TODO Auto-generated constructor stub
   }
 
-  public MsaWSClient(Jws2Instance sh, WsParamSetI preset, String altitle,
+  public MsaWSClient(ServiceWithParameters sh, WsParamSetI preset,
+          String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
           boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
@@ -93,44 +105,48 @@ public class MsaWSClient extends Jws2Client
    *          DOCUMENT ME!
    */
 
-  public MsaWSClient(Jws2Instance sh, WsParamSetI preset,
-          List<Argument> arguments, boolean editParams, String altitle,
+  public MsaWSClient(ServiceWithParameters sh, WsParamSetI preset,
+          List<ArgumentI> arguments, boolean editParams, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
           boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
   {
     super(_alignFrame, preset, arguments);
-    if (!processParams(sh, editParams))
-    {
-      return;
-    }
-
-    if (!(sh.service instanceof MsaWS))
-    {
-      // redundant at mo - but may change
-      JvOptionPane.showMessageDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.service_called_is_not_msa_service",
-                      new String[]
-                      { sh.serviceType }),
-              MessageManager.getString("label.internal_jalview_error"),
-              JvOptionPane.WARNING_MESSAGE);
-
-      return;
-    }
-    server = (MsaWS) sh.service;
-    if ((wsInfo = setWebService(sh, false)) == null)
-    {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
-              .formatMessage("label.msa_service_is_unknown", new String[]
-              { sh.serviceType }),
-              MessageManager.getString("label.internal_jalview_error"),
-              JvOptionPane.WARNING_MESSAGE);
-
-      return;
-    }
-
-    startMsaWSClient(altitle, msa, submitGaps, preserveOrder, seqdataset);
+    processParams(sh, editParams).thenAccept((startJob) -> {
+      if (!startJob)
+        return;
+      
+      if (!(sh instanceof JalviewServiceEndpointProviderI
+              && ((JalviewServiceEndpointProviderI) sh)
+                      .getEndpoint() instanceof MultipleSequenceAlignmentI))
+      {
+        // redundant at mo - but may change
+        JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
+                MessageManager.formatMessage(
+                        "label.service_called_is_not_msa_service",
+                        new String[]
+                        { sh.getName() }),
+                MessageManager.getString("label.internal_jalview_error"),
+                JvOptionPane.WARNING_MESSAGE);
+  
+        return;
+      }
+      serviceHandle = sh;
+      server = (MultipleSequenceAlignmentI) ((JalviewServiceEndpointProviderI) sh)
+              .getEndpoint();
+      if ((wsInfo = setWebService(sh, false)) == null)
+      {
+        JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), MessageManager
+                .formatMessage("label.msa_service_is_unknown", new String[]
+                { sh.getName() }),
+                MessageManager.getString("label.internal_jalview_error"),
+                JvOptionPane.WARNING_MESSAGE);
+  
+        return;
+      }
+  
+      startMsaWSClient(altitle, msa, submitGaps, preserveOrder, seqdataset);
+    });
 
   }
 
@@ -215,16 +231,28 @@ public class MsaWSClient extends Jws2Client
 
   @Override
   public void attachWSMenuEntry(JMenu rmsawsmenu,
-          final Jws2Instance service, final AlignFrame af)
+          final ServiceWithParameters service, final AlignFrame alignFrame)
   {
-    if (registerAAConWSInstance(rmsawsmenu, service, af))
+    if (Jws2ClientFactory.registerAAConWSInstance(rmsawsmenu,
+                    service, alignFrame))
     {
-      // Alignment dependent analysis calculation WS gui
+           // Alignment dependent analysis calculation WS gui
       return;
     }
+    serviceHandle = service;
     setWebService(service, true); // headless
+    attachWSMenuEntry(rmsawsmenu, alignFrame);
+  }
+
+  @Override
+  public void attachWSMenuEntry(JMenu wsmenu, AlignFrame alignFrame)
+  {
     boolean finished = true, submitGaps = false;
-    JMenu msawsmenu = rmsawsmenu;
+    /**
+     * temp variables holding msa service submenu or root service menu
+     */
+    JMenu msawsmenu = wsmenu;
+    JMenu rmsawsmenu = wsmenu;
     String svcname = WebServiceName;
     if (svcname.endsWith("WS"))
     {
@@ -237,7 +265,8 @@ public class MsaWSClient extends Jws2Client
       rmsawsmenu.add(msawsmenu);
       calcName = "";
     }
-    boolean hasparams = service.hasParameters();
+    boolean hasparams = serviceHandle.hasParameters();
+    ServiceWithParameters service = (ServiceWithParameters) serviceHandle;
     do
     {
       String action = "Align ";
@@ -265,12 +294,14 @@ public class MsaWSClient extends Jws2Client
         @Override
         public void actionPerformed(ActionEvent e)
         {
-          AlignmentView msa = af.gatherSequencesForAlignment();
+          AlignmentView msa = alignFrame.gatherSequencesForAlignment();
 
           if (msa != null)
           {
-            new MsaWSClient(service, af.getTitle(), msa, withGaps, true,
-                    af.getViewport().getAlignment().getDataset(), af);
+            new MsaWSClient(service, alignFrame.getTitle(), msa, withGaps,
+                    true,
+                    alignFrame.getViewport().getAlignment().getDataset(),
+                    alignFrame);
           }
 
         }
@@ -290,10 +321,13 @@ public class MsaWSClient extends Jws2Client
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            AlignmentView msa = af.gatherSequencesForAlignment();
+            AlignmentView msa = alignFrame.gatherSequencesForAlignment();
             if (msa != null)
             {
-              startJob(service, af, withGaps, msa);
+              new MsaWSClient(service, null, null, true,
+                      alignFrame.getTitle(), msa, withGaps, true,
+                      alignFrame.getViewport().getAlignment().getDataset(),
+                      alignFrame);
             }
 
           }
@@ -308,9 +342,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
@@ -332,24 +366,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 = af.gatherSequencesForAlignment();
+                AlignmentView msa = alignFrame
+                        .gatherSequencesForAlignment();
 
                 if (msa != null)
                 {
-                  MsaWSClient msac = new MsaWSClient(service, preSet,
-                          af.getTitle(), msa, false, true,
-                          af.getViewport().getAlignment().getDataset(), af);
+                  MsaWSClient msac = new MsaWSClient(service, preset,
+                          alignFrame.getTitle(), msa, false, true,
+                          alignFrame.getViewport().getAlignment()
+                                  .getDataset(),
+                          alignFrame);
                 }
 
               }
@@ -371,21 +408,4 @@ 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 c7fad05..d789dda 100644 (file)
@@ -37,7 +37,7 @@ public class ParameterUtils
   public static List<String> writeParameterSet(List<Option> optSet,
           String pseparator)
   {
-    List<String> pset = new ArrayList<String>();
+    List<String> pset = new ArrayList<>();
     for (Option o : optSet)
     {
       pset.add(o.toCommand(pseparator));
@@ -64,7 +64,7 @@ public class ParameterUtils
   public static List<Option> processParameters(List<String> params,
           RunnerConfig options, String pseparator)
   {
-    List<Option> chosenOptions = new ArrayList<Option>();
+    List<Option> chosenOptions = new ArrayList<>();
     for (String param : params)
     {
       String oname = null;
diff --git a/src/jalview/ws/jws2/PreferredServiceChangeListener.java b/src/jalview/ws/jws2/PreferredServiceChangeListener.java
new file mode 100644 (file)
index 0000000..ff959ca
--- /dev/null
@@ -0,0 +1,9 @@
+package jalview.ws.jws2;
+
+import jalview.ws.api.ServiceWithParameters;
+
+@FunctionalInterface
+public interface PreferredServiceChangeListener
+{
+  public void preferredServiceChanged(ServiceWithParameters newValue);
+}
diff --git a/src/jalview/ws/jws2/PreferredServiceRegistry.java b/src/jalview/ws/jws2/PreferredServiceRegistry.java
new file mode 100644 (file)
index 0000000..45af925
--- /dev/null
@@ -0,0 +1,355 @@
+package jalview.ws.jws2;
+
+import jalview.bin.Cache;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+public class PreferredServiceRegistry
+{
+
+  private static PreferredServiceRegistry us = new PreferredServiceRegistry();
+
+  public static PreferredServiceRegistry getRegistry()
+  {
+    if (us == null)
+    {
+      us = new PreferredServiceRegistry();
+    }
+    return us;
+  }
+
+  List<ServiceWithParameters> ourServices = new ArrayList<>();
+
+  /**
+   * forget about any known services
+   */
+  public void clearServices()
+  {
+    ourServices.clear();
+  }
+
+  public void populateWSMenuEntry(List<ServiceWithParameters> services,
+          PreferredServiceChangeListener changeListener, JMenu menu,
+          final AlignFrame alignFrame, String typeFilter)
+  {
+    /**
+     * eventually, JWS2 services will appear under the same align/etc submenus.
+     * for moment we keep them separate.
+     */
+    ourServices.addAll(services);
+    JMenu atpoint;
+    
+    List<ServiceWithParameters> oneshotServices = new ArrayList<>();
+    List<ServiceWithParameters> interactiveServices = new ArrayList<>();
+    Map<String, ServiceWithParameters> preferredHosts = new HashMap<>();
+    Map<String, List<ServiceWithParameters>> alternates = new HashMap<>();
+    
+    for (var service : services) 
+    {
+      if (service.isInteractiveUpdate())
+        interactiveServices.add(service);
+      else
+        oneshotServices.add(service);
+    }
+    for (var service : interactiveServices)
+    {
+      if (!preferredHosts.containsKey(service.getName()))
+      {
+        var preferred = getPreferredServiceFor(alignFrame, service.getName());
+        preferredHosts.put(service.getName(), (preferred != null) ? preferred : service);
+      }
+      var ph = alternates.getOrDefault(service.getName(), new ArrayList<>());
+      if (!preferredHosts.containsValue(service)) 
+      {
+        ph.add(service);
+        alternates.putIfAbsent(service.getName(), ph);
+      }
+    }
+
+    // create GUI element for classic services
+    addEnumeratedServices(menu, alignFrame, oneshotServices);
+    // and the instantaneous services
+    for (final ServiceWithParameters service : preferredHosts.values())
+    {
+      atpoint = JvSwingUtils.findOrCreateMenu(menu,
+              service.getServiceType());
+      if (atpoint.getItemCount() > 1)
+      {
+        // previous service of this type already present
+        atpoint.addSeparator();
+      }
+      JMenuItem hitm;
+      atpoint.add(hitm = new JMenuItem(service.getHostURL()));
+      hitm.setForeground(Color.blue);
+      hitm.addActionListener(e -> Desktop.showUrl(service.getHostURL()));
+      hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
+              MessageManager.getString("label.open_jabaws_web_page")));
+
+      service.attachWSMenuEntry(atpoint, alignFrame);
+      if (alternates.containsKey(service.getName()))
+      {
+        atpoint.add(hitm = new JMenu(
+                MessageManager.getString("label.switch_server")));
+        hitm.setToolTipText(JvSwingUtils.wrapTooltip(false,
+                MessageManager.getString("label.choose_jabaws_server")));
+        for (final ServiceWithParameters sv : alternates
+                .get(service.getName()))
+        {
+          JMenuItem itm;
+          hitm.add(itm = new JMenuItem(sv.getHostURL()));
+          itm.setForeground(Color.blue);
+          itm.addActionListener(e -> {
+            setPreferredServiceFor(alignFrame, sv.getName(), sv.getServiceType(), sv);
+            changeListener.preferredServiceChanged(sv);
+          });
+        }
+      }
+    }
+  }
+
+  /**
+   * add services using the Java 2.5/2.6/2.7 system which optionally creates
+   * submenus to index by host and service program type
+   */
+  private void addEnumeratedServices(final JMenu jws2al,
+          final AlignFrame alignFrame,
+          List<ServiceWithParameters> enumerableServices)
+  {
+    boolean byhost = Cache.getDefault("WSMENU_BYHOST", false),
+            bytype = Cache.getDefault("WSMENU_BYTYPE", false);
+    /**
+     * eventually, JWS2 services will appear under the same align/etc submenus.
+     * for moment we keep them separate.
+     */
+    JMenu atpoint;
+
+    List<String> hostLabels = new ArrayList<>();
+    Hashtable<String, String> lasthostFor = new Hashtable<>();
+    Hashtable<String, ArrayList<ServiceWithParameters>> hosts = new Hashtable<>();
+    ArrayList<String> hostlist = new ArrayList<>();
+    for (ServiceWithParameters service : enumerableServices)
+    {
+      ArrayList<ServiceWithParameters> hostservices = hosts
+              .get(service.getHostURL());
+      if (hostservices == null)
+      {
+        hosts.put(service.getHostURL(), hostservices = new ArrayList<>());
+        hostlist.add(service.getHostURL());
+      }
+      hostservices.add(service);
+    }
+    // now add hosts in order of the given array
+    for (String host : hostlist)
+    {
+      ServiceWithParameters orderedsvcs[] = hosts.get(host)
+              .toArray(new ServiceWithParameters[1]);
+      String sortbytype[] = new String[orderedsvcs.length];
+      for (int i = 0; i < sortbytype.length; i++)
+      {
+        sortbytype[i] = orderedsvcs[i].getName();
+      }
+      jalview.util.QuickSort.sort(sortbytype, orderedsvcs);
+      for (final ServiceWithParameters service : orderedsvcs)
+      {
+        atpoint = JvSwingUtils.findOrCreateMenu(jws2al,
+                service.getAction());
+        String type = service.getName();
+        if (byhost)
+        {
+          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, host);
+          if (atpoint.getToolTipText() == null)
+          {
+            atpoint.setToolTipText(MessageManager
+                    .formatMessage("label.services_at", new String[]
+                    { host }));
+          }
+        }
+        if (bytype)
+        {
+          atpoint = JvSwingUtils.findOrCreateMenu(atpoint, type);
+          if (atpoint.getToolTipText() == null)
+          {
+            atpoint.setToolTipText(service.getActionText());
+          }
+        }
+        if (!byhost && !hostLabels.contains(
+                host + service.getName() + service.getActionText()))
+        // !hostLabels.contains(host + (bytype ?
+        // service.serviceType+service.getActionText() : "")))
+        {
+          // add a marker indicating where this service is hosted
+          // relies on services from the same host being listed in a
+          // contiguous
+          // group
+          JMenuItem hitm;
+          if (hostLabels.contains(host))
+          {
+            atpoint.addSeparator();
+          }
+          else
+          {
+            hostLabels.add(host);
+          }
+          if (lasthostFor.get(service.getAction()) == null
+                  || !lasthostFor.get(service.getAction()).equals(host))
+          {
+            atpoint.add(hitm = new JMenuItem(host));
+            hitm.setForeground(Color.blue);
+            hitm.addActionListener(new ActionListener()
+            {
+
+              @Override
+              public void actionPerformed(ActionEvent e)
+              {
+                Desktop.showUrl(service.getHostURL());
+              }
+            });
+            hitm.setToolTipText(
+                    JvSwingUtils.wrapTooltip(true, MessageManager
+                            .getString("label.open_jabaws_web_page")));
+            lasthostFor.put(service.getAction(), host);
+          }
+          hostLabels
+                  .add(host + service.getName() + service.getActionText());
+        }
+
+        service.attachWSMenuEntry(atpoint, alignFrame);
+      }
+    }
+  }
+
+  /**
+   * pick the user's preferred service based on a set of URLs (jaba server
+   * locations) and service URIs (specifying version and service interface
+   * class)
+   * 
+   * @param serviceURL
+   * @return null or best match for given uri/ls.
+   */
+  public ServiceWithParameters getPreferredServiceFor(String[] serviceURLs)
+  {
+    HashSet<String> urls = new HashSet<>();
+    urls.addAll(Arrays.asList(serviceURLs));
+    ServiceWithParameters match = null;
+
+    if (ourServices != null)
+    {
+      for (ServiceWithParameters svc : ourServices)
+      {
+        // TODO getNameURI Should return a versioned URI for the service, but
+        // doesn't as of 2.11
+        if (urls.contains(svc.getNameURI()))
+        {
+          if (match == null)
+          {
+            // for moment we always pick service from server ordered first in
+            // user's preferences
+            match = svc;
+          }
+          if (urls.contains(svc.getUri()))
+          {
+            // stop and return - we've matched type URI and URI for service
+            // endpoint
+            return svc;
+          }
+        }
+      }
+    }
+    return match;
+  }
+
+  Map<String, Map<String, String>> preferredServiceMap = new HashMap<>();
+
+  /**
+   * get current preferred endpoint of the given Jabaws service, or global
+   * default
+   * 
+   * @param af
+   *          null or a specific alignFrame
+   * @param serviceName
+   *          ServiceWithParameters.getName() for service
+   * @return null if no service of this type is available, the preferred service
+   *         for the serviceType and af if specified and if defined.
+   */
+  public ServiceWithParameters getPreferredServiceFor(AlignFrame af,
+          String serviceName)
+  {
+    String serviceurl = null;
+    synchronized (preferredServiceMap)
+    {
+      String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
+      Map<String, String> prefmap = preferredServiceMap.get(afid);
+      if (afid.length() > 0 && prefmap == null)
+      {
+        // recover global setting, if any
+        prefmap = preferredServiceMap.get("");
+      }
+      if (prefmap != null)
+      {
+        serviceurl = prefmap.get(serviceName);
+      }
+
+    }
+    ServiceWithParameters response = null;
+    for (ServiceWithParameters svc : ourServices)
+    {
+      if (svc.getName().equals(serviceName))
+      {
+        if (serviceurl == null || serviceurl.equals(svc.getHostURL()))
+        {
+          response = svc;
+          break;
+        }
+      }
+    }
+    return response;
+  }
+
+  public void setPreferredServiceFor(AlignFrame af, String serviceName,
+          String serviceAction, ServiceWithParameters selectedServer)
+  {
+    // TODO: pull out and generalise for the selectedServer's attributes
+    String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
+    if (preferredServiceMap == null)
+    {
+      preferredServiceMap = new HashMap<>();
+    }
+    Map<String, String> prefmap = preferredServiceMap.get(afid);
+    if (prefmap == null)
+    {
+      prefmap = new HashMap<>();
+      preferredServiceMap.put(afid, prefmap);
+    }
+    prefmap.put(serviceName, selectedServer.getHostURL());
+    prefmap.put(serviceAction, selectedServer.getHostURL());
+  }
+
+  public void setPreferredServiceFor(String serviceType,
+          String serviceAction, ServiceWithParameters selectedServer)
+  {
+    setPreferredServiceFor(null, serviceType, serviceAction,
+            selectedServer);
+  }
+
+  public boolean contains(ServiceWithParameters service)
+  {
+    return ourServices.contains(service);
+  }
+
+}
diff --git a/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java b/src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java
new file mode 100644 (file)
index 0000000..3253f43
--- /dev/null
@@ -0,0 +1,843 @@
+/*
+ * 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.jws2;
+
+import jalview.analysis.AlignSeq;
+import jalview.analysis.AlignmentAnnotationUtils;
+import jalview.analysis.SeqsetUtils;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureColourI;
+import jalview.api.PollableAlignCalcWorkerI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.ContiguousI;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.IProgressIndicator;
+import jalview.gui.IProgressIndicatorHandler;
+import jalview.gui.JvOptionPane;
+import jalview.gui.WebserviceInfo;
+import jalview.schemes.FeatureSettingsAdapter;
+import jalview.schemes.ResidueProperties;
+import jalview.util.MapList;
+import jalview.util.MessageManager;
+import jalview.workers.AlignCalcWorker;
+import jalview.ws.JobStateSummary;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JalviewServiceEndpointProviderI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.SequenceAnnotationServiceI;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.api.WSAnnotationCalcManagerI;
+import jalview.ws.gui.AnnotationWsJob;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker
+        implements WSAnnotationCalcManagerI, PollableAlignCalcWorkerI
+{
+
+  protected ServiceWithParameters service;
+
+  protected WsParamSetI preset;
+
+  protected List<ArgumentI> arguments;
+
+  protected IProgressIndicator guiProgress;
+
+  protected boolean submitGaps = true;
+
+  /**
+   * by default, we filter out non-standard residues before submission
+   */
+  protected boolean filterNonStandardResidues = true;
+
+  /**
+   * Recover any existing parameters for this service
+   */
+  protected void initViewportParams()
+  {
+    if (getCalcId() != null)
+    {
+      ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
+              getCalcId(),
+              new AAConSettings(true, service, this.preset, arguments),
+              true);
+    }
+  }
+
+  /**
+   * 
+   * @return null or a string used to recover all annotation generated by this
+   *         worker
+   */
+  public String getCalcId()
+  {
+    return service.getAlignAnalysisUI() == null ? null
+            : service.getAlignAnalysisUI().getCalcId();
+  }
+
+  public WsParamSetI getPreset()
+  {
+    return preset;
+  }
+
+  public List<ArgumentI> getArguments()
+  {
+    return arguments;
+  }
+
+  /**
+   * reconfigure and restart the AAConClient. This method will spawn a new
+   * thread that will wait until any current jobs are finished, modify the
+   * parameters and restart the conservation calculation with the new values.
+   * 
+   * @param newpreset
+   * @param newarguments
+   */
+  public void updateParameters(final WsParamSetI newpreset,
+          final List<ArgumentI> newarguments)
+  {
+    preset = newpreset;
+    arguments = newarguments;
+    calcMan.startWorker(this);
+    initViewportParams();
+  }
+  protected boolean alignedSeqs = true;
+
+  protected boolean nucleotidesAllowed = false;
+
+  protected boolean proteinAllowed = false;
+
+  /**
+   * record sequences for mapping result back to afterwards
+   */
+  protected boolean bySequence = false;
+
+  protected Map<String, SequenceI> seqNames;
+
+  // TODO: convert to bitset
+  protected boolean[] gapMap;
+
+  int realw;
+
+  protected int start;
+
+  int end;
+
+  private AlignFrame alignFrame;
+
+  public boolean[] getGapMap()
+  {
+    return gapMap;
+  }
+
+  public SeqAnnotationServiceCalcWorker(ServiceWithParameters service,
+          AlignFrame alignFrame,
+          WsParamSetI preset, List<ArgumentI> paramset)
+  {
+    super(alignFrame.getCurrentView(), alignFrame.alignPanel);
+    // TODO: both these fields needed ?
+    this.alignFrame = alignFrame;
+    this.guiProgress = alignFrame;
+    this.preset = preset;
+    this.arguments = paramset;
+    this.service = service;
+    try
+    {
+      annotService = (jalview.ws.api.SequenceAnnotationServiceI) ((JalviewServiceEndpointProviderI) service)
+              .getEndpoint();
+    } catch (ClassCastException cce)
+    {
+      annotService = null;
+      JvOptionPane.showMessageDialog(Desktop.getInstance(),
+              MessageManager.formatMessage(
+                      "label.service_called_is_not_an_annotation_service",
+                      new String[]
+                      { service.getName() }),
+              MessageManager.getString("label.internal_jalview_error"),
+              JvOptionPane.WARNING_MESSAGE);
+
+    }
+    cancellable = CancellableI.class.isInstance(annotService);
+    // configure submission flags
+    proteinAllowed = service.isProteinService();
+    nucleotidesAllowed = service.isNucleotideService();
+    alignedSeqs = service.isNeedsAlignedSequences();
+    bySequence = !service.isAlignmentAnalysis();
+    filterNonStandardResidues = service.isFilterSymbols();
+    min_valid_seqs = service.getMinimumInputSequences();
+    submitGaps = service.isAlignmentAnalysis();
+
+    if (service.isInteractiveUpdate())
+    {
+      initViewportParams();
+    }
+  }
+
+  /**
+   * 
+   * @return true if the submission thread should attempt to submit data
+   */
+  public boolean hasService()
+  {
+    return annotService != null;
+  }
+
+  protected SequenceAnnotationServiceI annotService;
+  protected final boolean cancellable;
+
+  volatile JobId rslt = null;
+
+  AnnotationWsJob running = null;
+
+  private int min_valid_seqs;
+
+
+  private long progressId = -1;
+  JobStateSummary job = null;
+  WebserviceInfo info = null;
+  List<SequenceI> seqs = null;
+  
+  @Override public void startUp() throws Throwable
+  {
+    if (alignViewport.isClosed())
+    {
+      abortAndDestroy();
+      return;
+    }
+    if (!hasService())
+    {
+      return;
+    }
+
+    StringBuffer msg = new StringBuffer();
+    job = new JobStateSummary();
+    info = new WebserviceInfo("foo", "bar", false);
+
+    seqs = getInputSequences(
+            alignViewport.getAlignment(),
+            bySequence ? alignViewport.getSelectionGroup() : null);
+
+    if (seqs == null || !checkValidInputSeqs(seqs))
+    {
+      jalview.bin.Console.debug(
+              "Sequences for analysis service were null or not valid");
+      return;
+    }
+
+    if (guiProgress != null)
+    {
+      guiProgress.setProgressBar(service.getActionText(),
+              progressId = System.currentTimeMillis());
+    }
+    jalview.bin.Console.debug("submitted " + seqs.size()
+            + " sequences to " + service.getActionText());
+
+    rslt = annotService.submitToService(seqs, getPreset(),
+            getArguments());
+    if (rslt == null)
+    {
+      return;
+    }
+    // TODO: handle job submission error reporting here.
+    Console.debug("Service " + service.getUri() + "\nSubmitted job ID: "
+            + rslt);
+    // ///
+    // otherwise, construct WsJob and any UI handlers
+    running = new AnnotationWsJob();
+    running.setJobHandle(rslt);
+    running.setSeqNames(seqNames);
+    running.setStartPos(start);
+    running.setSeqs(seqs);
+    job.updateJobPanelState(info, "", running);
+    if (guiProgress != null)
+    {
+      guiProgress.registerHandler(progressId,
+              new IProgressIndicatorHandler()
+              {
+
+                @Override
+                public boolean cancelActivity(long id)
+                {
+                  calcMan.cancelWorker(SeqAnnotationServiceCalcWorker.this);
+                  return true;
+                }
+
+                @Override
+                public boolean canCancel()
+                {
+                  return cancellable;
+                }
+              });
+    }
+  }
+  
+  @Override public boolean poll() throws Throwable
+  {
+    boolean finished = false;
+    
+    Console.debug("Updating status for annotation service.");
+    annotService.updateStatus(running);
+    job.updateJobPanelState(info, "", running);
+    if (running.isSubjobComplete())
+    {
+      Console.debug(
+              "Finished polling analysis service job: status reported is "
+                      + running.getState());
+      finished = true;
+    }
+    else
+    {
+      Console.debug("Status now " + running.getState());
+    }
+
+    // pull any stats - some services need to flush log output before
+    // results are available
+    Console.debug("Updating progress log for annotation service.");
+
+    try
+    {
+      annotService.updateJobProgress(running);
+    } catch (Throwable thr)
+    {
+      Console.debug("Ignoring exception during progress update.",
+              thr);
+    }
+    Console.debug("Result of poll: " + running.getStatus());
+    
+    
+    if (finished)
+    {
+      Console.debug("Job poll loop exited. Job is " + running.getState());
+      if (running.isFinished())
+      {
+        // expect there to be results to collect
+        // configure job with the associated view's feature renderer, if one
+        // exists.
+        // TODO: here one would also grab the 'master feature renderer' in order
+        // to enable/disable
+        // features automatically according to user preferences
+        running.setFeatureRenderer(
+                ((jalview.gui.AlignmentPanel) ap).cloneFeatureRenderer());
+        Console.debug("retrieving job results.");
+        final Map<String, FeatureColourI> featureColours = new HashMap<>();
+        final Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
+        List<AlignmentAnnotation> returnedAnnot = annotService
+                .getAnnotationResult(running.getJobHandle(), seqs,
+                        featureColours, featureFilters);
+
+        Console.debug("Obtained " + (returnedAnnot == null ? "no rows"
+                : ("" + returnedAnnot.size())));
+        Console.debug("There were " + featureColours.size()
+                + " feature colours and " + featureFilters.size()
+                + " filters defined.");
+
+        // TODO
+        // copy over each annotation row reurned and also defined on each
+        // sequence, excluding regions not annotated due to gapMap/column
+        // visibility
+
+        // update calcId if it is not already set on returned annotation
+        if (returnedAnnot != null)
+        {
+          for (AlignmentAnnotation aa : returnedAnnot)
+          {
+            // assume that any CalcIds already set
+            if (getCalcId() != null && aa.getCalcId() == null
+                    || "".equals(aa.getCalcId()))
+            {
+              aa.setCalcId(getCalcId());
+            }
+            // autocalculated annotation are created by interactive alignment
+            // analysis services
+            aa.autoCalculated = service.isAlignmentAnalysis()
+                    && service.isInteractiveUpdate();
+          }
+        }
+
+        running.setAnnotation(returnedAnnot);
+
+        if (running.hasResults())
+        {
+          jalview.bin.Console.debug("Updating result annotation from Job "
+                  + rslt + " at " + service.getUri());
+          updateResultAnnotation(true);
+          if (running.isTransferSequenceFeatures())
+          {
+            // TODO
+            // look at each sequence and lift over any features, excluding
+            // regions
+            // not annotated due to gapMap/column visibility
+
+            jalview.bin.Console.debug(
+                    "Updating feature display settings and transferring features from Job "
+                            + rslt + " at " + service.getUri());
+            // TODO: consider merge rather than apply here
+            alignViewport.applyFeaturesStyle(new FeatureSettingsAdapter()
+            {
+              @Override
+              public FeatureColourI getFeatureColour(String type)
+              {
+                return featureColours.get(type);
+              }
+
+              @Override
+              public FeatureMatcherSetI getFeatureFilters(String type)
+              {
+                return featureFilters.get(type);
+              }
+
+              @Override
+              public boolean isFeatureDisplayed(String type)
+              {
+                return featureColours.containsKey(type);
+              }
+
+            });
+            // TODO: JAL-1150 - create sequence feature settings API for
+            // defining
+            // styles and enabling/disabling feature overlay on alignment panel
+
+            if (alignFrame.alignPanel == ap)
+            {
+              alignViewport.setShowSequenceFeatures(true);
+              alignFrame.setMenusForViewport();
+            }
+          }
+          ap.adjustAnnotationHeight();
+        }
+      }
+      Console.debug("Annotation Service Worker thread finished.");
+
+    }
+    
+    return finished;
+  }
+  
+  @Override public void cancel()
+  {
+    cancelCurrentJob();
+  }
+  
+  @Override public void done()
+  {
+    if (ap != null)
+    {
+      if (guiProgress != null && progressId != -1)
+      {
+        guiProgress.removeProgressBar(progressId);
+      }
+      // TODO: may not need to paintAlignment again !
+      ap.paintAlignment(false, false);
+    }
+  }
+
+  /**
+   * validate input for dynamic/non-dynamic update context TODO: move to
+   * analysis interface ?
+   * @param seqs
+   * 
+   * @return true if input is valid
+   */
+  boolean checkValidInputSeqs(List<SequenceI> seqs)
+  {
+    int nvalid = 0;
+    for (SequenceI sq : seqs)
+    {
+      if (sq.getStart() <= sq.getEnd()
+              && (sq.isProtein() ? proteinAllowed : nucleotidesAllowed))
+      {
+        if (submitGaps
+                || sq.getLength() == (sq.getEnd() - sq.getStart() + 1))
+        {
+          nvalid++;
+        }
+      }
+    }
+    return nvalid >= min_valid_seqs;
+  }
+
+  public void cancelCurrentJob()
+  {
+    try
+    {
+      String id = running.getJobId();
+      if (cancellable && ((CancellableI) annotService).cancel(running))
+      {
+        System.err.println("Cancelled job " + id);
+      }
+      else
+      {
+        System.err.println("Job " + id + " couldn't be cancelled.");
+      }
+    } catch (Exception q)
+    {
+      q.printStackTrace();
+    }
+  }
+
+  /**
+   * Interactive updating. Analysis calculations that work on the currently
+   * displayed alignment data should cancel existing jobs when the input data
+   * has changed.
+   * 
+   * @return true if a running job should be cancelled because new input data is
+   *         available for analysis
+   */
+  boolean isInteractiveUpdate()
+  {
+    return service.isInteractiveUpdate();
+  }
+
+  /**
+   * decide what sequences will be analysed TODO: refactor to generate
+   * List<SequenceI> for submission to service interface
+   * 
+   * @param alignment
+   * @param inputSeqs
+   * @return
+   */
+  public List<SequenceI> getInputSequences(AlignmentI alignment,
+          AnnotatedCollectionI inputSeqs)
+  {
+    if (alignment == null || alignment.getWidth() <= 0
+            || alignment.getSequences() == null || alignment.isNucleotide()
+                    ? !nucleotidesAllowed
+                    : !proteinAllowed)
+    {
+      return null;
+    }
+    if (inputSeqs == null || inputSeqs.getWidth() <= 0
+            || inputSeqs.getSequences() == null
+            || inputSeqs.getSequences().size() < 1)
+    {
+      inputSeqs = alignment;
+    }
+
+    List<SequenceI> seqs = new ArrayList<>();
+
+    int minlen = 10;
+    int ln = -1;
+    if (bySequence)
+    {
+      seqNames = new HashMap<>();
+    }
+    gapMap = new boolean[0];
+    start = inputSeqs.getStartRes();
+    end = inputSeqs.getEndRes();
+    // TODO: URGENT! unify with JPred / MSA code to handle hidden regions
+    // correctly
+    // TODO: push attributes into WsJob instance (so they can be safely
+    // persisted/restored
+    for (SequenceI sq : (inputSeqs.getSequences()))
+    {
+      if (bySequence
+              ? sq.findPosition(end + 1)
+                      - sq.findPosition(start + 1) > minlen - 1
+              : sq.getEnd() - sq.getStart() > minlen - 1)
+      {
+        String newname = SeqsetUtils.unique_name(seqs.size() + 1);
+        // make new input sequence with or without gaps
+        if (seqNames != null)
+        {
+          seqNames.put(newname, sq);
+        }
+        SequenceI seq;
+        if (submitGaps)
+        {
+          seqs.add(seq = new jalview.datamodel.Sequence(newname,
+                  sq.getSequenceAsString()));
+          if (gapMap == null || gapMap.length < seq.getLength())
+          {
+            boolean[] tg = gapMap;
+            gapMap = new boolean[seq.getLength()];
+            System.arraycopy(tg, 0, gapMap, 0, tg.length);
+            for (int p = tg.length; p < gapMap.length; p++)
+            {
+              gapMap[p] = false; // init as a gap
+            }
+          }
+          for (int apos : sq.gapMap())
+          {
+            char sqc = sq.getCharAt(apos);
+            if (!filterNonStandardResidues
+                    || (sq.isProtein() ? ResidueProperties.aaIndex[sqc] < 20
+                            : ResidueProperties.nucleotideIndex[sqc] < 5))
+            {
+              gapMap[apos] = true; // aligned and real amino acid residue
+            }
+            ;
+          }
+        }
+        else
+        {
+          // TODO: add ability to exclude hidden regions
+          seqs.add(seq = new jalview.datamodel.Sequence(newname,
+                  AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
+                          sq.getSequenceAsString(start, end + 1))));
+          // for annotation need to also record map to sequence start/end
+          // position in range
+          // then transfer back to original sequence on return.
+        }
+        if (seq.getLength() > ln)
+        {
+          ln = seq.getLength();
+        }
+      }
+    }
+    if (alignedSeqs && submitGaps)
+    {
+      realw = 0;
+      for (int i = 0; i < gapMap.length; i++)
+      {
+        if (gapMap[i])
+        {
+          realw++;
+        }
+      }
+      // try real hard to return something submittable
+      // TODO: some of AAcon measures need a minimum of two or three amino
+      // acids at each position, and AAcon doesn't gracefully degrade.
+      for (int p = 0; p < seqs.size(); p++)
+      {
+        SequenceI sq = seqs.get(p);
+        // strip gapped columns
+        char[] padded = new char[realw],
+                orig = sq.getSequence();
+        for (int i = 0, pp = 0; i < realw; pp++)
+        {
+          if (gapMap[pp])
+          {
+            if (orig.length > pp)
+            {
+              padded[i++] = orig[pp];
+            }
+            else
+            {
+              padded[i++] = '-';
+            }
+          }
+        }
+        seqs.set(p, new jalview.datamodel.Sequence(sq.getName(),
+                new String(padded)));
+      }
+    }
+    return seqs;
+  }
+
+  @Override
+  public void updateAnnotation()
+  {
+    updateResultAnnotation(false);
+  }
+
+  public void updateResultAnnotation(boolean immediate)
+  {
+    if ((immediate || !calcMan.isWorking(this)) && running != null
+            && running.hasResults())
+    {
+      List<AlignmentAnnotation> ourAnnot = running.getAnnotation(),
+              newAnnots = new ArrayList<>();
+      //
+      // update graphGroup for all annotation
+      //
+      /**
+       * find a graphGroup greater than any existing ones this could be a method
+       * provided by alignment Alignment.getNewGraphGroup() - returns next
+       * unused graph group
+       */
+      int graphGroup = 1;
+      if (alignViewport.getAlignment().getAlignmentAnnotation() != null)
+      {
+        for (AlignmentAnnotation ala : alignViewport.getAlignment()
+                .getAlignmentAnnotation())
+        {
+          if (ala.graphGroup > graphGroup)
+          {
+            graphGroup = ala.graphGroup;
+          }
+        }
+      }
+      /**
+       * update graphGroup in the annotation rows returned from service
+       */
+      // TODO: look at sequence annotation rows and update graph groups in the
+      // case of reference annotation.
+      for (AlignmentAnnotation ala : ourAnnot)
+      {
+        if (ala.graphGroup > 0)
+        {
+          ala.graphGroup += graphGroup;
+        }
+        SequenceI aseq = null;
+
+        /**
+         * transfer sequence refs and adjust gapmap
+         */
+        if (ala.sequenceRef != null)
+        {
+          SequenceI seq = running.getSeqNames()
+                  .get(ala.sequenceRef.getName());
+          aseq = seq;
+          while (seq.getDatasetSequence() != null)
+          {
+            seq = seq.getDatasetSequence();
+          }
+        }
+        Annotation[] resAnnot = ala.annotations,
+                gappedAnnot = new Annotation[Math.max(
+                        alignViewport.getAlignment().getWidth(),
+                        gapMap.length)];
+        for (int p = 0, ap = start; ap < gappedAnnot.length; ap++)
+        {
+          if (gapMap != null && gapMap.length > ap && !gapMap[ap])
+          {
+            gappedAnnot[ap] = new Annotation("", "", ' ', Float.NaN);
+          }
+          else if (p < resAnnot.length)
+          {
+            gappedAnnot[ap] = resAnnot[p++];
+          }
+        }
+        ala.sequenceRef = aseq;
+        ala.annotations = gappedAnnot;
+        AlignmentAnnotation newAnnot = getAlignViewport().getAlignment()
+                .updateFromOrCopyAnnotation(ala);
+        if (aseq != null)
+        {
+
+          aseq.addAlignmentAnnotation(newAnnot);
+          newAnnot.adjustForAlignment();
+
+          AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith(
+                  newAnnot, newAnnot.label, newAnnot.getCalcId());
+        }
+        newAnnots.add(newAnnot);
+
+      }
+      for (SequenceI sq : running.getSeqs())
+      {
+        if (!sq.getFeatures().hasFeatures()
+                && (sq.getDBRefs() == null || sq.getDBRefs().size() == 0))
+        {
+          continue;
+        }
+        running.setTransferSequenceFeatures(true);
+        SequenceI seq = running.getSeqNames().get(sq.getName());
+        SequenceI dseq;
+        ContiguousI seqRange = seq.findPositions(start, end);
+
+        while ((dseq = seq).getDatasetSequence() != null)
+        {
+          seq = seq.getDatasetSequence();
+        }
+        List<ContiguousI> sourceRange = new ArrayList();
+        if (gapMap != null && gapMap.length >= end)
+        {
+          int lastcol = start, col = start;
+          do
+          {
+            if (col == end || !gapMap[col])
+            {
+              if (lastcol <= (col - 1))
+              {
+                seqRange = seq.findPositions(lastcol, col);
+                sourceRange.add(seqRange);
+              }
+              lastcol = col + 1;
+            }
+          } while (++col <= end);
+        }
+        else
+        {
+          sourceRange.add(seq.findPositions(start, end));
+        }
+        int i = 0;
+        int source_startend[] = new int[sourceRange.size() * 2];
+
+        for (ContiguousI range : sourceRange)
+        {
+          source_startend[i++] = range.getBegin();
+          source_startend[i++] = range.getEnd();
+        }
+        Mapping mp = new Mapping(
+                new MapList(source_startend, new int[]
+                { seq.getStart(), seq.getEnd() }, 1, 1));
+        dseq.transferAnnotation(sq, mp);
+
+      }
+      updateOurAnnots(newAnnots);
+    }
+  }
+
+  protected void updateOurAnnots(List<AlignmentAnnotation> ourAnnot)
+  {
+    List<AlignmentAnnotation> our = ourAnnots;
+    ourAnnots = ourAnnot;
+    AlignmentI alignment = alignViewport.getAlignment();
+    if (our != null)
+    {
+      if (our.size() > 0)
+      {
+        for (AlignmentAnnotation an : our)
+        {
+          if (!ourAnnots.contains(an))
+          {
+            // remove the old annotation
+            alignment.deleteAnnotation(an);
+          }
+        }
+      }
+      our.clear();
+    }
+
+    // validate rows and update Alignmment state
+    for (AlignmentAnnotation an : ourAnnots)
+    {
+      alignViewport.getAlignment().validateAnnotation(an);
+    }
+    // TODO: may need a menu refresh after this
+    // af.setMenusForViewport();
+    ap.adjustAnnotationHeight();
+
+  }
+
+  public SequenceAnnotationServiceI getService()
+  {
+    return annotService;
+  }
+
+}
index a30a09c..29343f2 100644 (file)
  */
 package jalview.ws.jws2;
 
-import java.util.Locale;
-
 import jalview.api.AlignCalcWorkerI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.JvSwingUtils;
 import jalview.util.MessageManager;
-import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.params.AutoCalcSetting;
 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 java.util.Locale;
+
 
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
@@ -53,7 +53,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
     // TODO Auto-generated constructor stub
   }
 
-  public SequenceAnnotationWSClient(final Jws2Instance sh,
+  public SequenceAnnotationWSClient(final ServiceWithParameters sh,
           AlignFrame alignFrame, WsParamSetI preset, boolean editParams)
   {
     super(alignFrame, preset, null);
@@ -63,8 +63,8 @@ public class SequenceAnnotationWSClient extends Jws2Client
   // dan think. Do I need to change this method to run RNAalifold through the
   // GUI
 
-  public void initSequenceAnnotationWSClient(final Jws2Instance sh,
-          AlignFrame alignFrame, WsParamSetI preset, boolean editParams)
+  private void initSequenceAnnotationWSClient(final ServiceWithParameters sh,
+      AlignFrame alignFrame, final WsParamSetI preset, boolean editParams)
   {
     // dan changed! dan test. comment out if conditional
     // if (alignFrame.getViewport().getAlignment().isNucleotide())
@@ -84,70 +84,100 @@ public class SequenceAnnotationWSClient extends Jws2Client
       // columns
 
       List<AlignCalcWorkerI> clnts = alignFrame.getViewport()
-              .getCalcManager().getRegisteredWorkersOfClass(clientClass);
-      AbstractJabaCalcWorker worker;
-      if (clnts == null || clnts.size() == 0)
+          .getCalcManager()
+          .getWorkersOfClass(SeqAnnotationServiceCalcWorker.class);
+
+      SeqAnnotationServiceCalcWorker tmpworker = null;
+      if (clnts != null)
       {
-        if (!processParams(sh, editParams))
+        for (AlignCalcWorkerI _worker : clnts)
         {
-          return;
+          tmpworker = (SeqAnnotationServiceCalcWorker) _worker;
+          if (tmpworker.hasService()
+              && tmpworker.getService().getClass().equals(clientClass))
+          {
+            break;
+          }
+          tmpworker = null;
         }
-        try
-        {
-          worker = (AbstractJabaCalcWorker) (clientClass
-                  .getConstructor(new Class[]
-                  { Jws2Instance.class, AlignFrame.class, WsParamSetI.class,
-                      List.class })
-                  .newInstance(new Object[]
-                  { sh, alignFrame, this.preset, paramset }));
-        } catch (Exception x)
-        {
-          x.printStackTrace();
-          throw new Error(
+      }
+      final var worker = tmpworker;
+      if (worker == null)
+      {
+        processParams(sh, editParams).thenAccept((startJob) -> {
+          if (startJob)
+          {
+            final SeqAnnotationServiceCalcWorker worker_;
+            try
+            {
+              worker_ = new SeqAnnotationServiceCalcWorker(sh, alignFrame, this.preset,
+                  paramset);
+            } catch (Exception x)
+            {
+              x.printStackTrace();
+              throw new Error(
                   MessageManager.getString("error.implementation_error"),
                   x);
-        }
-        alignFrame.getViewport().getCalcManager().registerWorker(worker);
-        alignFrame.getViewport().getCalcManager().startWorker(worker);
+            }
+            alignFrame.getViewport().getCalcManager().registerWorker(worker_);
+                // also starts the worker
+            startSeqAnnotationWorker(sh, alignFrame, preset, editParams);
+          }
+        });
 
       }
       else
       {
-        worker = (AbstractJabaCalcWorker) clnts.get(0);
+        WsParamSetI preset_;
         if (editParams)
         {
           paramset = worker.getArguments();
-          preset = worker.getPreset();
+          preset_ = worker.getPreset();
         }
-
-        if (!processParams(sh, editParams, true))
+        else
         {
-          return;
+          preset_ = preset;
         }
-        // reinstate worker if it was blacklisted (might have happened due to
-        // invalid parameters)
-        alignFrame.getViewport().getCalcManager().enableWorker(worker);
-        worker.updateParameters(this.preset, paramset);
+        processParams(sh, editParams, true).thenAccept((startJob) -> {
+          if (startJob)
+          {
+            // reinstate worker if it was blacklisted (might have happened due
+            // to
+            // invalid parameters)
+            alignFrame.getViewport().getCalcManager().enableWorker(worker);
+            worker.updateParameters(this.preset, paramset);
+            startSeqAnnotationWorker(sh, alignFrame, preset_, editParams);
+          }
+        });
       }
     }
-    if (sh.action.toLowerCase(Locale.ROOT).contains("disorder"))
+    else
+    {
+      startSeqAnnotationWorker(sh, alignFrame, preset, editParams);
+    }
+  }
+
+  private void startSeqAnnotationWorker(ServiceWithParameters sh,
+      AlignFrame alignFrame, WsParamSetI preset, boolean editParams)
+  {
+    if (!sh.isInteractiveUpdate())
     {
       // build IUPred style client. take sequences, returns annotation per
       // sequence.
-      if (!processParams(sh, editParams))
-      {
-        return;
-      }
-
-      alignFrame.getViewport().getCalcManager().startWorker(
-              new AADisorderClient(sh, alignFrame, preset, paramset));
+      processParams(sh, editParams).thenAccept((startJob) -> {
+        if (startJob)
+        {
+          alignFrame.getViewport().getCalcManager().startWorker(
+              new SeqAnnotationServiceCalcWorker(sh, alignFrame, preset, paramset));
+        }
+      });
     }
   }
 
-  public SequenceAnnotationWSClient(AAConSettings fave,
+  public SequenceAnnotationWSClient(AutoCalcSetting fave,
           AlignFrame alignFrame, boolean b)
   {
-    super(alignFrame, fave.getPreset(), fave.getJobArgset());
+    super(alignFrame, fave.getPreset(), fave.getArgumentSet());
     initSequenceAnnotationWSClient(fave.getService(), alignFrame,
             fave.getPreset(), b);
   }
@@ -158,18 +188,24 @@ public class SequenceAnnotationWSClient extends Jws2Client
    * @see jalview.ws.jws2.Jws2Client#attachWSMenuEntry(javax.swing.JMenu,
    * jalview.ws.jws2.jabaws2.Jws2Instance, jalview.gui.AlignFrame)
    */
-  public void attachWSMenuEntry(JMenu wsmenu, final Jws2Instance service,
+  @Override
+  public void attachWSMenuEntry(JMenu wsmenu,
+          final ServiceWithParameters service,
           final AlignFrame alignFrame)
   {
-    if (registerAAConWSInstance(wsmenu, service, alignFrame))
+    if (Jws2ClientFactory.registerAAConWSInstance(wsmenu,
+            service, alignFrame))
     {
       // Alignment dependent analysis calculation WS gui
       return;
     }
     boolean hasparams = service.hasParameters();
-    // Assume name ends in WS
-    String calcName = service.serviceType.substring(0,
-            service.serviceType.length() - 2);
+    String calcName = service.getName();
+    if (calcName.endsWith("WS"))
+    {
+      // Remove "WS" suffix
+      calcName = calcName.substring(0, calcName.length() - 2);
+    }
 
     JMenuItem annotservice = new JMenuItem(MessageManager.formatMessage(
             "label.calcname_with_default_settings", new String[]
@@ -180,7 +216,8 @@ public class SequenceAnnotationWSClient extends Jws2Client
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        new SequenceAnnotationWSClient(service, alignFrame, null, false);
+        new SequenceAnnotationWSClient(service, alignFrame,
+                null, false);
       }
     });
     wsmenu.add(annotservice);
@@ -195,9 +232,11 @@ public class SequenceAnnotationWSClient extends Jws2Client
 
       annotservice.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
-          new SequenceAnnotationWSClient(service, alignFrame, null, true);
+          new SequenceAnnotationWSClient(service, alignFrame,
+                  null, true);
         }
       });
       wsmenu.add(annotservice);
@@ -219,9 +258,11 @@ public class SequenceAnnotationWSClient extends Jws2Client
                   + "</strong><br/>" + preset.getDescription()));
           methodR.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
-              new SequenceAnnotationWSClient(service, alignFrame, preset,
+              new SequenceAnnotationWSClient(service,
+                      alignFrame, preset,
                       false);
             }
 
@@ -236,7 +277,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
     {
       annotservice = new JMenuItem(
               MessageManager.getString("label.view_documentation"));
-      if (service.docUrl != null)
+      if (service != null && service.hasDocumentationUrl())
       {
         annotservice.addActionListener(new ActionListener()
         {
@@ -244,13 +285,14 @@ public class SequenceAnnotationWSClient extends Jws2Client
           @Override
           public void actionPerformed(ActionEvent arg0)
           {
-            Desktop.instance.showUrl(service.docUrl);
+            Desktop.getInstance().showUrl(service.getDocumentationUrl());
           }
         });
         annotservice.setToolTipText(
                 JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage(
                         "label.view_service_doc_url", new String[]
-                        { service.docUrl, service.docUrl })));
+                        { service.getDocumentationUrl(),
+                            service.getDocumentationUrl() })));
         wsmenu.add(annotservice);
       }
     }
index 708787e..fa56874 100644 (file)
  */
 package jalview.ws.jws2.dm;
 
-import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws2.JabaParamStore;
-import jalview.ws.jws2.JabaPreset;
 import jalview.ws.jws2.ParameterUtils;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import compbio.metadata.Argument;
 import compbio.metadata.Option;
 
 /**
@@ -42,76 +38,13 @@ import compbio.metadata.Option;
  */
 public class AAConSettings extends jalview.ws.params.AutoCalcSetting
 {
-  Jws2Instance service;
-
-  public AAConSettings(boolean autoUpdate, Jws2Instance service,
+  public AAConSettings(boolean autoUpdate, ServiceWithParameters service2,
           WsParamSetI preset, List<ArgumentI> jobArgset)
   {
-    super(preset, jobArgset, autoUpdate);
-    this.service = service;
-  }
-
-  public Jws2Instance getService()
-  {
-    return service;
-  }
-
-  public void setService(Jws2Instance service)
-  {
-    this.service = service;
-    if (preset != null)
-    {
-      // migrate preset to new service
-      for (String url : preset.getApplicableUrls())
-      {
-        if (url.equals(service.getUri()))
-        {
-          return;
-        }
-      }
-      WsParamSetI pr = service.getParamStore().getPreset(preset.getName());
-      if (pr instanceof JabaPreset && preset instanceof JabaPreset)
-      {
-        // easy - Presets are identical (we assume)
-        preset = pr;
-        return;
-      }
-      List<ArgumentI> oldargs = new ArrayList<ArgumentI>(),
-              newargs = new ArrayList<ArgumentI>();
-      oldargs.addAll(preset.getArguments());
-      // need to compare parameters
-      for (ArgumentI newparg : pr.getArguments())
-      {
-        if (!oldargs.remove(newparg))
-        {
-          newargs.add(newparg);
-        }
-      }
-      if (oldargs.size() == 0 && newargs.size() == 0)
-      {
-        // exact match.
-        preset = pr;
-        return;
-      }
-      // Try even harder to migrate arguments.
-      throw new Error(MessageManager
-              .getString("error.parameter_migration_not_implemented_yet"));
-    }
-  }
-
-  public List<Argument> getJobArgset()
-  {
-    return jobArgset == null ? null
-            : JabaParamStore.getJabafromJwsArgs(jobArgset);
-  }
-
-  public void setJobArgset(List<Argument> jobArgset)
-  {
-    // TODO: test if parameters valid for service
-    this.jobArgset = jobArgset == null ? null
-            : JabaParamStore.getJwsArgsfromJaba(jobArgset);
+    super(service2, preset, jobArgset, autoUpdate);
   }
 
+  @Override
   public String getWsParamFile()
   {
     List<Option> opts = null;
@@ -138,16 +71,4 @@ public class AAConSettings extends jalview.ws.params.AutoCalcSetting
     }
     return pset.toString();
   }
-
-  @Override
-  public String getServiceURI()
-  {
-    return service.getServiceTypeURI();
-  }
-
-  @Override
-  public String[] getServiceURLs()
-  {
-    return new String[] { service.getUri() };
-  }
 }
index 5a3fb35..ef6969c 100644 (file)
@@ -116,4 +116,10 @@ public class JabaOption implements jalview.ws.params.OptionI
     return opt;
   }
 
+  @Override
+  public List<String> getDisplayNames()
+  {
+    return null; // not supported for Jaba options
+  }
+
 }
diff --git a/src/jalview/ws/jws2/jabaws2/AAConClient.java b/src/jalview/ws/jws2/jabaws2/AAConClient.java
new file mode 100644 (file)
index 0000000..18b6ce1
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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.jws2.jabaws2;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.util.MessageManager;
+import jalview.ws.uimodel.AlignAnalysisUIText;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import compbio.data.sequence.Score;
+
+public class AAConClient extends JabawsAnnotationInstance
+{
+  // configuration for factory
+  public static String getServiceActionText()
+  {
+    return "calculating Amino acid consensus using AACon service";
+  }
+
+
+
+  private static String CALC_ID = "jabaws2.AACon";
+
+  public static AlignAnalysisUIText getAlignAnalysisUIText()
+  {
+    return new AlignAnalysisUIText(
+            compbio.ws.client.Services.AAConWS.toString(),
+            AAConClient.class, CALC_ID, false, true, true, true,
+            true, 2, MessageManager.getString("label.aacon_calculations"),
+            MessageManager.getString("tooltip.aacon_calculations"),
+            MessageManager.getString("label.aacon_settings"),
+            MessageManager.getString("tooltip.aacon_settings"));
+  }
+
+  // instance
+  public AAConClient(Jws2Instance handle)
+  {
+    super(handle);
+  }
+
+  @Override
+  List<AlignmentAnnotation> annotationFromScoreManager(AlignmentI seqs,
+          Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters)
+  {
+    return aacons_annotation(seqs.getWidth(), seqs, null);
+  }
+
+  private List<AlignmentAnnotation> aacons_annotation(int alWidth,
+          AlignmentI alignViewport, boolean[] gapMap)
+  {
+    Map<String, TreeSet<Score>> scoremap = scoremanager.asMap();
+    ArrayList<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+    for (String score : scoremap.keySet())
+    {
+      Set<Score> scores = scoremap.get(score);
+      for (Score scr : scores)
+      {
+        if (scr.getRanges() != null && scr.getRanges().size() > 0)
+        {
+          /**
+           * annotation in range annotation = findOrCreate(scr.getMethod(),
+           * true, null, null); Annotation[] elm = new Annotation[alWidth];
+           * Iterator<Float> vals = scr.getScores().iterator(); for (Range rng :
+           * scr.getRanges()) { float val = vals.next().floatValue(); for (int i
+           * = rng.from; i <= rng.to; i++) { elm[i] = new Annotation("", "", '
+           * ', val); } } annotation.annotations = elm;
+           * annotation.validateRangeAndDisplay();
+           */
+        }
+        else
+        {
+          createAnnotationRowsForScores(alignViewport, null, ourAnnot,
+                  getCalcId(),
+                  scr.getScores().size(), scr);
+        }
+      }
+    }
+    return ourAnnot;
+  }
+
+}
diff --git a/src/jalview/ws/jws2/jabaws2/AADisorderClient.java b/src/jalview/ws/jws2/jabaws2/AADisorderClient.java
new file mode 100644 (file)
index 0000000..69cd008
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * 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.jws2.jabaws2;
+
+import jalview.analysis.AlignmentAnnotationUtils;
+import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.sequence.Range;
+import compbio.data.sequence.Score;
+import compbio.data.sequence.ScoreManager.ScoreHolder;
+
+public class AADisorderClient extends JabawsAnnotationInstance
+{
+  // static configuration
+  public static String getServiceActionText()
+  {
+    return "Submitting amino acid sequences for disorder prediction.";
+  }
+
+  // minSeq = 1; protein only, no gaps
+
+  // instance
+  public AADisorderClient(Jws2Instance handle)
+  {
+    super(handle);
+
+  }
+
+  @Override
+  List<AlignmentAnnotation> annotationFromScoreManager(AlignmentI seqs,
+          Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters)
+  {
+
+    Map<String, String[]> featureTypeMap = featureMap.get(our.getName());
+    Map<String, Map<String, Object>> annotTypeMap = annotMap
+            .get(our.getName());
+    boolean dispFeatures = false;
+    Map<String, SequenceFeature> fc = new Hashtable<>(),
+            fex = new Hashtable();
+    List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+    int graphGroup = 1, lastAnnot = 0;
+
+    for (SequenceI seq : seqs.getSequences())
+    {
+      String seqId = seq.getName();
+      boolean sameGroup = false;
+      SequenceI dseq, aseq;
+      int base = seq.findPosition(0) - 1;
+      aseq = seq;
+      while ((dseq = seq).getDatasetSequence() != null)
+      {
+        seq = seq.getDatasetSequence();
+      }
+      ScoreHolder scores = null;
+      try
+      {
+        scores = scoremanager.getAnnotationForSequence(seqId);
+      } catch (Exception q)
+      {
+        Console.info("Couldn't recover disorder prediction for sequence "
+                + seq.getName() + "(Prediction name was " + seqId + ")"
+                + "\nSee http://issues.jalview.org/browse/JAL-1319 for one possible reason why disorder predictions might fail.",
+                q);
+      }
+      float last = Float.NaN, val = Float.NaN;
+      if (scores != null && scores.scores != null)
+      {
+        for (Score scr : scores.scores)
+        {
+
+          if (scr.getRanges() != null && scr.getRanges().size() > 0)
+          {
+            Iterator<Float> vals = scr.getScores().iterator();
+            // make features on sequence
+            for (Range rn : scr.getRanges())
+            {
+              // TODO: Create virtual feature settings
+              SequenceFeature sf;
+              String[] type = featureTypeMap.get(scr.getMethod());
+              if (type == null)
+              {
+                // create a default type for this feature
+                type = new String[] {
+                    typeName + " (" + scr.getMethod() + ")",
+                    our.getActionText() };
+              }
+              if (vals.hasNext())
+              {
+                val = vals.next().floatValue();
+                sf = new SequenceFeature(type[0], type[1], base + rn.from,
+                        base + rn.to, val, methodName);
+              }
+              else
+              {
+                sf = new SequenceFeature(type[0], type[1], base + rn.from,
+                        base + rn.to, methodName);
+              }
+              dseq.addSequenceFeature(sf);
+              // mark feature as requiring a graduated colourscheme if has
+              // variable scores
+              if (!Float.isNaN(last) && !Float.isNaN(val) && last != val)
+              {
+                fc.put(sf.getType(), sf);
+              } else 
+              {
+                fex.put(sf.getType(), sf);
+              }
+              last = val;
+              dispFeatures = true;
+            }
+          }
+          else
+          {
+            if (scr.getScores().size() == 0)
+            {
+              continue;
+            }
+            String typename, calcName;
+            AlignmentAnnotation annot = createAnnotationRowsForScores(
+                    seqs, null, ourAnnot,
+                    typename = our.getName() + " (" + scr.getMethod() + ")",
+                    calcName = our.getNameURI() + "/" + scr.getMethod(),
+                    aseq, base + 1, scr);
+            annot.graph = AlignmentAnnotation.LINE_GRAPH;
+
+            Map<String, Object> styleMap = (annotTypeMap == null) ? null
+                    : annotTypeMap.get(scr.getMethod());
+
+            annot.visible = (styleMap == null
+                    || styleMap.get(INVISIBLE) == null);
+            double[] thrsh = (styleMap == null) ? null
+                    : (double[]) styleMap.get(THRESHOLD);
+            float[] range = (styleMap == null) ? null
+                    : (float[]) styleMap.get(RANGE);
+            if (range != null)
+            {
+              annot.graphMin = range[0];
+              annot.graphMax = range[1];
+            }
+            if (styleMap == null || styleMap.get(DONTCOMBINE) == null)
+            {
+              {
+                if (!sameGroup)
+                {
+                  graphGroup++;
+                  sameGroup = true;
+                }
+
+                annot.graphGroup = graphGroup;
+              }
+            }
+
+            annot.description = "<html>" + our.getActionText()
+                    + " - raw scores";
+            if (thrsh != null)
+            {
+              String threshNote = (thrsh[0] > 0 ? "Above " : "Below ")
+                      + thrsh[1] + " indicates disorder";
+              annot.threshold = new GraphLine((float) thrsh[1], threshNote,
+                      Color.red);
+              annot.description += "<br/>" + threshNote;
+            }
+            annot.description += "</html>";
+            Color col = ColorUtils
+                    .createColourFromName(typeName + scr.getMethod());
+            for (int p = 0, ps = annot.annotations.length; p < ps; p++)
+            {
+              if (annot.annotations[p] != null)
+              {
+                annot.annotations[p].colour = col;
+              }
+            }
+            annot._linecolour = col;
+            // finally, update any dataset annotation
+            AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith(annot,
+                    typename, calcName);
+          }
+        }
+      }
+      if (lastAnnot + 1 == ourAnnot.size())
+      {
+        // remove singleton alignment annotation row
+        ourAnnot.get(lastAnnot).graphGroup = -1;
+      }
+    }
+    {
+      if (dispFeatures)
+      {
+        // TODO: virtual feature settings
+        // feature colours need to merged with current viewport's colours
+        // simple feature colours promoted to colour-by-score ranges using
+        // currently assigned or created feature colour
+        for (String ft : fex.keySet())
+        {
+          Color col = ColorUtils.createColourFromName(ft);
+          // set graduated color as fading to white for minimum, and
+          // autoscaling to values on alignment
+          
+          FeatureColourI ggc;
+          if (fc.get(ft) != null)
+          {
+            ggc = new FeatureColour(col, Color.white, col,
+
+                  Color.white, Float.MIN_VALUE, Float.MAX_VALUE);
+            ggc.setAutoScaled(true);
+          }
+          else
+          {
+            ggc = new FeatureColour(col);
+          }
+          featureColours.put(ft, ggc);
+        }
+
+      }
+      return ourAnnot;
+    }
+  }
+
+  private static final String THRESHOLD = "THRESHOLD";
+
+  private static final String RANGE = "RANGE";
+
+  String typeName;
+
+  String methodName;
+
+  String groupName;
+
+  private static Map<String, Map<String, String[]>> featureMap;
+
+  private static Map<String, Map<String, Map<String, Object>>> annotMap;
+
+  private static String DONTCOMBINE = "DONTCOMBINE";
+
+  private static String INVISIBLE = "INVISIBLE";
+  static
+  {
+    // TODO: turn this into some kind of configuration file that's a bit easier
+    // to edit
+    featureMap = new HashMap<>();
+    Map<String, String[]> fmap;
+    featureMap.put(compbio.ws.client.Services.IUPredWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("Glob",
+            new String[]
+            { "Globular Domain", "Predicted globular domain" });
+    featureMap.put(compbio.ws.client.Services.JronnWS.toString(),
+            fmap = new HashMap<>());
+    featureMap.put(compbio.ws.client.Services.DisemblWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("REM465", new String[] { "REM465", "Missing density" });
+    fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" });
+    fmap.put("COILS", new String[] { "COILS", "Random coil" });
+    featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("GlobDoms",
+            new String[]
+            { "Globular Domain", "Predicted globular domain" });
+    fmap.put("Disorder",
+            new String[]
+            { "Protein Disorder", "Probable unstructured peptide region" });
+    Map<String, Map<String, Object>> amap;
+    annotMap = new HashMap<>();
+    annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
+            amap = new HashMap<>());
+    amap.put("Dydx", new HashMap<String, Object>());
+    amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE);
+    amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 });
+    amap.get("Dydx").put(RANGE, new float[] { -1, +1 });
+
+    amap.put("SmoothedScore", new HashMap<String, Object>());
+    amap.get("SmoothedScore").put(INVISIBLE, INVISIBLE);
+    amap.put("RawScore", new HashMap<String, Object>());
+    amap.get("RawScore").put(INVISIBLE, INVISIBLE);
+    annotMap.put(compbio.ws.client.Services.DisemblWS.toString(),
+            amap = new HashMap<>());
+    amap.put("COILS", new HashMap<String, Object>());
+    amap.put("HOTLOOPS", new HashMap<String, Object>());
+    amap.put("REM465", new HashMap<String, Object>());
+    amap.get("COILS").put(THRESHOLD, new double[] { 1, 0.516 });
+    amap.get("COILS").put(RANGE, new float[] { 0, 1 });
+
+    amap.get("HOTLOOPS").put(THRESHOLD, new double[] { 1, 0.6 });
+    amap.get("HOTLOOPS").put(RANGE, new float[] { 0, 1 });
+    amap.get("REM465").put(THRESHOLD, new double[] { 1, 0.1204 });
+    amap.get("REM465").put(RANGE, new float[] { 0, 1 });
+
+    annotMap.put(compbio.ws.client.Services.IUPredWS.toString(),
+            amap = new HashMap<>());
+    amap.put("Long", new HashMap<String, Object>());
+    amap.put("Short", new HashMap<String, Object>());
+    amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("Long").put(RANGE, new float[] { 0, 1 });
+    amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("Short").put(RANGE, new float[] { 0, 1 });
+    annotMap.put(compbio.ws.client.Services.JronnWS.toString(),
+            amap = new HashMap<>());
+    amap.put("JRonn", new HashMap<String, Object>());
+    amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("JRonn").put(RANGE, new float[] { 0, 1 });
+  }
+
+}
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsAnnotationInstance.java b/src/jalview/ws/jws2/jabaws2/JabawsAnnotationInstance.java
new file mode 100644 (file)
index 0000000..a52b515
--- /dev/null
@@ -0,0 +1,279 @@
+package jalview.ws.jws2.jabaws2;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.util.MessageManager;
+import jalview.ws.api.JobId;
+import jalview.ws.api.SequenceAnnotationServiceI;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.msa.SequenceAnnotation;
+import compbio.data.sequence.FastaSequence;
+import compbio.data.sequence.Score;
+import compbio.data.sequence.ScoreManager;
+import compbio.metadata.JobSubmissionException;
+import compbio.metadata.WrongParameterException;
+
+public abstract class JabawsAnnotationInstance
+        extends JabawsServiceInstance<SequenceAnnotation>
+        implements SequenceAnnotationServiceI
+{
+
+  /**
+   * holds last results obtained when non-null. TODO: remove this as a field ?
+   */
+  protected ScoreManager scoremanager = null;
+
+  public JabawsAnnotationInstance(Jws2Instance handle)
+  {
+    super(handle);
+  }
+
+
+  /**
+   * 
+   * @return the calcId for this Jabaws Service (convenience method).
+   * 
+   *         TODO: decide if this is really convenient since manager and
+   *         instance have same method !
+   */
+  public String getCalcId()
+  {
+    return our.getAlignAnalysisUI() == null ? null
+            : our.getAlignAnalysisUI().getCalcId();
+  }
+
+  @Override
+  public JobId submitToService(List<SequenceI> seqs, WsParamSetI preset,
+          List<ArgumentI> arguments) throws Throwable
+  {
+    String rslt = null;
+    scoremanager = null;
+    List<FastaSequence> jabaseqs = new ArrayList(seqs.size());
+    for (SequenceI seq : seqs)
+    {
+      jabaseqs.add(
+              new FastaSequence(seq.getName(), seq.getSequenceAsString()));
+    }
+    if (preset == null && arguments == null)
+    {
+      rslt = service.analize(jabaseqs);
+    }
+    if (preset != null)
+    {
+      if (preset instanceof JabaPreset)
+      {
+        // TODO: verify behaviour is really the same, since preset analyze was
+        // never called in Jalview 2.11.x
+        rslt = service.presetAnalize(jabaseqs,
+                ((JabaPreset) preset).getJabaPreset());
+      }
+      else
+      {
+        rslt = service.customAnalize(jabaseqs,
+                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
+      }
+    }
+    else if (arguments != null && arguments.size() > 0)
+    {
+      try
+      {
+        rslt = service.customAnalize(jabaseqs,
+                JabaParamStore.getJabafromJwsArgs(arguments));
+      } catch (WrongParameterException x)
+      {
+        throw new JobSubmissionException(MessageManager.getString(
+                "exception.jobsubmission_invalid_params_set"), x);
+
+      }
+    }
+
+    if (rslt == null)
+    {
+      return null;
+    }
+    return new JobId(our.getServiceType(), our.getName(), rslt);
+  }
+
+
+  @Override
+  public
+  List<AlignmentAnnotation> getAnnotationResult(JobId job,
+          List<SequenceI> seqs, Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters) throws Throwable
+  {
+    if (scoremanager == null)
+    {
+      // TODO: raise annotation unavailable exception ?
+      scoremanager = service.getAnnotation(job.getJobId());
+    }
+    if (scoremanager == null)
+    {
+      return List.of();
+    }
+    /**
+     * dummy alignment to perform annotation on
+     */
+    AlignmentI newal = new Alignment(seqs.toArray(new SequenceI[0]));
+    List<AlignmentAnnotation> ourAnnot = annotationFromScoreManager(newal,
+            featureColours, featureFilters);
+    return ourAnnot;
+  }
+
+  /**
+   * service specific annotation creation method
+   * 
+   * @param seqs
+   *          - sequences to be annotated with results
+   * @param featureColours
+   *          - - updated with any colours imported during result processing
+   * @param featureFilters
+   *          - updated with any filters imported during result processing
+   * 
+   * @return
+   */
+  abstract List<AlignmentAnnotation> annotationFromScoreManager(
+          AlignmentI seqs, Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters);
+
+
+  /**
+   * create and complete an annotation row from a JABAWS score object
+   * 
+   * @param alignViewport
+   * @param gapMap
+   * @param ourAnnot
+   * @param calcId
+   * @param alWidth
+   * @param scr
+   */
+
+  protected void createAnnotationRowsForScores(AlignmentI al_result,
+          boolean[] gapMap, List<AlignmentAnnotation> ourAnnot,
+          String calcId,
+          int alWidth, Score scr)
+  {
+    // simple annotation row
+    AlignmentAnnotation annotation = al_result
+            .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
+                    null);
+    if (gapMap == null || alWidth == gapMap.length) // scr.getScores().size())
+    {
+      constructAnnotationFromScore(gapMap, annotation, 0,
+              alWidth, scr);
+      ourAnnot.add(annotation);
+    }
+  }
+
+  /**
+   * create a sequence associated annotation row for JABAWS score object scr
+   * 
+   * @param alignViewport
+   * @param gapMap
+   * @param ourAnnot
+   * @param typeName
+   * @param calcId
+   * @param dseq
+   * @param base
+   * @param scr
+   * @return
+   */
+  protected AlignmentAnnotation createAnnotationRowsForScores(
+          AlignmentI alignment, boolean[] gapMap,
+          List<AlignmentAnnotation> ourAnnot, String typeName,
+          String calcId, SequenceI dseq, int base, Score scr)
+  {
+    System.out.println("Creating annotation on dseq:" + dseq.getStart()
+            + " base is " + base + " and length=" + dseq.getLength()
+            + " == " + scr.getScores().size());
+    // AlignmentAnnotation annotation = new AlignmentAnnotation(
+    // scr.getMethod(), typeName, new Annotation[]
+    // {}, 0, -1, AlignmentAnnotation.LINE_GRAPH);
+    // annotation.setCalcId(calcId);
+    AlignmentAnnotation annotation = alignment
+            .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
+    constructAnnotationFromScore(gapMap, annotation, 0, dseq.getLength(),
+            scr);
+    annotation.createSequenceMapping(dseq, base, false);
+    annotation.adjustForAlignment();
+    dseq.addAlignmentAnnotation(annotation);
+    ourAnnot.add(annotation);
+    return annotation;
+  }
+
+  /**
+   * create column annotation elements from Jabaws score object
+   * 
+   * @param gapMap
+   * @param annotation
+   * @param base
+   * @param alWidth
+   * @param scr
+   *          JABAWS score object
+   */
+  protected void constructAnnotationFromScore(boolean[] gapMap,
+          AlignmentAnnotation annotation,
+          int base, int alWidth, Score scr)
+  {
+    Annotation[] elm = new Annotation[alWidth];
+    Iterator<Float> vals = scr.getScores().iterator();
+    float m = 0f, x = 0f;
+    for (int i = 0; vals.hasNext(); i++)
+    {
+      float val = vals.next().floatValue();
+      if (i == 0)
+      {
+        m = val;
+        x = val;
+      }
+      else
+      {
+        if (m > val)
+        {
+          m = val;
+        }
+        ;
+        if (x < val)
+        {
+          x = val;
+        }
+      }
+      // if we're at a gapped column then skip to next ungapped position
+      if (gapMap != null && gapMap.length > 0)
+      {
+        // if gapMap is a different length to the result then it may be out of
+        // sync with the job.
+        while (i < gapMap.length && !gapMap[i])
+        {
+          elm[i++] = new Annotation("", "", ' ', Float.NaN);
+        }
+      }
+      elm[i] = new Annotation("", "" + val, ' ', val);
+    }
+
+    annotation.annotations = elm;
+    annotation.belowAlignment = true;
+    if (x < 0)
+    {
+      x = 0;
+    }
+    x += (x - m) * 0.1;
+    annotation.graphMax = x;
+    annotation.graphMin = m;
+    annotation.validateRangeAndDisplay();
+  }
+
+}
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java b/src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java
new file mode 100644 (file)
index 0000000..cbb4b05
--- /dev/null
@@ -0,0 +1,105 @@
+package jalview.ws.jws2.jabaws2;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.WsParamSetI;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+import java.util.ArrayList;
+import java.util.List;
+
+import compbio.data.sequence.Alignment;
+import compbio.data.sequence.FastaSequence;
+import compbio.metadata.ResultNotAvailableException;
+
+public class JabawsMsaInstance
+        extends JabawsServiceInstance<compbio.data.msa.MsaWS>
+        implements MultipleSequenceAlignmentI, CancellableI
+{
+  @Override
+  public JobId align(List<SequenceI> toalign, WsParamSetI parameters,
+          List<ArgumentI> arguments) throws Throwable
+  {
+    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
+    for (SequenceI seq : toalign)
+    {
+      seqs.add(new FastaSequence(seq.getName(), seq.getSequenceAsString()));
+    }
+    String jobid = null;
+    if (parameters != null)
+    {
+      if (parameters instanceof JabaPreset)
+      {
+        jobid = service.presetAlign(seqs,
+                ((JabaPreset) parameters).getJabaPreset());
+      }
+      else
+      {
+        jobid = service.customAlign(seqs, JabaParamStore
+                .getJabafromJwsArgs(parameters.getArguments()));
+      }
+    }
+    else if (arguments != null && arguments.size() > 0)
+    {
+      jobid = service.customAlign(seqs,
+              JabaParamStore.getJabafromJwsArgs(arguments));
+    }
+    else
+    {
+      jobid = service.align(seqs);
+    }
+
+    if (jobid == null)
+    {
+      return null;
+    }
+    return new JobId(our.getServiceType(), our.getName(), jobid);
+  }
+
+  @Override
+  public AlignmentI getAlignmentFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError
+  {
+    Alignment alignment = null;
+    try
+    {
+      alignment = service.getResult(jobId.getJobId());
+    } catch (ResultNotAvailableException rnotav)
+    {
+
+      // TODO - migrate JABA exception
+      // throw new ServerError("Couldn't get result for job",rnotav);
+    }
+    SequenceI[] alseqs;
+    int alseq_l = 0;
+    if (alignment.getSequences().size() == 0)
+    {
+      return null;
+    }
+
+    alseqs = new SequenceI[alignment.getSequences().size()];
+    for (compbio.data.sequence.FastaSequence seq : alignment.getSequences())
+    {
+      alseqs[alseq_l++] = new Sequence(seq.getId(), seq.getSequence());
+    }
+    AlignmentI jv_al = new jalview.datamodel.Alignment(alseqs);
+    jv_al.setGapCharacter(alignment.getMetadata().getGapchar());
+    return jv_al;
+
+  }
+
+  public JabawsMsaInstance(Jws2Instance handle)
+  {
+    super(handle);
+  }
+
+}
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsMsaInterfaceAlignCalcWorker.java b/src/jalview/ws/jws2/jabaws2/JabawsMsaInterfaceAlignCalcWorker.java
new file mode 100644 (file)
index 0000000..edcc05c
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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.jws2.jabaws2;
+
+import jalview.gui.AlignFrame;
+import jalview.ws.jws2.SeqAnnotationServiceCalcWorker;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+
+public abstract class JabawsMsaInterfaceAlignCalcWorker
+        extends SeqAnnotationServiceCalcWorker
+{
+
+  public JabawsMsaInterfaceAlignCalcWorker(Jws2Instance service,
+          AlignFrame alignFrame, WsParamSetI preset,
+          List<ArgumentI> paramset)
+  {
+    super(service, alignFrame, preset, paramset);
+    // TODO Auto-generated constructor stub
+  }
+
+  // TODO: REFACTOR if needed !
+  // may be able to get away with overriding run() only, but maybe not.
+  /***
+   * @SuppressWarnings("unchecked") protected MsaWS msaservice;
+   * 
+   * protected Alignment msascoreset;
+   * 
+   * public JabawsMsaInterfaceAlignCalcWorker(AlignViewportI alignViewport,
+   * AlignmentViewPanel alignPanel) { super(alignViewport, alignPanel); }
+   * 
+   * public JabawsMsaInterfaceAlignCalcWorker(Jws2Instance service, AlignFrame
+   * alignFrame, WsParamSetI preset, List<ArgumentI> paramset) {
+   * this(alignFrame.getCurrentView(), alignFrame.alignPanel); this.guiProgress
+   * = alignFrame; this.preset = preset; this.arguments = paramset; this.service
+   * = service; msaservice = (MsaWS) service.service;
+   * 
+   * }
+   * 
+   * @Override ChunkHolder pullExecStatistics(String rslt, long rpos) { return
+   *           msaservice.pullExecStatistics(rslt, rpos); }
+   * 
+   * @Override boolean collectAnnotationResultsFor(String rslt) throws
+   *           ResultNotAvailableException { msascoreset =
+   *           msaservice.getResult(rslt); if (msascoreset != null) { return
+   *           true; } return false; }
+   * 
+   * @Override boolean cancelJob(String rslt) throws Exception { return
+   *           msaservice.cancelJob(rslt); }
+   * 
+   * @Override protected JobStatus getJobStatus(String rslt) throws Exception {
+   *           return msaservice.getJobStatus(rslt); }
+   * 
+   * @Override boolean hasService() { return msaservice != null; }
+   * 
+   * @Override protected boolean isInteractiveUpdate() { return false; // this
+   *           instanceof AAConClient; }
+   * 
+   * @Override protected String submitToService(
+   *           List<compbio.data.sequence.FastaSequence> seqs) throws
+   *           JobSubmissionException { String rslt; if (preset == null &&
+   *           arguments == null) { rslt = msaservice.align(seqs); } else { try
+   *           { rslt = msaservice.customAlign(seqs, getJabaArguments()); }
+   *           catch (WrongParameterException x) { throw new
+   *           JobSubmissionException(MessageManager.getString(
+   *           "exception.jobsubmission_invalid_params_set"), x); } } return
+   *           rslt; }
+   * 
+   *           protected void createAnnotationRowsForScores(
+   *           List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
+   *           Score scr) { // simple annotation row AlignmentAnnotation
+   *           annotation = alignViewport.getAlignment()
+   *           .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
+   *           null); if (alWidth == gapMap.length) // scr.getScores().size()) {
+   *           constructAnnotationFromScore(annotation, 0, alWidth, scr);
+   *           ourAnnot.add(annotation); } }
+   * 
+   *           protected AlignmentAnnotation createAnnotationRowsForScores(
+   *           List<AlignmentAnnotation> ourAnnot, String typeName, String
+   *           calcId, SequenceI dseq, int base, Score scr) {
+   *           System.out.println("Creating annotation on dseq:" +
+   *           dseq.getStart() + " base is " + base + " and length=" +
+   *           dseq.getLength() + " == " + scr.getScores().size()); //
+   *           AlignmentAnnotation annotation = new AlignmentAnnotation( //
+   *           scr.getMethod(), typeName, new Annotation[] // {}, 0, -1,
+   *           AlignmentAnnotation.LINE_GRAPH); // annotation.setCalcId(calcId);
+   *           AlignmentAnnotation annotation = alignViewport.getAlignment()
+   *           .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
+   *           constructAnnotationFromScore(annotation, 0, dseq.getLength(),
+   *           scr); annotation.createSequenceMapping(dseq, base, false);
+   *           annotation.adjustForAlignment();
+   *           dseq.addAlignmentAnnotation(annotation);
+   *           ourAnnot.add(annotation); return annotation; }
+   * 
+   *           private void constructAnnotationFromScore(AlignmentAnnotation
+   *           annotation, int base, int alWidth, Score scr) { Annotation[] elm
+   *           = new Annotation[alWidth]; Iterator<Float> vals =
+   *           scr.getScores().iterator(); float m = 0f, x = 0f; for (int i = 0;
+   *           vals.hasNext(); i++) { float val = vals.next().floatValue(); if
+   *           (i == 0) { m = val; x = val; } else { if (m > val) { m = val; } ;
+   *           if (x < val) { x = val; } } // if we're at a gapped column then
+   *           skip to next ungapped position if (gapMap != null &&
+   *           gapMap.length > 0) { while (!gapMap[i]) { elm[i++] = new
+   *           Annotation("", "", ' ', Float.NaN); } } elm[i] = new
+   *           Annotation("", "" + val, ' ', val); }
+   * 
+   *           annotation.annotations = elm; annotation.belowAlignment = true;
+   *           if (x < 0) { x = 0; } x += (x - m) * 0.1; annotation.graphMax =
+   *           x; annotation.graphMin = m; annotation.validateRangeAndDisplay();
+   *           }
+   ***/
+}
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java b/src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java
new file mode 100644 (file)
index 0000000..07f5614
--- /dev/null
@@ -0,0 +1,213 @@
+package jalview.ws.jws2.jabaws2;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.gui.WebserviceInfo;
+import jalview.util.MessageManager;
+import jalview.ws.gui.WsJob;
+import jalview.ws.gui.WsJob.JobState;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import compbio.metadata.Argument;
+import compbio.metadata.ChunkHolder;
+import compbio.metadata.JobStatus;
+import compbio.metadata.Preset;
+
+/**
+ * Base class for JABAWS service instances. Provides helper methods for
+ * interfacing with Jalview.
+ * 
+ * @author jprocter
+ *
+ */
+public class JabawsServiceInstance<T extends compbio.data.msa.JManagement>
+        implements
+        jalview.ws.api.JalviewWebServiceI, jalview.ws.api.CancellableI
+{
+  /**
+   * our service instance handler generated by the discoverer
+   */
+  Jws2Instance our;
+  protected T service;
+  protected Map<JobStatus, JobState> jwsState = new HashMap<>();
+
+  @Override
+  public boolean cancel(WsJob job)
+  {
+    service.cancelJob(job.getJobId());
+    // if the Jaba server indicates the job can't be cancelled, its
+    // because its running on the server's local execution engine
+    // so we just close the window anyway.
+  
+    return true;
+  }
+
+
+  public JabawsServiceInstance(Jws2Instance handle)
+  {
+    our = handle;
+    service = (T) handle.service;
+  }
+
+  @Override
+  public void updateStatus(WsJob job)
+  {
+    JobStatus jwsstatus = service.getJobStatus(job.getJobId());
+    job.setState(jwsState.get(jwsstatus));
+  }
+
+  @Override
+  public boolean updateJobProgress(WsJob job) throws Exception
+  {
+    StringBuilder response = new StringBuilder(job.getStatus());
+    long lastchunk = job.getNextChunk();
+    if (lastchunk == -1)
+    {
+      Console.debug("No more status messages for job " + job.getJobId());
+      return false;
+    }
+    boolean changed = false;
+    do
+    {
+      ChunkHolder chunk = service.pullExecStatistics(job.getJobId(),
+              lastchunk);
+      if (chunk != null)
+      {
+        changed |= chunk.getChunk().length() > 0;
+        response.append(chunk.getChunk());
+        lastchunk = chunk.getNextPosition();
+        try
+        {
+          Thread.sleep(50);
+        } catch (InterruptedException x)
+        {
+        }
+        ;
+      }
+      ;
+      job.setnextChunk(lastchunk);
+    } while (lastchunk >= 0 && job.getNextChunk() != lastchunk);
+    if (job instanceof WsJob)
+    {
+      // TODO decide if WsJob will be the bean for all ng-webservices
+      job.setStatus(response.toString());
+    }
+    return changed;
+  }
+
+  {
+    jwsState.put(JobStatus.CANCELLED, JobState.CANCELLED);
+    jwsState.put(JobStatus.COLLECTED, JobState.FINISHED);
+    jwsState.put(JobStatus.FAILED, JobState.FAILED);
+    jwsState.put(JobStatus.FINISHED, JobState.FINISHED);
+    jwsState.put(JobStatus.PENDING, JobState.QUEUED);
+    jwsState.put(JobStatus.RUNNING, JobState.RUNNING);
+    jwsState.put(JobStatus.STARTED, JobState.RUNNING);
+    jwsState.put(JobStatus.SUBMITTED, JobState.SUBMITTED);
+    jwsState.put(JobStatus.UNDEFINED, JobState.UNKNOWN);
+  }
+
+  public boolean isPresetJob(WsJob job)
+  {
+    return job.getPreset() != null && job.getPreset() instanceof JabaPreset;
+  }
+
+  public Preset getServerPreset(WsJob job)
+  {
+    return (isPresetJob(job))
+            ? ((JabaPreset) job.getPreset()).getJabaPreset()
+            : null;
+  }
+
+  public List<Argument> getJabaArguments(WsParamSetI preset)
+  {
+    List<Argument> newargs = new ArrayList<>();
+    if (preset != null)
+    {
+      if (preset instanceof JabaWsParamSet)
+      {
+        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
+      }
+      else
+      {
+        newargs.addAll(
+                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
+      }
+    }
+    return newargs;
+  }
+
+  @Override
+  public boolean handleSubmitError(Throwable _lex, WsJob j,
+          WebserviceInfo wsInfo) throws Exception, Error
+  {
+    if (_lex instanceof compbio.metadata.UnsupportedRuntimeException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_server_doesnt_support_program",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.service_not_supported"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      wsInfo.setStatus(j.getJobnum(),
+              WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.LimitExceededException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.input_is_too_big"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.WrongParameterException)
+    {
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.invalid_job_param_set"));
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_incorrect_param_setting",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    // pass on to generic error handler
+    return false;
+  }
+
+  @Override
+  public boolean handleCollectionException(Exception ex, WsJob msjob,
+          WebserviceInfo wsInfo)
+  {
+    if (ex instanceof compbio.metadata.ResultNotAvailableException)
+    {
+      // job has failed for some reason - probably due to invalid
+      // parameters
+      Console.debug(
+              "Results not available for finished job - marking as broken job.",
+              ex);
+      String status = msjob.getStatus();
+
+      msjob.setStatus(status
+              + "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
+              + ex.getLocalizedMessage());
+      msjob.setState(WsJob.JobState.BROKEN);
+      return true;
+    }
+    return false;
+  }
+}
index e092192..47fb9c6 100644 (file)
  */
 package jalview.ws.jws2.jabaws2;
 
-import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.util.MessageManager;
+import jalview.ws.api.JalviewServiceEndpointProviderI;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws2.JabaParamStore;
-import jalview.ws.jws2.MsaWSClient;
-import jalview.ws.jws2.SequenceAnnotationWSClient;
 import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.ParamManager;
 
 import java.io.Closeable;
-
-import javax.swing.JMenu;
+import java.net.URL;
 
 import compbio.data.msa.JABAService;
 import compbio.data.msa.MsaWS;
@@ -38,20 +37,12 @@ import compbio.data.msa.SequenceAnnotation;
 import compbio.metadata.PresetManager;
 import compbio.metadata.RunnerConfig;
 
-public class Jws2Instance implements AutoCloseable
+public class Jws2Instance extends ServiceWithParameters
+        implements JalviewServiceEndpointProviderI, AutoCloseable
 {
-  public String hosturl;
-
-  public String serviceType;
-
-  public String action;
 
   public JABAService service;
 
-  public String description;
-
-  public String docUrl;
-
   /**
    * 
    * @param hosturl
@@ -69,20 +60,40 @@ public class Jws2Instance implements AutoCloseable
   public Jws2Instance(String hosturl, String serviceType, String action,
           String description, JABAService service)
   {
-    super();
-    this.hosturl = hosturl;
-    this.serviceType = serviceType;
+    super(action, action, serviceType, description, hosturl);
     this.service = service;
-    this.action = action;
-    this.description = description;
+    if (service instanceof MsaWS<?>)
+    {
+      style = ServiceClient.MSAWSCLIENT;
+    }
+    else if (service instanceof SequenceAnnotation<?>)
+    {
+      style = ServiceClient.SEQUENCEANNOTATIONWSCLIENT;
+    }
+
     int p = description.indexOf("MORE INFORMATION:");
     if (p > -1)
     {
-      docUrl = description.substring(description.indexOf("http", p)).trim();
+      String docUrl = description.substring(description.indexOf("http", p))
+              .trim();
       if (docUrl.indexOf('\n') > -1)
       {
         docUrl = docUrl.substring(0, docUrl.indexOf("\n")).trim();
       }
+      if (docUrl.length() > 0)
+      {
+        try
+        {
+          URL url = new URL(docUrl);
+          if (url != null)
+          {
+            setDocumentationUrl(docUrl);
+          }
+        } catch (Exception x)
+        {
+
+        }
+      }
 
     }
   }
@@ -115,32 +126,12 @@ public class Jws2Instance implements AutoCloseable
       } catch (Exception ex)
       {
         System.err.println("Exception when retrieving presets for service "
-                + serviceType + " at " + hosturl);
+                + getServiceType() + " at " + getHostURL());
       }
     }
     return presets;
   }
 
-  public String getHost()
-  {
-    return hosturl;
-    /*
-     * try { URL serviceurl = new URL(hosturl); if (serviceurl.getPort()!=80) {
-     * return serviceurl.getHost()+":"+serviceurl.getPort(); } return
-     * serviceurl.getHost(); } catch (Exception e) {
-     * System.err.println("Failed to parse service URL '" + hosturl +
-     * "' as a valid URL!"); } return null;
-     */
-  }
-
-  /**
-   * @return short description of what the service will do
-   */
-  public String getActionText()
-  {
-    return action + " with " + serviceType;
-  }
-
   /**
    * non-thread safe - blocks whilst accessing service to get complete set of
    * available options and parameters
@@ -160,7 +151,7 @@ public class Jws2Instance implements AutoCloseable
     throw new Error(MessageManager.formatMessage(
             "error.implementation_error_runner_config_not_available",
             new String[]
-            { serviceType, service.getClass().toString() }));
+            { getServiceType(), service.getClass().toString() }));
   }
 
   @Override
@@ -179,6 +170,7 @@ public class Jws2Instance implements AutoCloseable
     // super.finalize();
   }
 
+  @Override
   public ParamDatastoreI getParamStore()
   {
     if (paramStore == null)
@@ -186,7 +178,7 @@ public class Jws2Instance implements AutoCloseable
       try
       {
         paramStore = new JabaParamStore(this,
-                (Desktop.instance != null ? Desktop.getUserParameterStore()
+                (Desktop.getInstance() != null ? Desktop.getUserParameterStore()
                         : null));
       } catch (Exception ex)
       {
@@ -198,17 +190,9 @@ public class Jws2Instance implements AutoCloseable
     return paramStore;
   }
 
-  public String getUri()
-  {
-    // this is only valid for Jaba 1.0 - this formula might have to change!
-    return hosturl
-            + (hosturl.lastIndexOf("/") == (hosturl.length() - 1) ? ""
-                    : "/")
-            + serviceType;
-  }
-
   private boolean hasParams = false, lookedForParams = false;
 
+  @Override
   public boolean hasParameters()
   {
     if (!lookedForParams)
@@ -225,28 +209,62 @@ public class Jws2Instance implements AutoCloseable
     return hasParams;
   }
 
-  public void attachWSMenuEntry(JMenu atpoint, AlignFrame alignFrame)
+  /**
+   * initialise a parameter store for this service
+   * 
+   * @param userParameterStore
+   *          - the user ParamManager (e.g. Desktop.getUserParameterStore() )
+   */
+  @Override
+  public void initParamStore(ParamManager userParameterStore)
   {
-    if (service instanceof MsaWS<?>)
-    {
-      new MsaWSClient().attachWSMenuEntry(atpoint, this, alignFrame);
-    }
-    else if (service instanceof SequenceAnnotation<?>)
+    if (paramStore == null)
     {
-      new SequenceAnnotationWSClient().attachWSMenuEntry(atpoint, this,
-              alignFrame);
+      paramStore = new JabaParamStore(this, userParameterStore);
     }
   }
 
-  public String getServiceTypeURI()
+  /**
+   * an object that implements one or more interfaces in jalview.ws.api
+   * 
+   * @return
+   */
+  @Override
+  public Object getEndpoint()
   {
-    return "java:" + serviceType;
-  }
-
-  jalview.ws.uimodel.AlignAnalysisUIText aaui;
+    if (service instanceof MsaWS<?>)
+    {
+      if (aaui != null)
+      {
+        throw new Error(
+                "JABAWS MsaWS based instant calculation not implemented.");
 
-  public jalview.ws.uimodel.AlignAnalysisUIText getAlignAnalysisUI()
-  {
-    return aaui;
+      }
+      else
+      {
+        return new JabawsMsaInstance(this);
+      }
+    }
+    else
+    {
+      if (service instanceof compbio.data.msa.SequenceAnnotation)
+      {
+        if (aaui != null)
+        {
+          try
+          {
+            // probably a factory would be nicer but..
+            return aaui.getClient().getConstructor(getClass())
+                    .newInstance(this);
+          } catch (Throwable t)
+          {
+            throw new Error("Implementation Error in web service framework",
+                    t);
+          }
+        }
+        return new AADisorderClient(this);
+      }
+      return null;
+    }
   }
 }
index 623b8de..8eeef4d 100644 (file)
@@ -20,8 +20,9 @@
  */
 package jalview.ws.jws2.jabaws2;
 
-import jalview.ws.jws2.AAConClient;
-import jalview.ws.jws2.RNAalifoldClient;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
 import java.util.HashMap;
@@ -29,11 +30,23 @@ import java.util.HashSet;
 
 import compbio.data.msa.JABAService;
 
-public class Jws2InstanceFactory
+public class Jws2InstanceFactory implements ApplicationSingletonI
 {
-  private static HashMap<String, AlignAnalysisUIText> aaConGUI;
 
-  private static HashSet<String> ignoreGUI;
+  private Jws2InstanceFactory()
+  {
+    // private singleton
+  }
+
+  private static Jws2InstanceFactory getInstance()
+  {
+    return (Jws2InstanceFactory) ApplicationSingletonProvider
+            .getInstance(Jws2InstanceFactory.class);
+  }
+
+  private HashMap<String, AlignAnalysisUIText> aaConGUI;
+
+  private HashSet<String> ignoreGUI;
 
   private static String category_rewrite(String cat_name)
   {
@@ -42,17 +55,17 @@ public class Jws2InstanceFactory
             : cat_name;
   }
 
-  private static void init()
+  private void init()
   {
     if (aaConGUI == null)
     {
-      aaConGUI = new HashMap<String, AlignAnalysisUIText>();
+      aaConGUI = new HashMap<>();
       aaConGUI.put(compbio.ws.client.Services.AAConWS.toString(),
-              AAConClient.getAlignAnalysisUITest());
+              AAConClient.getAlignAnalysisUIText());
       aaConGUI.put(compbio.ws.client.Services.RNAalifoldWS.toString(),
-              RNAalifoldClient.getAlignAnalysisUITest());
+              RNAalifoldClient.getAlignAnalysisUIText());
       // ignore list for JABAWS services not supported in jalview ...
-      ignoreGUI = new HashSet<String>();
+      ignoreGUI = new HashSet<>();
     }
   }
 
@@ -65,8 +78,8 @@ public class Jws2InstanceFactory
    */
   public static boolean ignoreService(String serviceType)
   {
-    init();
-    return (ignoreGUI.contains(serviceType.toString()));
+    getInstance().init();
+    return (getInstance().ignoreGUI.contains(serviceType.toString()));
   }
 
   /**
@@ -84,10 +97,10 @@ public class Jws2InstanceFactory
           String serviceType, String name, String description,
           JABAService service)
   {
-    init();
+    getInstance().init();
     Jws2Instance svc = new Jws2Instance(jwsservers, serviceType,
             category_rewrite(name), description, service);
-    svc.aaui = aaConGUI.get(serviceType.toString());
+    svc.setAlignAnalysisUI(getInstance().aaConGUI.get(serviceType.toString()));
     return svc;
   }
 
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.jws2;
+package jalview.ws.jws2.jabaws2;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
-import jalview.gui.AlignFrame;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.TreeSet;
 import java.util.regex.Pattern;
 
-import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.RNAStructReader.AlifoldResult;
 import compbio.data.sequence.RNAStructScoreManager;
 import compbio.data.sequence.Range;
 import compbio.data.sequence.Score;
-import compbio.metadata.Argument;
 
 /**
  * Client for the JABA RNA Alifold Service
@@ -49,29 +48,10 @@ import compbio.metadata.Argument;
  * 
  */
 
-public class RNAalifoldClient extends JabawsCalcWorker
+public class RNAalifoldClient extends JabawsAnnotationInstance
 {
 
-  String methodName;
-
-  AlignFrame af;
-
-  // keeps track of whether the RNAalifold result includes base contact
-  // probabilities
-  boolean bpScores;
-
-  public RNAalifoldClient(Jws2Instance sh, AlignFrame alignFrame,
-          WsParamSetI preset, List<Argument> paramset)
-  {
-    super(sh, alignFrame, preset, paramset);
-    af = alignFrame;
-    methodName = sh.serviceType;
-    alignedSeqs = true;
-    submitGaps = true;
-    nucleotidesAllowed = true;
-    proteinAllowed = false;
-    initViewportParams();
-  }
+  // configuration
 
   @Override
   public String getCalcId()
@@ -81,93 +61,89 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
   private static String CALC_ID = "jalview.ws.jws2.RNAalifoldClient";
 
-  public static AlignAnalysisUIText getAlignAnalysisUITest()
+  public static AlignAnalysisUIText getAlignAnalysisUIText()
   {
     return new AlignAnalysisUIText(
             compbio.ws.client.Services.RNAalifoldWS.toString(),
-            jalview.ws.jws2.RNAalifoldClient.class, CALC_ID, true, false,
-            true, MessageManager.getString("label.rnalifold_calculations"),
+            jalview.ws.jws2.jabaws2.RNAalifoldClient.class, CALC_ID, true,
+            false, true, true, false, 2,
+            MessageManager.getString("label.rnalifold_calculations"),
             MessageManager.getString("tooltip.rnalifold_calculations"),
             MessageManager.getString("label.rnalifold_settings"),
             MessageManager.getString("tooltip.rnalifold_settings"));
   }
 
-  @Override
-  public String getServiceActionText()
+  public static String getServiceActionText()
   {
     return "Submitting RNA alignment for Secondary Structure prediction using "
             + "RNAalifold Service";
   }
 
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
+  // instance
+
+  public RNAalifoldClient(Jws2Instance handle)
   {
-    return (seqs.size() > 1);
+    super(handle);
   }
 
   @Override
-  public void updateResultAnnotation(boolean immediate)
+  List<AlignmentAnnotation> annotationFromScoreManager(AlignmentI seqs,
+          Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters)
   {
-
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
+    List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+
+    // Unpack the ScoreManager
+    List<String> structs = ((RNAStructScoreManager) scoremanager)
+            .getStructs();
+    List<TreeSet<Score>> data = ((RNAStructScoreManager) scoremanager)
+            .getData();
+
+    // test to see if this data object contains base pair contacts
+    Score fscore = data.get(0).first();
+    boolean bpScores = (fscore.getMethod()
+            .equals(AlifoldResult.contactProbabilities.toString()));
+
+    // add annotation for the consensus sequence alignment
+    createAnnotationRowforScoreHolder(seqs, null,
+            ourAnnot,
+            getCalcId(), structs.get(0), null, null);
+
+    // Add annotations for the mfe Structure
+    createAnnotationRowforScoreHolder(seqs, null,
+            ourAnnot,
+            getCalcId(), structs.get(1), data.get(1), null);
+
+    // decide whether to add base pair contact probability histogram
+    int count = 2;
+    if (bpScores)
     {
+      createAnnotationRowforScoreHolder(seqs, null,
+              ourAnnot,
+              getCalcId(), structs.get(2), data.get(0), data.get(2));
+      count++;
+    }
 
-      List<AlignmentAnnotation> ourAnnot = new ArrayList<AlignmentAnnotation>();
-
-      // Unpack the ScoreManager
-      List<String> structs = ((RNAStructScoreManager) scoremanager)
-              .getStructs();
-      List<TreeSet<Score>> data = ((RNAStructScoreManager) scoremanager)
-              .getData();
-
-      // test to see if this data object contains base pair contacts
-      Score fscore = data.get(0).first();
-      this.bpScores = (fscore.getMethod()
-              .equals(AlifoldResult.contactProbabilities.toString()));
-
-      // add annotation for the consensus sequence alignment
-      createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-              structs.get(0), null, null);
-
-      // Add annotations for the mfe Structure
-      createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-              structs.get(1), data.get(1), null);
-
-      // decide whether to add base pair contact probability histogram
-      int count = 2;
-      if (bpScores)
-      {
-        createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-                structs.get(2), data.get(0), data.get(2));
-        count++;
-      }
-
-      // Now loop for the rest of the Annotations (if there it isn't stochastic
-      // output
-      // only the centroid and MEA structures remain anyway)
-      for (int i = count; i < structs.size(); i++)
-      {
-        // The ensemble values should be displayed in the description of the
-        // first (or all?) Stochastic Backtrack Structures.
-        if (!data.get(i).first().getMethod()
-                .equals(AlifoldResult.ensembleValues.toString()))
-        {
-
-          createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-                  structs.get(i), data.get(i), null);
-        }
-      }
-
-      if (ourAnnot.size() > 0)
+    // Now loop for the rest of the Annotations (if there it isn't stochastic
+    // output
+    // only the centroid and MEA structures remain anyway)
+    for (int i = count; i < structs.size(); i++)
+    {
+      // The ensemble values should be displayed in the description of the
+      // first (or all?) Stochastic Backtrack Structures.
+      if (!data.get(i).first().getMethod()
+              .equals(AlifoldResult.ensembleValues.toString()))
       {
 
-        updateOurAnnots(ourAnnot);
-        ap.adjustAnnotationHeight();
+        createAnnotationRowforScoreHolder(seqs, null, ourAnnot,
+                getCalcId(), structs.get(i), data.get(i), null);
       }
     }
+    return ourAnnot;
   }
 
-  protected void createAnnotationRowforScoreHolder(
+  private static void createAnnotationRowforScoreHolder(
+          AlignmentI alignment, boolean[] gapMap,
           List<AlignmentAnnotation> ourAnnot, String calcId, String struct,
           TreeSet<Score> data, TreeSet<Score> descriptionData)
   {
@@ -194,10 +170,10 @@ public class RNAalifoldClient extends JabawsCalcWorker
     String typename = typenameAndDescription[0];
     String description = typenameAndDescription[1];
 
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
+    AlignmentAnnotation annotation = alignment
             .findOrCreateAnnotation(typename, calcId, false, null, null);
 
-    constructAnnotationFromScoreHolder(annotation, struct, data);
+    constructAnnotationFromScoreHolder(gapMap, annotation, struct, data);
 
     /*
      * update annotation description with the free Energy, frequency in ensemble
@@ -211,15 +187,12 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
     annotation.belowAlignment = false;
     // annotation.showAllColLabels = true;
-
-    alignViewport.getAlignment().validateAnnotation(annotation);
-    af.setMenusForViewport();
-
+    annotation.validateRangeAndDisplay();
     ourAnnot.add(annotation);
   }
 
-  private AlignmentAnnotation constructAnnotationFromScoreHolder(
-          AlignmentAnnotation annotation, String struct,
+  private static AlignmentAnnotation constructAnnotationFromScoreHolder(
+          boolean[] gapMap, AlignmentAnnotation annotation, String struct,
           TreeSet<Score> data)
   {
     Annotation[] anns = new Annotation[gapMap != null ? gapMap.length + 1
@@ -231,7 +204,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
       // The base pair probabilities are stored in a set in scoreholder. we want
       // a map
-      LinkedHashMap<Range, Float> basePairs = new LinkedHashMap<Range, Float>();
+      LinkedHashMap<Range, Float> basePairs = new LinkedHashMap<>();
       for (Score score : data)
       {
         // The Score objects contain a set of size one containing the range and
@@ -307,7 +280,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
     return annotation;
   }
 
-  private String[] constructTypenameAndDescription(Score score)
+  private static String[] constructTypenameAndDescription(Score score)
   {
     String description = "";
     String typename = "";
@@ -374,10 +347,10 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
   // Check whether, at position i there is a base contact and return all the
   // contacts at this position. Should be in order of descending probability.
-  private LinkedHashMap<Range, Float> isContact(
+  private static LinkedHashMap<Range, Float> isContact(
           LinkedHashMap<Range, Float> basePairs, int i)
   {
-    LinkedHashMap<Range, Float> contacts = new LinkedHashMap<Range, Float>();
+    LinkedHashMap<Range, Float> contacts = new LinkedHashMap<>();
 
     for (Range contact : basePairs.keySet())
     {
@@ -393,10 +366,11 @@ public class RNAalifoldClient extends JabawsCalcWorker
     return contacts;
   }
 
-  private char isSS(char chr)
+  private static char isSS(char chr)
   {
     String regex = "\\(|\\)|\\{|\\}|\\[|\\]";
     char ss = (Pattern.matches(regex, Character.toString(chr))) ? 'S' : ' ';
     return ss;
   }
+
 }
index 1554a7b..a5cb473 100644 (file)
@@ -30,6 +30,15 @@ public interface ArgumentI
 
   /**
    * 
+   * @return display name of this argument
+   */
+  default String getLabel()
+  {
+    return getName();
+  };
+
+  /**
+   * 
    * @return current value for the argument (may equal the name)
    */
   String getValue();
index 29aeef9..68ec7a6 100644 (file)
  */
 package jalview.ws.params;
 
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+
+import java.util.ArrayList;
 import java.util.List;
 
-public abstract class AutoCalcSetting
+public class AutoCalcSetting
 {
 
   protected boolean autoUpdate;
@@ -31,9 +35,13 @@ public abstract class AutoCalcSetting
 
   protected List<ArgumentI> jobArgset;
 
-  public AutoCalcSetting(WsParamSetI preset2, List<ArgumentI> jobArgset2,
+  protected ServiceWithParameters service;
+
+  public AutoCalcSetting(ServiceWithParameters service2,
+          WsParamSetI preset2, List<ArgumentI> jobArgset2,
           boolean autoUpdate2)
   {
+    service = service2;
     autoUpdate = autoUpdate2;
     preset = preset2;
     jobArgset = jobArgset2;
@@ -66,25 +74,117 @@ public abstract class AutoCalcSetting
   }
 
   /**
+   * TODO: refactor to ServiceWithParameters ?
    * 
    * @return characteristic URI for this service. The URI should reflect the
    *         type and version of this service, enabling the service client code
    *         to recover the correct client for this calculation.
    */
-  public abstract String getServiceURI();
+  public String getServiceURI()
+  {
+    return service.getNameURI();
+  }
 
   /**
+   * TODO: refactor to ServiceWithParameters ?
+   * 
    * return any concrete service endpoints associated with this calculation.
    * built in services should return a zero length array
    * 
    * @return
    */
-  public abstract String[] getServiceURLs();
+  public String[] getServiceURLs()
+  {
+    return new String[] { service.getUri() };
+  }
 
   /**
+   * default WsParamFile generator method - clients with custom formats should
+   * override and implement their own
    * 
    * @return stringified representation of the parameters for this setting
    */
-  public abstract String getWsParamFile();
+  public String getWsParamFile()
+  {
+    List<ArgumentI> opts = null;
+    if (jobArgset != null)
+    {
+      opts = jobArgset;
+    }
+    else
+    {
+      if (preset != null)
+      {
+        opts = preset.getArguments();
+      }
+    }
+    if (opts == null || opts.size() == 0)
+    {
+      return "";
+    }
+    StringBuffer pset = new StringBuffer();
+    for (ArgumentI ps : opts)
+    {
+      pset.append(ps.getName() + "\t" + ps.getValue());
+      pset.append("\n");
+    }
+    return pset.toString();
+  }
+  public ServiceWithParameters getService()
+  {
+    return service;
+  }
+
+  public void setService(ServiceWithParameters service)
+  {
+    this.service = service;
+    if (preset != null)
+    {
+      // check if we need to migrate preset to a new service URL
+      for (String url : preset.getApplicableUrls())
+      {
+        if (url.equals(service.getUri()))
+        {
+          // preset already verified
+          return;
+        }
+      }
+      WsParamSetI pr = service.getParamStore().getPreset(preset.getName());
+  
+      // TODO: decide of this distinction between preset and args are needed.
+      //
+      // if (pr instanceof JabaPreset && preset instanceof JabaPreset)
+      // {
+      // // easy - Presets are identical (we assume)
+      // preset = pr;
+      // return;
+      // }
+  
+      // this verifies that all arguments in the existing preset are the same as
+      // the parameters for the preset provided by the service parameter store.
+      // ie the LastUsed settings or a predefined preset.
+  
+      List<ArgumentI> oldargs = new ArrayList<>(),
+              newargs = new ArrayList<>();
+      oldargs.addAll(preset.getArguments());
+      // need to compare parameters
+      for (ArgumentI newparg : pr.getArguments())
+      {
+        if (!oldargs.remove(newparg))
+        {
+          newargs.add(newparg);
+        }
+      }
+      if (oldargs.size() == 0 && newargs.size() == 0)
+      {
+        // exact match.
+        preset = pr;
+        return;
+      }
+      // Try even harder to migrate arguments.
+      throw new Error(MessageManager
+              .getString("error.parameter_migration_not_implemented_yet"));
+    }
+  }
 
 }
index 2c5386d..af8ae27 100644 (file)
@@ -25,15 +25,49 @@ import java.util.List;
 
 public interface OptionI extends ArgumentI
 {
-
+  /**
+   * Answers a URL with further details for this option, or null if none is
+   * known
+   * 
+   * @return
+   */
   URL getFurtherDetails();
 
+  /**
+   * Answers true if the option is mandatory (a value must be chosen), false if
+   * it is optional
+   * 
+   * @return
+   */
   boolean isRequired();
 
+  /**
+   * Answers the description of the option
+   * 
+   * @return
+   */
   String getDescription();
 
+  /**
+   * Answers a list of possible values that may be chosen for the option (or
+   * null if not applicable)
+   * 
+   * @return
+   */
   List<String> getPossibleValues();
 
-  OptionI copy();
+  /**
+   * Answers a list of display names corresponding to the possible values that
+   * may be chosen for the option (or null if not applicable)
+   * 
+   * @return
+   */
+  List<String> getDisplayNames();
 
+  /**
+   * Answers a new Option with a copy of the settings of this one
+   * 
+   * @return
+   */
+  OptionI copy();
 }
index da46745..d1bdc98 100644 (file)
@@ -29,7 +29,7 @@ public interface ValueConstrainI
 
   public enum ValueType
   {
-    Integer, Float, String
+    Integer, Float, String, Double, File
   };
 
   ValueType getType();
index f80ff77..df17296 100644 (file)
  */
 package jalview.ws.params.simple;
 
-import jalview.ws.params.OptionI;
-
 import java.net.URL;
 import java.util.Arrays;
 
-public class BooleanOption extends Option implements OptionI
+public class BooleanOption extends Option
 {
 
   public BooleanOption(String name, String descr, boolean required,
-          boolean defVal, boolean val, URL link)
+      Boolean defVal, Boolean val, URL link)
   {
+    super(name, descr, required, (defVal != null && defVal ? name : null),
+        (val != null && val ? name : null), Arrays.asList(name), link);
+  }
 
-    super(name, descr, required, (defVal ? name : ""), (val ? name : ""),
-            Arrays.asList(new String[]
-            { name }), link);
+  public BooleanOption(String name, String description, String label,
+      boolean isrequired, Boolean defValue, String reprValue, URL link)
+  {
+    super(name, description, label, isrequired,
+        defValue != null && defValue ? reprValue : null,
+        defValue != null && defValue ? reprValue : null,
+        Arrays.asList(reprValue), link);
   }
 
+  public BooleanOption(String name, String description, String label,
+      boolean isrequired, Boolean defValue, URL link)
+  {
+    this(name, description, label, isrequired, defValue, String.valueOf(true), link);
+  }
 }
diff --git a/src/jalview/ws/params/simple/DoubleParameter.java b/src/jalview/ws/params/simple/DoubleParameter.java
new file mode 100644 (file)
index 0000000..6b76170
--- /dev/null
@@ -0,0 +1,76 @@
+package jalview.ws.params.simple;
+
+import jalview.ws.params.ParameterI;
+import jalview.ws.params.ValueConstrainI;
+
+/**
+ * 
+ * @author TZVanaalten
+ *
+ */
+public class DoubleParameter extends Option implements ParameterI
+{
+  double defval;
+
+  double min;
+
+  double max;
+
+  @Override
+  public ValueConstrainI getValidValue()
+  {
+    return new ValueConstrainI()
+    {
+      @Override
+      public ValueType getType()
+      {
+        return ValueType.Double;
+      }
+
+      @Override
+      public Number getMin()
+      {
+        return min < max ? min : null;
+      }
+
+      @Override
+      public Number getMax()
+      {
+        return min < max ? max : null;
+      }
+    };
+  }
+
+  public DoubleParameter(DoubleParameter parm)
+  {
+    super(parm);
+    max = parm.max;
+    min = parm.min;
+  }
+
+  public DoubleParameter(String name, String description, boolean required,
+          Double defValue, double min, double max)
+  {
+    super(name, description, required, String.valueOf(defValue), null, null,
+            null);
+    defval = defValue;
+    this.min = min;
+    this.max = max;
+  }
+
+  public DoubleParameter(String name, String description, boolean required,
+          Double defValue, Double value, double min, double max)
+  {
+    super(name, description, required, String.valueOf(defValue),
+            String.valueOf(value), null, null);
+    defval = defValue;
+    this.min = min;
+    this.max = max;
+  }
+
+  @Override
+  public DoubleParameter copy()
+  {
+    return new DoubleParameter(this);
+  }
+}
diff --git a/src/jalview/ws/params/simple/FileParameter.java b/src/jalview/ws/params/simple/FileParameter.java
new file mode 100644 (file)
index 0000000..aa8e7ad
--- /dev/null
@@ -0,0 +1,47 @@
+package jalview.ws.params.simple;
+
+import jalview.ws.params.ValueConstrainI;
+
+/**
+ * A class that represents a file parameter. User entry options should include
+ * direct input of a file path as text, or file selection using a file browser.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class FileParameter extends StringParameter
+{
+
+  public FileParameter(String name, String description, boolean required,
+          String defValue, String value)
+  {
+    super(name, description, required, defValue, value);
+  }
+
+  @Override
+  public ValueConstrainI getValidValue()
+  {
+    return new ValueConstrainI()
+    {
+
+      @Override
+      public ValueType getType()
+      {
+        return ValueType.File;
+      }
+
+      @Override
+      public Number getMax()
+      {
+        return null;
+      }
+
+      @Override
+      public Number getMin()
+      {
+        return null;
+      }
+    };
+  }
+
+}
index fb34e89..b3a01b2 100644 (file)
@@ -31,8 +31,11 @@ public class IntegerParameter extends Option implements ParameterI
 {
   int defval;
 
-  int min, max;
+  int min;
 
+  int max;
+
+  @Override
   public ValueConstrainI getValidValue()
   {
     return new ValueConstrainI()
@@ -47,27 +50,13 @@ public class IntegerParameter extends Option implements ParameterI
       @Override
       public Number getMin()
       {
-        if (min < max)
-        {
-          return min;
-        }
-        else
-        {
-          return null;
-        }
+        return min < max ? min : null;
       }
 
       @Override
       public Number getMax()
       {
-        if (min < max)
-        {
-          return max;
-        }
-        else
-        {
-          return null;
-        }
+        return min < max ? max : null;
       }
     };
   }
diff --git a/src/jalview/ws/params/simple/LogarithmicParameter.java b/src/jalview/ws/params/simple/LogarithmicParameter.java
new file mode 100644 (file)
index 0000000..af80181
--- /dev/null
@@ -0,0 +1,80 @@
+package jalview.ws.params.simple;
+
+import jalview.ws.params.ParameterI;
+import jalview.ws.params.ValueConstrainI;
+
+/**
+ * A model for a numeric-valued parameter which should be displayed using a
+ * logarithmic scale
+ * 
+ * @author TZVanaalten
+ */
+public class LogarithmicParameter extends Option implements ParameterI
+{
+  final double defval;
+
+  final double min;
+
+  final double max;
+
+  @Override
+  public ValueConstrainI getValidValue()
+  {
+    return new ValueConstrainI()
+    {
+
+      @Override
+      public ValueType getType()
+      {
+        return ValueType.Double;
+      }
+
+      @Override
+      public Number getMin()
+      {
+        return min < max ? min : null;
+      }
+
+      @Override
+      public Number getMax()
+      {
+        return min < max ? max : null;
+      }
+    };
+  }
+
+  public LogarithmicParameter(LogarithmicParameter parm)
+  {
+    super(parm);
+    max = parm.max;
+    min = parm.min;
+    defval = 0D;
+  }
+
+  public LogarithmicParameter(String name, String description,
+          boolean required, Double defValue, double min, double max)
+  {
+    super(name, description, required, String.valueOf(defValue), null, null,
+            null);
+    defval = defValue;
+    this.min = min;
+    this.max = max;
+  }
+
+  public LogarithmicParameter(String name, String description,
+          boolean required, Double defValue, double value, double min,
+          double max)
+  {
+    super(name, description, required, String.valueOf(defValue),
+            String.valueOf(value), null, null);
+    defval = defValue;
+    this.min = min;
+    this.max = max;
+  }
+
+  @Override
+  public LogarithmicParameter copy()
+  {
+    return new LogarithmicParameter(this);
+  }
+}
index 653359f..40e3804 100644 (file)
@@ -24,20 +24,135 @@ import jalview.ws.params.OptionI;
 
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 public class Option implements OptionI
 {
+  String name;
 
-  String name, value, defvalue, description;
+  String label;
 
-  ArrayList<String> possibleVals = new ArrayList<String>();
+  /*
+   * current value in string format, or "null" if undefined
+   */
+  String value;
+
+  /*
+   * default value in string format, or "null" if undefined
+   */
+  String defvalue;
+
+  String description;
+
+  List<String> possibleVals;
+
+  /*
+   * optional display names corresponding to possibleVals
+   */
+  List<String> displayVals;
 
   boolean required;
 
   URL fdetails;
 
+  /**
+   * Copy constructor
+   * 
+   * @param opt
+   */
+  public Option(Option opt)
+  {
+    name = opt.name;
+    label = opt.label;
+    value = opt.value;
+    defvalue = opt.defvalue;
+    description = opt.description;
+    if (opt.possibleVals != null)
+    {
+      possibleVals = new ArrayList<>(opt.possibleVals);
+    }
+    required = opt.required;
+    // URLs are singletons - so we copy by reference. nasty but true.
+    fdetails = opt.fdetails;
+  }
+
+  public Option()
+  {
+  }
+
+  public Option(String name, String description, String label, boolean isrequired,
+      String defValue, String val, List<String> possibleVals, URL fdetails)
+  {
+    this(name, description, isrequired, defValue, val, possibleVals, fdetails);
+    this.label = label;
+  }
+
+  /**
+   * Constructor including display names for possible values
+   * 
+   * @param name2
+   * @param description2
+   * @param isrequired
+   * @param defValue
+   * @param val
+   * @param possibleVals
+   * @param fdetails
+   */
+  public Option(String name2, String description2, boolean isrequired,
+          String defValue, String val, List<String> possibleVals,
+          List<String> displayNames, URL fdetails)
+  {
+    name = name2;
+    description = description2;
+    this.value = val;
+    this.required = isrequired;
+    this.defvalue = defValue;
+    if (possibleVals != null)
+    {
+      this.possibleVals = new ArrayList<>(possibleVals);
+    }
+    if (displayNames != null)
+    {
+      this.displayVals = new ArrayList<>(displayNames);
+    }
+    this.fdetails = fdetails;
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param name2
+   * @param description2
+   * @param isrequired
+   * @param defValue
+   * @param val
+   * @param possibleVals
+   * @param fdetails
+   */
+  public Option(String name2, String description2, boolean isrequired,
+          String defValue, String val, List<String> possibleVals,
+          URL fdetails)
+  {
+    this(name2, description2, isrequired, defValue, val, possibleVals, null,
+            fdetails);
+  }
+
+  @Override
+  public OptionI copy()
+  {
+    Option opt = new Option(this);
+    return opt;
+  }
+
+  /**
+   * toString method to help identify options in the debugger only
+   */
+  @Override
+  public String toString()
+  {
+    return this.getClass().getName() + ":" + name;
+  }
+
   @Override
   public String getName()
   {
@@ -45,6 +160,12 @@ public class Option implements OptionI
   }
 
   @Override
+  public String getLabel()
+  {
+    return label != null ? label : name;
+  }
+
+  @Override
   public String getValue()
   {
     return value == null ? defvalue : value;
@@ -80,49 +201,9 @@ public class Option implements OptionI
     return possibleVals;
   }
 
-  public Option(Option opt)
-  {
-    name = new String(opt.name);
-    if (opt.value != null)
-      value = new String(opt.value);
-    if (opt.defvalue != null)
-      defvalue = new String(opt.defvalue);
-    if (opt.description != null)
-      description = new String(opt.description);
-    if (opt.possibleVals != null)
-    {
-      possibleVals = (ArrayList<String>) opt.possibleVals.clone();
-    }
-    required = opt.required;
-    // URLs are singletons - so we copy by reference. nasty but true.
-    fdetails = opt.fdetails;
-  }
-
-  public Option()
-  {
-  }
-
-  public Option(String name2, String description2, boolean isrequired,
-          String defValue, String value, Collection<String> possibleVals,
-          URL fdetails)
-  {
-    name = name2;
-    description = description2;
-    this.value = value;
-    this.required = isrequired;
-    this.defvalue = defValue;
-    if (possibleVals != null)
-    {
-      this.possibleVals = new ArrayList<String>();
-      this.possibleVals.addAll(possibleVals);
-    }
-    this.fdetails = fdetails;
-  }
-
   @Override
-  public OptionI copy()
+  public List<String> getDisplayNames()
   {
-    Option opt = new Option(this);
-    return opt;
+    return displayVals;
   }
 }
diff --git a/src/jalview/ws/params/simple/Parameter.java b/src/jalview/ws/params/simple/Parameter.java
deleted file mode 100644 (file)
index e2c81a6..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.params.simple;
-
-import jalview.ws.params.OptionI;
-import jalview.ws.params.ParameterI;
-import jalview.ws.params.ValueConstrainI;
-
-public abstract class Parameter extends Option
-        implements OptionI, ParameterI
-{
-  ValueConstrainI validator;
-
-  @Override
-  public ValueConstrainI getValidValue()
-  {
-    return validator;
-  }
-
-  public Parameter(Parameter parm)
-  {
-    super(parm);
-  }
-
-  public Parameter(ValueConstrainI validator)
-  {
-    super();
-    this.validator = validator;
-  }
-
-  @Override
-  public abstract Parameter copy();
-}
  */
 package jalview.ws.params.simple;
 
-public class StringChoiceParameter extends Option
+import java.util.List;
+
+/**
+ * A parameter with a choice of possible options, preferred to be rendered as
+ * radio buttons if possible
+ */
+public class RadioChoiceParameter extends StringParameter
 {
 
+  /**
+   * Constructor
+   * 
+   * @param name
+   * @param description
+   * @param options
+   * @param def
+   */
+  public RadioChoiceParameter(String name, String description,
+          List<String> options, String def)
+  {
+    super(name, description, true, def, def, options, null);
+  }
 }
diff --git a/src/jalview/ws/params/simple/StringParameter.java b/src/jalview/ws/params/simple/StringParameter.java
new file mode 100644 (file)
index 0000000..d3d899c
--- /dev/null
@@ -0,0 +1,88 @@
+package jalview.ws.params.simple;
+
+import jalview.ws.params.ParameterI;
+import jalview.ws.params.ValueConstrainI;
+
+import java.util.List;
+
+public class StringParameter extends Option implements ParameterI
+{
+  @Override
+  public ValueConstrainI getValidValue()
+  {
+    return new StringValueConstrain();
+  }
+
+  @Override
+  public ParameterI copy()
+  {
+    return new StringParameter(this);
+  }
+
+  private class StringValueConstrain implements ValueConstrainI
+  {
+
+    @Override
+    public ValueType getType()
+    {
+      return ValueType.String;
+    }
+
+    @Override
+    public Number getMax()
+    {
+      return null;
+    }
+
+    @Override
+    public Number getMin()
+    {
+      return null;
+    }
+
+  }
+
+  public StringParameter(StringParameter parm)
+  {
+    this.name = parm.name;
+    this.defvalue = parm.defvalue;
+    this.possibleVals = parm.possibleVals;
+    this.displayVals = parm.displayVals;
+  }
+
+  public StringParameter(String name, String description, boolean required,
+          String defValue)
+  {
+    super(name, description, required, String.valueOf(defValue), null, null,
+            null);
+    this.defvalue = defValue;
+  }
+
+  public StringParameter(String name, String description, boolean required,
+          String defValue, String value)
+  {
+    super(name, description, required, String.valueOf(defValue),
+            String.valueOf(value), null, null);
+    this.defvalue = defValue;
+  }
+
+  /**
+   * Constructor for a parameter with a list of possible values and (optionally)
+   * corresponding display names
+   * 
+   * @param name2
+   * @param description2
+   * @param isrequired
+   * @param defValue
+   * @param value
+   * @param possibleVals
+   * @param displayNames
+   */
+  public StringParameter(String name2, String description2,
+          boolean isrequired, String defValue, String value,
+          List<String> possibleVals, List<String> displayNames)
+  {
+    super(name2, description2, isrequired, defValue, value, possibleVals,
+            displayNames, null);
+  }
+}
index bd2f664..204e3eb 100644 (file)
@@ -40,6 +40,7 @@ import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.util.EntityUtils;
 import org.apache.james.mime4j.MimeException;
 import org.apache.james.mime4j.parser.MimeStreamParser;
 
@@ -116,23 +117,22 @@ public class HttpResultSet extends FileParse implements AutoCloseable
     }
     jalview.io.packed.JalviewDataset ds = restJob.newJalviewDataset();
     // Decide how we deal with content.
-    if (en instanceof MultipartEntity)
+    // TODO : verify we are detecting a multipart response correctly
+    if (en.getContentType().getValue().startsWith("multipart/form-data"))
     {
       // Multipart messages should be properly typed, so we parse them as we go.
-      MultipartEntity mpe = (MultipartEntity) en;
-      // multipart
       JalviewMimeContentHandler handler = new JalviewMimeContentHandler(ds);
       MimeStreamParser parser = new MimeStreamParser();
       parser.setContentHandler(handler);
       try
       {
-        parser.parse(mpe.getContent());
+        parser.parse(en.getContent());
       } catch (MimeException me)
       {
         error = true;
         errormessage = "Couldn't parse message from web service.";
         Console.warn("Failed to parse MIME multipart content", me);
-        en.consumeContent();
+        EntityUtils.consume(en);
       }
       return new ParsePackedSet().getAlignment(ds,
               handler.getJalviewDataProviders());
@@ -186,7 +186,7 @@ public class HttpResultSet extends FileParse implements AutoCloseable
       {
         Console.error("Can't handle encoding '" + enc
                 + "' for response from webservice.", e);
-        en.consumeContent();
+        EntityUtils.consume(en);
         error = true;
         errormessage = "Can't handle encoding for response from webservice";
         return;
index fb291da..ad03ce4 100644 (file)
@@ -32,7 +32,6 @@ import jalview.ws.params.simple.Option;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -60,9 +59,9 @@ public abstract class InputType
   {
     NUC, PROT, MIX;
 
-    public static Collection<String> toStringValues()
+    public static List<String> toStringValues()
     {
-      Collection<String> c = new ArrayList<String>();
+      List<String> c = new ArrayList<>();
       for (molType type : values())
       {
         c.add(type.toString());
@@ -77,7 +76,7 @@ public abstract class InputType
 
   public int max = 0; // unbounded
 
-  protected ArrayList<Class> inputData = new ArrayList<Class>();
+  protected List<Class> inputData = new ArrayList<>();
 
   /**
    * initialise the InputType with a list of jalview data classes that the
@@ -106,7 +105,9 @@ public abstract class InputType
   public boolean validFor(RestJob restJob)
   {
     if (!validFor(restJob.rsd))
+    {
       return false;
+    }
     for (Class cl : inputData)
     {
       if (!restJob.hasDataOfType(cl))
@@ -120,7 +121,9 @@ public abstract class InputType
   public boolean validFor(RestServiceDescription restServiceDescription)
   {
     if (!restServiceDescription.inputParams.values().contains(this))
+    {
       return false;
+    }
 
     return true;
   }
@@ -272,7 +275,7 @@ public abstract class InputType
 
   public List<OptionI> getBaseOptions()
   {
-    ArrayList<OptionI> opts = new ArrayList<OptionI>();
+    ArrayList<OptionI> opts = new ArrayList<>();
     opts.add(new IntegerParameter("min",
             "Minimum number of data of this type", true, 1, min, 0, -1));
     opts.add(new IntegerParameter("max",
@@ -297,7 +300,7 @@ public abstract class InputType
   public void configureFromArgumentI(List<ArgumentI> currentSettings)
           throws InvalidArgumentException
   {
-    ArrayList<String> urltoks = new ArrayList<String>();
+    List<String> urltoks = new ArrayList<>();
     String rg;
     for (ArgumentI arg : currentSettings)
     {
index 255ab58..0394fea 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.rest;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
@@ -28,15 +30,14 @@ import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
 import jalview.gui.WebserviceInfo;
-import jalview.io.packed.DataProvider.JvDataType;
 import jalview.util.MessageManager;
 import jalview.ws.WSClient;
 import jalview.ws.WSClientI;
 import jalview.ws.WSMenuEntryProviderI;
+import jalview.ws.rest.clientdefs.ShmrRestClient;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.Hashtable;
 import java.util.Vector;
 
 import javax.swing.JMenu;
@@ -49,8 +50,24 @@ import javax.swing.event.MenuListener;
  * 
  */
 public class RestClient extends WSClient
-        implements WSClientI, WSMenuEntryProviderI
+implements WSClientI, WSMenuEntryProviderI, ApplicationSingletonI
 {
+  @SuppressWarnings("unused")
+  private RestClient()
+  {
+    // accessed by ApplicationSingletonProvider
+  }
+
+  
+private static RestClient getInstance()
+{
+return (RestClient) ApplicationSingletonProvider.getInstance(RestClient.class);
+}
+
+public static final String RSBS_SERVICES = "RSBS_SERVICES";
+
+
+  protected Vector<String> services = null;
   RestServiceDescription service;
 
   public RestClient(RestServiceDescription rsd)
@@ -99,17 +116,17 @@ public class RestClient extends WSClient
   {
     WebServiceJobTitle = MessageManager
             .formatMessage("label.webservice_job_title", new String[]
-            { service.details.Action, service.details.Name });
-    WebServiceName = service.details.Name;
+            { service.details.getAction(), service.details.getName() });
+    WebServiceName = service.details.getName();
     WebServiceReference = "No reference - go to url for more info";
-    if (service.details.description != null)
+    if (service.details.getDescription() != null)
     {
-      WebServiceReference = service.details.description;
+      WebServiceReference = service.details.getDescription();
     }
     if (!headless)
     {
       wsInfo = new WebserviceInfo(WebServiceJobTitle,
-              WebServiceName + "\n" + WebServiceReference, true);
+              WebServiceName + "\n" + WebServiceReference, Desktop.FRAME_MAKE_VISIBLE);
       wsInfo.setRenderAsHtml(true);
     }
 
@@ -141,10 +158,10 @@ public class RestClient extends WSClient
   public void attachWSMenuEntry(final JMenu wsmenu,
           final AlignFrame alignFrame)
   {
-    JMenuItem submit = new JMenuItem(service.details.Name);
+    JMenuItem submit = new JMenuItem(service.details.getName());
     submit.setToolTipText(MessageManager
             .formatMessage("label.rest_client_submit", new String[]
-            { service.details.Action, service.details.Name }));
+            { service.details.getAction(), service.details.getName() }));
     submit.addActionListener(new ActionListener()
     {
 
@@ -329,7 +346,7 @@ public class RestClient extends WSClient
     else
     {
       // TODO: try to tell the user why the job couldn't be started.
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               (jobsthread.hasWarnings() ? jobsthread.getWarnings()
                       : MessageManager.getString(
                               "label.job_couldnt_be_started_check_input")),
@@ -339,45 +356,6 @@ public class RestClient extends WSClient
     }
   }
 
-  public static RestClient makeShmmrRestClient()
-  {
-    String action = "Analysis",
-            description = "Sequence Harmony and Multi-Relief (Brandt et al. 2010)",
-            name = MessageManager.getString("label.multiharmony");
-    Hashtable<String, InputType> iparams = new Hashtable<String, InputType>();
-    jalview.ws.rest.params.JobConstant toolp;
-    // toolp = new jalview.ws.rest.JobConstant("tool","jalview");
-    // iparams.put(toolp.token, toolp);
-    // toolp = new jalview.ws.rest.params.JobConstant("mbjob[method]","shmr");
-    // iparams.put(toolp.token, toolp);
-    // toolp = new
-    // jalview.ws.rest.params.JobConstant("mbjob[description]","step 1");
-    // iparams.put(toolp.token, toolp);
-    // toolp = new jalview.ws.rest.params.JobConstant("start_search","1");
-    // iparams.put(toolp.token, toolp);
-    // toolp = new jalview.ws.rest.params.JobConstant("blast","0");
-    // iparams.put(toolp.token, toolp);
-
-    jalview.ws.rest.params.Alignment aliinput = new jalview.ws.rest.params.Alignment();
-    // SHMR server has a 65K limit for content pasted into the 'ali' parameter,
-    // so we always upload our files.
-    aliinput.token = "ali_file";
-    aliinput.writeAsFile = true;
-    iparams.put(aliinput.token, aliinput);
-    jalview.ws.rest.params.SeqGroupIndexVector sgroups = new jalview.ws.rest.params.SeqGroupIndexVector();
-    sgroups.setMinsize(2);
-    sgroups.min = 2;// need at least two group defined to make a partition
-    iparams.put("groups", sgroups);
-    sgroups.token = "groups";
-    sgroups.sep = " ";
-    RestServiceDescription shmrService = new RestServiceDescription(action,
-            description, name,
-            "http://zeus.few.vu.nl/programs/shmrwww/index.php?tool=jalview", // ?tool=jalview&mbjob[method]=shmr&mbjob[description]=step1",
-            "?tool=jalview", iparams, true, false, '-');
-    // a priori knowledge of the data returned from the service
-    shmrService.addResultDatatype(JvDataType.ANNOTATION);
-    return new RestClient(shmrService);
-  }
 
   public AlignmentPanel recoverAlignPanelForView()
   {
@@ -399,20 +377,23 @@ public class RestClient extends WSClient
     return true;
   }
 
-  protected static Vector<String> services = null;
-
-  public static final String RSBS_SERVICES = "RSBS_SERVICES";
 
   public static RestClient[] getRestClients()
   {
+    return getInstance().getClients();
+  }
+    
+  private RestClient[] getClients()
+  {
     if (services == null)
     {
-      services = new Vector<String>();
+      services = new Vector<>();
       try
       {
         for (RestServiceDescription descr : RestServiceDescription
-                .parseDescriptions(Cache.getDefault(RSBS_SERVICES,
-                        makeShmmrRestClient().service.toString())))
+                .parseDescriptions(Cache.getDefault(
+                        RSBS_SERVICES,
+                        ShmrRestClient.makeShmmrRestClient().service.toString())))
         {
           services.add(descr.toString());
         }
@@ -435,7 +416,7 @@ public class RestClient extends WSClient
 
   public String getAction()
   {
-    return service.details.Action;
+    return service.details.getAction();
   }
 
   public RestServiceDescription getRestDescription()
@@ -445,7 +426,7 @@ public class RestClient extends WSClient
 
   public static Vector<String> getRsbsDescriptions()
   {
-    Vector<String> rsbsDescrs = new Vector<String>();
+    Vector<String> rsbsDescrs = new Vector<>();
     for (RestClient rsbs : getRestClients())
     {
       rsbsDescrs.add(rsbs.getRestDescription().toString());
@@ -457,10 +438,11 @@ public class RestClient extends WSClient
   {
     if (rsbsUrls != null)
     {
+      
       // TODO: consider validating services ?
-      services = new Vector<String>(rsbsUrls);
+      getInstance().services = new Vector<String>(rsbsUrls);
       StringBuffer sprop = new StringBuffer();
-      for (String s : services)
+      for (String s : getInstance().services)
       {
         sprop.append(s);
       }
index 824af90..67f11c2 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ws.rest;
 
+import jalview.analysis.SeqsetUtils.SequenceInfo;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
@@ -52,7 +53,7 @@ public class RestJob extends AWsJob
 
   boolean gotresult;
 
-  Hashtable squniq;
+  Map<String, SequenceInfo> squniq;
 
   /**
    * dataset associated with this input data.
index eff38fb..0895f0f 100644 (file)
@@ -61,8 +61,6 @@ import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.entity.mime.HttpMultipartMode;
 import org.apache.http.entity.mime.MultipartEntity;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EntityUtils;
 
 public class RestJobThread extends AWSThread
@@ -218,9 +216,6 @@ public class RestJobThread extends AWSThread
   protected void doHttpReq(Stage stg, RestJob rj, String postUrl)
           throws Exception
   {
-    StringBuffer respText = new StringBuffer();
-    // con.setContentHandlerFactory(new
-    // jalview.ws.io.mime.HttpContentHandler());
     HttpRequestBase request = null;
     String messages = "";
     if (stg == Stage.SUBMIT)
@@ -255,7 +250,6 @@ public class RestJobThread extends AWSThread
     {
       DefaultHttpClient httpclient = new DefaultHttpClient();
 
-      HttpContext localContext = new BasicHttpContext();
       HttpResponse response = null;
       try
       {
@@ -290,38 +284,18 @@ public class RestJobThread extends AWSThread
         processResultSet(rj, response, request);
         break;
       case 202:
-        rj.statMessage = "<br>Job submitted successfully. Results available at this URL:\n"
-                + "<a href=" + rj.getJobId() + "\">" + rj.getJobId()
-                + "</a><br>";
-        rj.running = true;
+        markJobAsRunning(rj);
         break;
+      case 201:
+        // Created - redirect may be present. Fallthrough to 302
       case 302:
-        Header[] loc;
-        if (!rj.isSubmitted()
-                && (loc = response
-                        .getHeaders(HTTPConstants.HEADER_LOCATION)) != null
-                && loc.length > 0)
-        {
-          if (loc.length > 1)
-          {
-            Console.warn("Ignoring additional " + (loc.length - 1)
-                    + " location(s) provided in response header ( next one is '"
-                    + loc[1].getValue() + "' )");
-          }
-          rj.setJobId(loc[0].getValue());
-          rj.setSubmitted(true);
-        }
+        extractJobId(rj, response);
         completeStatus(rj, response);
         break;
       case 500:
-        // Failed.
-        rj.setSubmitted(true);
-        rj.setAllowedServerExceptions(0);
-        rj.setSubjobComplete(true);
-        rj.error = true;
-        rj.running = false;
-        completeStatus(rj, response,
-                "" + getStage(stg) + "failed. Reason below:\n");
+        markAsFailed(rj, response);
+        completeStatus(rj, response, "" + getStage(stg)
+                + "failed. Reason below:\n");
         break;
       default:
         // Some other response. Probably need to pop up the content in a window.
@@ -347,6 +321,62 @@ public class RestJobThread extends AWSThread
     }
   }
 
+  private void markAsFailed(RestJob rj, HttpResponse response)
+  {
+    // Failed.
+    rj.setSubmitted(true);
+    rj.setAllowedServerExceptions(0);
+    rj.setSubjobComplete(true);
+    rj.error = true;
+    rj.running = false;
+  }
+
+  /**
+   * set the jobRunning flag and post a link to the physical result page encoded
+   * in rj.getJobId()
+   * 
+   * @param rj
+   */
+  private void markJobAsRunning(RestJob rj)
+  {
+    rj.statMessage = "<br>Job submitted successfully. Results available at this URL:\n"
+            + "<a href="
+            + rj.getJobId()
+            + "\">"
+            + rj.getJobId()
+            + "</a><br>";
+    rj.running = true;
+  }
+
+  /**
+   * extract the job ID URL from the redirect page. Does nothing if job is
+   * already running.
+   * 
+   * @param rj
+   * @param response
+   */
+  private void extractJobId(RestJob rj, HttpResponse response)
+  {
+    Header[] loc;
+    if (!rj.isSubmitted())
+    {
+
+      // redirect URL - typical for IBIVU type jobs.
+      if ((loc = response.getHeaders(HTTPConstants.HEADER_LOCATION)) != null
+              && loc.length > 0)
+      {
+        if (loc.length > 1)
+        {
+          Console.warn("Ignoring additional "
+                          + (loc.length - 1)
+                          + " location(s) provided in response header ( next one is '"
+                          + loc[1].getValue() + "' )");
+        }
+        rj.setJobId(loc[0].getValue());
+        rj.setSubmitted(true);
+      }
+    }
+  }
   /**
    * job has completed. Something valid should be available from con
    * 
@@ -1038,8 +1068,9 @@ public class RestJobThread extends AWSThread
       HiddenColumns destcs;
       String alTitle = MessageManager
               .formatMessage("label.webservice_job_title_on", new String[]
-              { restClient.service.details.Action,
-                  restClient.service.details.Name, restClient.viewTitle });
+              { restClient.service.details.getAction(),
+                  restClient.service.details.getName(),
+                  restClient.viewTitle });
       switch (action)
       {
       case newAlignment:
index 5533406..b07f3b6 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ws.rest;
 import jalview.datamodel.SequenceI;
 import jalview.io.packed.DataProvider.JvDataType;
 import jalview.util.StringUtils;
+import jalview.ws.api.UIinfo;
 import jalview.ws.rest.params.Alignment;
 import jalview.ws.rest.params.AnnotationFile;
 import jalview.ws.rest.params.SeqGroupIndexVector;
@@ -66,10 +67,7 @@ public class RestServiceDescription
           boolean vseparable, char gapCharacter)
   {
     super();
-    this.details = new UIinfo();
-    details.Action = action == null ? "" : action;
-    details.description = description == null ? "" : description;
-    details.Name = name == null ? "" : name;
+    this.details = new UIinfo(action, action, name, description, postUrl);
     this.postUrl = postUrl == null ? "" : postUrl;
     this.urlSuffix = urlSuffix == null ? "" : urlSuffix;
     if (inputParams != null)
@@ -98,56 +96,11 @@ public class RestServiceDescription
     // TODO - robust diff that includes constants and reordering of URL
     // diff |= !(postUrl.equals(other.postUrl));
     // diff |= !inputParams.equals(other.inputParams);
-    diff |= !details.Name.equals(other.details.Name);
-    diff |= !details.Action.equals(other.details.Action);
-    diff |= !details.description.equals(other.details.description);
+    diff |= !details.equals(other.details);
     return !diff;
   }
 
-  /**
-   * Service UI Info { Action, Specific Name of Service, Brief Description }
-   */
-
-  public class UIinfo
-  {
-    public String getAction()
-    {
-      return Action;
-    }
-
-    public void setAction(String action)
-    {
-      Action = action;
-    }
-
-    public String getName()
-    {
-      return Name;
-    }
-
-    public void setName(String name)
-    {
-      Name = name;
-    }
-
-    public String getDescription()
-    {
-      return description;
-    }
-
-    public void setDescription(String description)
-    {
-      this.description = description;
-    }
-
-    String Action;
-
-    String Name;
-
-    String description;
-  }
-
-  public UIinfo details = new UIinfo();
+  public UIinfo details;
 
   public String getAction()
   {
@@ -238,7 +191,7 @@ public class RestServiceDescription
   /**
    * input info given as key/value pairs - mapped to post arguments
    */
-  Map<String, InputType> inputParams = new HashMap<String, InputType>();
+  Map<String, InputType> inputParams = new HashMap<>();
 
   /**
    * assigns the given inputType it to its corresponding input parameter token
@@ -455,7 +408,7 @@ public class RestServiceDescription
     }
     StringTokenizer st = new StringTokenizer(outstring, ";");
     String tok = "";
-    resultData = new ArrayList<JvDataType>();
+    resultData = new ArrayList<>();
     while (st.hasMoreTokens())
     {
       try
@@ -479,7 +432,7 @@ public class RestServiceDescription
 
   private String getServiceIOProperties()
   {
-    ArrayList<String> vls = new ArrayList<String>();
+    ArrayList<String> vls = new ArrayList<>();
     if (isHseparable())
     {
       vls.add("hseparable");
@@ -496,17 +449,18 @@ public class RestServiceDescription
             ",");
   }
 
+  @Override
   public String toString()
   {
     StringBuffer result = new StringBuffer();
     result.append("|");
-    result.append(details.Name);
+    result.append(details.getName());
     result.append('|');
-    result.append(details.Action);
+    result.append(details.getAction());
     result.append('|');
-    if (details.description != null)
+    if (details.getDescription() != null)
     {
-      result.append(details.description);
+      result.append(details.getDescription());
     }
     ;
     // list job input flags
@@ -567,9 +521,8 @@ public class RestServiceDescription
     {
       p++;
     }
-    details.Name = list[p];
-    details.Action = list[p + 1];
-    details.description = list[p + 2];
+    String action = list[p + 1], name = list[p], descrip = list[p + 2];
+
     invalid |= !configureFromServiceInputProperties(list[p + 3], warnings);
     if (list.length - p > 5 && list[p + 5] != null
             && list[p + 5].trim().length() > 5)
@@ -589,6 +542,7 @@ public class RestServiceDescription
         p += 5;
       }
     }
+    details = new UIinfo(action, action, name, descrip, postUrl);
     return invalid ? -1 : p;
   }
 
@@ -667,7 +621,7 @@ public class RestServiceDescription
     int lastp = 0;
     String url = new String();
     Matcher prms = PARAM_ENCODED_URL_PATTERN.matcher(ipurl);
-    Map<String, InputType> iparams = new Hashtable<String, InputType>();
+    Map<String, InputType> iparams = new Hashtable<>();
     InputType jinput;
     while (prms.find())
     {
@@ -728,7 +682,7 @@ public class RestServiceDescription
         jinput = (InputType) (type.getConstructor().newInstance());
         if (iprm.equalsIgnoreCase(jinput.getURLtokenPrefix()))
         {
-          ArrayList<String> al = new ArrayList<String>();
+          ArrayList<String> al = new ArrayList<>();
           for (String prprm : StringUtils.separatorListToArray(iprmparams,
                   ","))
           {
@@ -838,7 +792,7 @@ public class RestServiceDescription
     return jobId + urlSuffix;
   }
 
-  private List<JvDataType> resultData = new ArrayList<JvDataType>();
+  private List<JvDataType> resultData = new ArrayList<>();
 
   /**
    * 
@@ -852,7 +806,7 @@ public class RestServiceDescription
   {
     if (resultData == null)
     {
-      resultData = new ArrayList<JvDataType>();
+      resultData = new ArrayList<>();
     }
     resultData.add(dt);
   }
@@ -883,7 +837,7 @@ public class RestServiceDescription
           String services) throws Exception
   {
     String[] list = StringUtils.separatorListToArray(services, "|");
-    List<RestServiceDescription> svcparsed = new ArrayList<RestServiceDescription>();
+    List<RestServiceDescription> svcparsed = new ArrayList<>();
     int p = 0, lastp = 0;
     StringBuffer warnings = new StringBuffer();
     do
diff --git a/src/jalview/ws/rest/clientdefs/ShmrRestClient.java b/src/jalview/ws/rest/clientdefs/ShmrRestClient.java
new file mode 100644 (file)
index 0000000..986d760
--- /dev/null
@@ -0,0 +1,54 @@
+package jalview.ws.rest.clientdefs;
+
+import jalview.io.packed.DataProvider.JvDataType;
+import jalview.util.MessageManager;
+import jalview.ws.rest.InputType;
+import jalview.ws.rest.RestClient;
+import jalview.ws.rest.RestServiceDescription;
+import java.util.Hashtable;
+
+public class ShmrRestClient
+{
+
+  public static RestClient makeShmmrRestClient()
+  {
+    String action = "Analysis", description = "Sequence Harmony and Multi-Relief (Brandt et al. 2010)", name = MessageManager
+            .getString("label.multiharmony");
+    Hashtable<String, InputType> iparams = new Hashtable<String, InputType>();
+    jalview.ws.rest.params.JobConstant toolp;
+    // toolp = new jalview.ws.rest.JobConstant("tool","jalview");
+    // iparams.put(toolp.token, toolp);
+    // toolp = new jalview.ws.rest.params.JobConstant("mbjob[method]","shmr");
+    // iparams.put(toolp.token, toolp);
+    // toolp = new
+    // jalview.ws.rest.params.JobConstant("mbjob[description]","step 1");
+    // iparams.put(toolp.token, toolp);
+    // toolp = new jalview.ws.rest.params.JobConstant("start_search","1");
+    // iparams.put(toolp.token, toolp);
+    // toolp = new jalview.ws.rest.params.JobConstant("blast","0");
+    // iparams.put(toolp.token, toolp);
+  
+    jalview.ws.rest.params.Alignment aliinput = new jalview.ws.rest.params.Alignment();
+    // SHMR server has a 65K limit for content pasted into the 'ali' parameter,
+    // so we always upload our files.
+    aliinput.token = "ali_file";
+    aliinput.writeAsFile = true;
+    iparams.put(aliinput.token, aliinput);
+    jalview.ws.rest.params.SeqGroupIndexVector sgroups = new jalview.ws.rest.params.SeqGroupIndexVector();
+    sgroups.setMinsize(2);
+    sgroups.min = 2;// need at least two group defined to make a partition
+    iparams.put("groups", sgroups);
+    sgroups.token = "groups";
+    sgroups.sep = " ";
+    RestServiceDescription shmrService = new RestServiceDescription(
+            action,
+            description,
+            name,
+            "http://zeus.few.vu.nl/programs/shmrwww/index.php?tool=jalview",// ?tool=jalview&mbjob[method]=shmr&mbjob[description]=step1",
+            "?tool=jalview", iparams, true, false, '-');
+    // a priori knowledge of the data returned from the service
+    shmrService.addResultDatatype(JvDataType.ANNOTATION);
+    return new RestClient(shmrService);
+  }
+
+}
index 0c707e5..892ebd8 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.ws.sifts;
 
-import java.util.Locale;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -41,6 +39,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -120,14 +119,16 @@ public class SiftsClient implements SiftsClientI
 
   private final static String NEWLINE = System.lineSeparator();
 
+  private static final boolean GET_STREAM = false;
+  private static final boolean CACHE_FILE = true;
   private String curSourceDBRef;
 
   private HashSet<String> curDBRefAccessionIdsString;
+  private boolean doCache = false;
 
   private enum CoordinateSys
   {
     UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe");
-
     private String name;
 
     private CoordinateSys(String name)
@@ -145,7 +146,6 @@ public class SiftsClient implements SiftsClientI
   {
     NAME_SEC_STRUCTURE("nameSecondaryStructure"),
     CODE_SEC_STRUCTURE("codeSecondaryStructure"), ANNOTATION("Annotation");
-
     private String code;
 
     private ResidueDetailType(String code)
@@ -170,8 +170,31 @@ public class SiftsClient implements SiftsClientI
   {
     this.pdb = pdb;
     this.pdbId = pdb.getId();
-    File siftsFile = getSiftsFile(pdbId);
-    siftsEntry = parseSIFTs(siftsFile);
+    if (doCache) {
+      File siftsFile = getSiftsFile(pdbId);
+      siftsEntry = parseSIFTs(siftsFile);
+    } else {
+      siftsEntry = parseSIFTSStreamFor(pdbId);
+    }
+  }
+
+  /**
+   * A more streamlined version of SIFT reading that allows for streaming of the data.
+   * 
+   * @param pdbId
+   * @return
+   * @throws SiftsException
+   */
+  private static Entry parseSIFTSStreamFor(String pdbId) throws SiftsException
+  {
+    try
+    {
+      InputStream is = (InputStream) downloadSifts(pdbId, GET_STREAM);
+      return parseSIFTs(is);
+    } catch (Exception e)
+    {
+      throw new SiftsException(e.getMessage());
+    }
   }
 
   /**
@@ -185,8 +208,17 @@ public class SiftsClient implements SiftsClientI
    */
   private Entry parseSIFTs(File siftFile) throws SiftsException
   {
-    try (InputStream in = new FileInputStream(siftFile);
-            GZIPInputStream gzis = new GZIPInputStream(in);)
+    try (InputStream in = new FileInputStream(siftFile)) {
+      return parseSIFTs(in);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      throw new SiftsException(e.getMessage());
+    }
+  }
+  
+  private static Entry parseSIFTs(InputStream in) throws Exception {
+    try (GZIPInputStream gzis = new GZIPInputStream(in);)
     {
       // System.out.println("File : " + siftFile.getAbsolutePath());
       JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.sifts");
@@ -195,10 +227,6 @@ public class SiftsClient implements SiftsClientI
       Unmarshaller um = jc.createUnmarshaller();
       JAXBElement<Entry> jbe = um.unmarshal(streamReader, Entry.class);
       return jbe.getValue();
-    } catch (Exception e)
-    {
-      e.printStackTrace();
-      throw new SiftsException(e.getMessage());
     }
   }
 
@@ -228,7 +256,7 @@ public class SiftsClient implements SiftsClientI
       // The line below is required for unit testing... don't comment it out!!!
       System.out.println(">>> SIFTS File already downloaded for " + pdbId);
 
-      if (isFileOlderThanThreshold(siftsFile,
+      if (Platform.isFileOlderThanThreshold(siftsFile,
               SiftsSettings.getCacheThresholdInDays()))
       {
         File oldSiftsFile = new File(siftsFileName + "_old");
@@ -261,35 +289,6 @@ public class SiftsClient implements SiftsClientI
   }
 
   /**
-   * This method enables checking if a cached file has exceeded a certain
-   * threshold(in days)
-   * 
-   * @param file
-   *          the cached file
-   * @param noOfDays
-   *          the threshold in days
-   * @return
-   */
-  public static boolean isFileOlderThanThreshold(File file, int noOfDays)
-  {
-    Path filePath = file.toPath();
-    BasicFileAttributes attr;
-    int diffInDays = 0;
-    try
-    {
-      attr = Files.readAttributes(filePath, BasicFileAttributes.class);
-      diffInDays = (int) ((new Date().getTime()
-              - attr.lastModifiedTime().toMillis())
-              / (1000 * 60 * 60 * 24));
-      // System.out.println("Diff in days : " + diffInDays);
-    } catch (IOException e)
-    {
-      e.printStackTrace();
-    }
-    return noOfDays <= diffInDays;
-  }
-
-  /**
    * Download a SIFTs XML file for a given PDB Id from an FTP repository
    * 
    * @param pdbId
@@ -299,52 +298,47 @@ public class SiftsClient implements SiftsClientI
    */
   public static File downloadSiftsFile(String pdbId)
           throws SiftsException, IOException
+  {    
+    return (File) downloadSifts(pdbId, CACHE_FILE);
+  }
+
+  /**
+   * Download SIFTs XML with the option to cache a file or to get a stream.
+   * 
+   * @param pdbId
+   * @param asFile 
+   * @return
+   * @throws IOException
+   */
+  private static Object downloadSifts(String pdbId, boolean asFile) throws IOException
   {
+    pdbId = pdbId.toLowerCase(Locale.ROOT);
     if (pdbId.contains(".cif"))
     {
       pdbId = pdbId.replace(".cif", "");
     }
     String siftFile = pdbId + ".xml.gz";
-    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
-
-    /*
-     * Download the file from URL to either
-     * Java: directory of cached downloaded SIFTS files
-     * Javascript: temporary 'file' (in-memory cache)
-     */
     File downloadTo = null;
-    if (Platform.isJS())
-    {
-      downloadTo = File.createTempFile(siftFile, ".xml.gz");
-    }
-    else
+    if (asFile)
     {
       downloadTo = new File(
               SiftsSettings.getSiftDownloadDirectory() + siftFile);
-      File siftsDownloadDir = new File(
-              SiftsSettings.getSiftDownloadDirectory());
+      File siftsDownloadDir = new File(SiftsSettings.getSiftDownloadDirectory());
       if (!siftsDownloadDir.exists())
       {
         siftsDownloadDir.mkdirs();
       }
     }
 
-    // System.out.println(">> Download ftp url : " + siftsFileFTPURL);
-    // long now = System.currentTimeMillis();
+    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
     URL url = new URL(siftsFileFTPURL);
     URLConnection conn = url.openConnection();
-    InputStream inputStream = conn.getInputStream();
-    FileOutputStream outputStream = new FileOutputStream(downloadTo);
-    byte[] buffer = new byte[BUFFER_SIZE];
-    int bytesRead = -1;
-    while ((bytesRead = inputStream.read(buffer)) != -1)
-    {
-      outputStream.write(buffer, 0, bytesRead);
-    }
-    outputStream.close();
-    inputStream.close();
-    // System.out.println(">>> File downloaded : " + downloadedSiftsFile
-    // + " took " + (System.currentTimeMillis() - now) + "ms");
+    InputStream is = conn.getInputStream();
+    if (!asFile)
+      return is;
+    // This is MUCH more efficent in JavaScript, as we already have the bytes
+    Platform.streamToFile(is, downloadTo);
+    is.close();
     return downloadTo;
   }
 
@@ -482,7 +476,7 @@ public class SiftsClient implements SiftsClientI
           SequenceI seq, java.io.PrintStream os) throws SiftsException
   {
     List<Integer> omitNonObserved = new ArrayList<>();
-    int nonObservedShiftIndex = 0, pdbeNonObserved = 0;
+    int nonObservedShiftIndex = 0,pdbeNonObserved=0;
     // System.out.println("Generating mappings for : " + entityId);
     Entity entity = null;
     entity = getEntityById(entityId);
@@ -515,7 +509,7 @@ public class SiftsClient implements SiftsClientI
     TreeMap<Integer, String> resNumMap = new TreeMap<Integer, String>();
     List<Segment> segments = entity.getSegment();
     SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap,
-            omitNonObserved, nonObservedShiftIndex, pdbeNonObserved);
+            omitNonObserved, nonObservedShiftIndex,pdbeNonObserved);
     processSegments(segments, shp);
     try
     {
@@ -537,20 +531,18 @@ public class SiftsClient implements SiftsClientI
     {
       throw new SiftsException("SIFTS mapping failed");
     }
-    // also construct a mapping object between the seq-coord sys and the PDB
-    // seq's coord sys
+    // also construct a mapping object between the seq-coord sys and the PDB seq's coord sys
 
     Integer[] keys = mapping.keySet().toArray(new Integer[0]);
     Arrays.sort(keys);
     seqStart = keys[0];
     seqEnd = keys[keys.length - 1];
-    List<int[]> from = new ArrayList<>(), to = new ArrayList<>();
-    int[] _cfrom = null, _cto = null;
+    List<int[]> from=new ArrayList<>(),to=new ArrayList<>();
+    int[]_cfrom=null,_cto=null;
     String matchedSeq = originalSeq;
-    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb
-                                // sequence that starts <-1
+    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb sequence that starts <-1
     {
-      for (int seqps : keys)
+      for (int seqps:keys)
       {
         int pdbpos = mapping.get(seqps)[PDBE_POS];
         if (pdbpos == UNASSIGNED)
@@ -558,23 +550,19 @@ public class SiftsClient implements SiftsClientI
           // not correct - pdbpos might be -1, but leave it for now
           continue;
         }
-        if (_cfrom == null || seqps != _cfrom[1] + 1)
+        if (_cfrom==null || seqps!=_cfrom[1]+1)
         {
-          _cfrom = new int[] { seqps, seqps };
+          _cfrom = new int[] { seqps,seqps};
           from.add(_cfrom);
           _cto = null; // discontinuity
+        } else {
+          _cfrom[1]= seqps;
         }
-        else
-        {
-          _cfrom[1] = seqps;
-        }
-        if (_cto == null || pdbpos != 1 + _cto[1])
+        if (_cto==null || pdbpos!=1+_cto[1])
         {
-          _cto = new int[] { pdbpos, pdbpos };
+          _cto = new int[] { pdbpos,pdbpos};
           to.add(_cto);
-        }
-        else
-        {
+        } else {
           _cto[1] = pdbpos;
         }
       }
@@ -596,7 +584,8 @@ public class SiftsClient implements SiftsClientI
       ;
 
       seqFromPdbMapping = new jalview.datamodel.Mapping(null, _cto, _cfrom,
-              1, 1);
+              1,
+              1);
       pdbStart = mapping.get(seqStart)[PDB_RES_POS];
       pdbEnd = mapping.get(seqEnd)[PDB_RES_POS];
       int orignalSeqStart = seq.getStart();
@@ -659,7 +648,7 @@ public class SiftsClient implements SiftsClientI
       for (Residue residue : residues)
       {
         boolean isObserved = isResidueObserved(residue);
-        int pdbeIndex = getLeadingIntegerValue(residue.getDbResNum(),
+        int pdbeIndex = Platform.getLeadingIntegerValue(residue.getDbResNum(),
                 UNASSIGNED);
         int currSeqIndex = UNASSIGNED;
         List<CrossRefDb> cRefDbs = residue.getCrossRefDb();
@@ -671,7 +660,7 @@ public class SiftsClient implements SiftsClientI
             pdbRefDb = cRefDb;
             if (firstPDBResNum == UNASSIGNED)
             {
-              firstPDBResNum = getLeadingIntegerValue(cRefDb.getDbResNum(),
+              firstPDBResNum = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(),
                       UNASSIGNED);
             }
             else
@@ -686,7 +675,7 @@ public class SiftsClient implements SiftsClientI
           if (cRefDb.getDbCoordSys().equalsIgnoreCase(seqCoordSys.getName())
                   && isAccessionMatched(cRefDb.getDbAccessionId()))
           {
-            currSeqIndex = getLeadingIntegerValue(cRefDb.getDbResNum(),
+            currSeqIndex = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(),
                     UNASSIGNED);
             if (pdbRefDb != null)
             {
@@ -727,18 +716,18 @@ public class SiftsClient implements SiftsClientI
         }
         // if (currSeqIndex >= seq.getStart() && currSeqIndex <= seqlength) //
         // true
-        // numbering
-        // is
-        // not
-        // up
-        // to
-        // seq.getEnd()
+                                                                         // numbering
+                                                                         // is
+                                                                         // not
+                                                                         // up
+                                                                         // to
+                                                                         // seq.getEnd()
         {
 
           int resNum = (pdbRefDb == null)
-                  ? getLeadingIntegerValue(residue.getDbResNum(),
+                  ? Platform.getLeadingIntegerValue(residue.getDbResNum(),
                           UNASSIGNED)
-                  : getLeadingIntegerValue(pdbRefDb.getDbResNum(),
+                  : Platform.getLeadingIntegerValue(pdbRefDb.getDbResNum(),
                           UNASSIGNED);
 
           if (isObserved)
@@ -759,29 +748,6 @@ public class SiftsClient implements SiftsClientI
   }
 
   /**
-   * Get the leading integer part of a string that begins with an integer.
-   * 
-   * @param input
-   *          - the string input to process
-   * @param failValue
-   *          - value returned if unsuccessful
-   * @return
-   */
-  static int getLeadingIntegerValue(String input, int failValue)
-  {
-    if (input == null)
-    {
-      return failValue;
-    }
-    String[] parts = input.split("(?=\\D)(?<=\\d)");
-    if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+"))
-    {
-      return Integer.valueOf(parts[0]);
-    }
-    return failValue;
-  }
-
-  /**
    * 
    * @param chainId
    *          Target chain to populate mapping of its atom positions.
@@ -1073,7 +1039,6 @@ public class SiftsClient implements SiftsClientI
     {
       return pdbeNonObserved;
     }
-
     public SequenceI getSeq()
     {
       return seq;
index 5e2c526..1c25a34 100644 (file)
@@ -22,57 +22,76 @@ package jalview.ws.sifts;
 
 import java.util.Objects;
 
-public class SiftsSettings
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
+public class SiftsSettings implements ApplicationSingletonI
 {
-  private static boolean mapWithSifts = false;
+  /**
+   * Constructor
+   * 
+   * @return
+   */
+  private static SiftsSettings getInstance()
+  {
+    return (SiftsSettings) ApplicationSingletonProvider
+            .getInstance(SiftsSettings.class);
+  }
+
+  private SiftsSettings()
+  {
+    // singleton; use getInstance()
+  }
+
+  private boolean mapWithSifts = false;
 
-  private static String siftDownloadDirectory;
+  private String siftDownloadDirectory;
 
-  private static int cacheThresholdInDays;
+  private int cacheThresholdInDays;
 
-  private static int failSafePIDThreshold;
+  private int failSafePIDThreshold;
 
   public static boolean isMapWithSifts()
   {
-    return mapWithSifts;
+    return getInstance().mapWithSifts;
   }
 
   public static void setMapWithSifts(boolean mapWithSifts)
   {
-    SiftsSettings.mapWithSifts = mapWithSifts;
+    getInstance().mapWithSifts = mapWithSifts;
   }
 
   public static String getSiftDownloadDirectory()
   {
-    return siftDownloadDirectory;
+    return getInstance().siftDownloadDirectory;
   }
 
   public static void setSiftDownloadDirectory(String siftDownloadDirectory)
   {
-    SiftsSettings.siftDownloadDirectory = siftDownloadDirectory;
+    getInstance().siftDownloadDirectory = siftDownloadDirectory;
   }
 
   public static int getCacheThresholdInDays()
   {
-    return cacheThresholdInDays;
+    return getInstance().cacheThresholdInDays;
   }
 
   public static void setCacheThresholdInDays(String cacheThresholdInDays)
   {
     Objects.requireNonNull(cacheThresholdInDays);
-    SiftsSettings.cacheThresholdInDays = Integer
+    getInstance().cacheThresholdInDays = Integer
             .valueOf(cacheThresholdInDays);
   }
 
   public static int getFailSafePIDThreshold()
   {
-    return failSafePIDThreshold;
+    return getInstance().failSafePIDThreshold;
   }
 
   public static void setFailSafePIDThreshold(String failSafePIDThreshold)
   {
     Objects.requireNonNull(failSafePIDThreshold);
-    SiftsSettings.failSafePIDThreshold = Integer
+    getInstance().failSafePIDThreshold = Integer
             .valueOf(failSafePIDThreshold);
   }
 }
diff --git a/src/jalview/ws/slivkaws/RNAalifoldServiceInstance.java b/src/jalview/ws/slivkaws/RNAalifoldServiceInstance.java
new file mode 100644 (file)
index 0000000..7940d06
--- /dev/null
@@ -0,0 +1,23 @@
+package jalview.ws.slivkaws;
+
+import jalview.util.MessageManager;
+import jalview.ws.uimodel.AlignAnalysisUIText;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class RNAalifoldServiceInstance
+        extends SlivkaAnnotationServiceInstance
+{
+  public RNAalifoldServiceInstance(SlivkaClient client,
+          SlivkaService service, String category)
+  {
+    super(client, service, category);
+    setAlignAnalysisUI(new AlignAnalysisUIText(getName(),
+            RNAalifoldServiceInstance.class,
+            "Slivka.RNAalifold", true, false, true, true, false, 2,
+            MessageManager.getString("label.rnalifold_calculations"),
+            MessageManager.getString("tooltip.rnalifold_calculations"),
+            MessageManager.getString("label.rnalifold_settings"),
+            MessageManager.getString("tooltip.rnalifold_settings")));
+  }
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaAnnotationServiceInstance.java b/src/jalview/ws/slivkaws/SlivkaAnnotationServiceInstance.java
new file mode 100644 (file)
index 0000000..999951a
--- /dev/null
@@ -0,0 +1,104 @@
+package jalview.ws.slivkaws;
+
+import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.io.AnnotationFile;
+import jalview.io.DataSourceType;
+import jalview.io.FeaturesFile;
+import jalview.util.MessageManager;
+import jalview.ws.api.JobId;
+import jalview.ws.api.SequenceAnnotationServiceI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+import jalview.ws.uimodel.AlignAnalysisUIText;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.msa.Category;
+import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class SlivkaAnnotationServiceInstance extends SlivkaWSInstance implements SequenceAnnotationServiceI
+{
+  public SlivkaAnnotationServiceInstance(SlivkaClient client,
+          SlivkaService service, String category)
+  {
+    super(client, service, category);
+    if (category == Category.CATEGORY_CONSERVATION)
+    {
+      /* FIXME: the category name is hardcoded for AACon, names other than
+       * "AAConWS" doesn't work. */
+      setAlignAnalysisUI(new AlignAnalysisUIText(getName(),
+              SlivkaAnnotationServiceInstance.class,
+              "Slivka.AACons", false, true, true, true, true, 2,
+              MessageManager.getString("label.aacon_calculations"),
+              MessageManager.getString("tooltip.aacon_calculations"),
+              MessageManager.getString("label.aacon_settings"),
+              MessageManager.getString("tooltip.aacon_settings")));
+    }
+    style = ServiceClient.SEQUENCEANNOTATIONWSCLIENT;
+  }
+
+  @Override
+  public JobId submitToService(List<SequenceI> seqs, WsParamSetI preset, List<ArgumentI> paramset) throws Throwable
+  {
+    return super.submit(seqs, preset, paramset);
+  }
+
+  @Override
+  public List<AlignmentAnnotation> getAnnotationResult(JobId jobId,
+          List<SequenceI> seqs, Map<String, FeatureColourI> featureColours,
+          Map<String, FeatureMatcherSetI> featureFilters) throws Throwable
+  {
+    RemoteFile annotFile = null;
+    RemoteFile featFile = null;
+    try
+    {
+      var slivkaJob = client.getJob(jobId.getJobId());
+      Collection<RemoteFile> files = slivkaJob.getResults();
+      for (RemoteFile f : files)
+      {
+        if (f.getMediaType().equals("application/jalview-annotations"))
+        {
+          annotFile = f;
+        }
+        else if (f.getMediaType().equals("application/jalview-features"))
+        {
+          featFile = f;
+        }
+      }
+    } catch (IOException e)
+    {
+      throw new IOError(e);
+    }
+    Alignment aln = new Alignment(seqs.toArray(new SequenceI[0]));
+    if (annotFile == null
+        || !new AnnotationFile().readAnnotationFileWithCalcId(aln, service.getId(), annotFile.getContentUrl().toString(), DataSourceType.URL))
+    {
+      Console.debug("No annotation from slivka job\n" + annotFile);
+    }
+    else {
+      Console.debug("Annotation file loaded " + annotFile);
+    }
+    if (featFile == null
+        || !new FeaturesFile(featFile.getContentUrl().toString(), DataSourceType.URL).parse(aln, featureColours, true))
+    {
+      Console.debug("No features from slivka job\n" + featFile);
+    }
+    else {
+      Console.debug("Features feil loaded " + featFile);
+    }
+    return Arrays.asList(aln.getAlignmentAnnotation());
+  }
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaDatastore.java b/src/jalview/ws/slivkaws/SlivkaDatastore.java
new file mode 100644 (file)
index 0000000..8bd446f
--- /dev/null
@@ -0,0 +1,88 @@
+package jalview.ws.slivkaws;
+
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.WsParamSetI;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class SlivkaDatastore implements ParamDatastoreI
+{
+  private SlivkaParamSet defaultPreset;
+  private List<WsParamSetI> presets = new ArrayList<>();
+
+  public SlivkaDatastore(SlivkaService service) {
+    defaultPreset = new SlivkaParamSet(service);
+  }
+
+  @Override
+  public List<WsParamSetI> getPresets()
+  {
+    return presets;
+  }
+
+  @Override
+  public WsParamSetI getPreset(String name)
+  {
+    for (WsParamSetI preset : presets)
+    {
+      if (preset.getName().equals(name))
+      {
+        return preset;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<ArgumentI> getServiceParameters()
+  {
+    return new ArrayList<>(defaultPreset.getArguments());
+  }
+
+  @Override
+  public boolean presetExists(String name)
+  {
+    for (WsParamSetI preset : presets)
+    {
+      if (preset.getName().equals(name))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public void deletePreset(String name)
+  {
+  }
+
+  @Override
+  public void storePreset(String presetName, String text, List<ArgumentI> jobParams)
+  {
+  }
+
+  @Override
+  public void updatePreset(String oldName, String presetName, String text, List<ArgumentI> jobParams)
+  {
+  }
+
+  @Override
+  public WsParamSetI parseServiceParameterFile(String name, String description, String[] serviceURL, String parameters)
+      throws IOException
+  {
+    return null;
+  }
+
+  @Override
+  public String generateServiceParameterFile(WsParamSetI pset) throws IOException
+  {
+    return null;
+  }
+
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaMsaServiceInstance.java b/src/jalview/ws/slivkaws/SlivkaMsaServiceInstance.java
new file mode 100644 (file)
index 0000000..374d2eb
--- /dev/null
@@ -0,0 +1,62 @@
+package jalview.ws.slivkaws;
+
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FormatAdapter;
+import jalview.ws.api.JobId;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.WsParamSetI;
+import java.io.IOError;
+import java.io.IOException;
+import java.rmi.ServerError;
+import java.util.Collection;
+import java.util.List;
+
+import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class SlivkaMsaServiceInstance extends SlivkaWSInstance implements MultipleSequenceAlignmentI
+{
+  SlivkaMsaServiceInstance(SlivkaClient client, SlivkaService service, String category) {
+    super(client, service, category);
+    style = ServiceClient.MSAWSCLIENT;
+  }
+
+  @Override
+  public JobId align(List<SequenceI> toalign, WsParamSetI parameters, List<ArgumentI> list) throws Throwable
+  {
+    return super.submit(toalign, parameters, list);
+  }
+
+  @Override
+  public AlignmentI getAlignmentFor(JobId jobId) throws InvalidArgumentException, ServerError, IOError
+  {
+    Collection<RemoteFile> files;
+    try
+    {
+      var slivkaJob = client.getJob(jobId.getJobId());
+      files = slivkaJob.getResults();
+      for (RemoteFile f : files)
+      {
+        if (f.getMediaType().equals("application/clustal"))
+        {
+          return new FormatAdapter().readFile(f.getContentUrl().toString(), DataSourceType.URL, FileFormat.Clustal);
+        }
+        else if (f.getMediaType().equals("application/fasta"))
+        {
+          return new FormatAdapter().readFile(f.getContentUrl().toString(), DataSourceType.URL, FileFormat.Fasta);
+        }
+      }
+    } catch (IOException e)
+    {
+      throw new IOError(e);
+    }
+    return null;
+  }
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaParamSet.java b/src/jalview/ws/slivkaws/SlivkaParamSet.java
new file mode 100644 (file)
index 0000000..c8b99db
--- /dev/null
@@ -0,0 +1,151 @@
+package jalview.ws.slivkaws;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+import jalview.ws.params.simple.BooleanOption;
+import jalview.ws.params.simple.DoubleParameter;
+import jalview.ws.params.simple.IntegerParameter;
+import jalview.ws.params.simple.StringParameter;
+import uk.ac.dundee.compbio.slivkaclient.Parameter;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class SlivkaParamSet implements WsParamSetI
+{
+  private SlivkaService service;
+
+  private List<ArgumentI> args = new ArrayList<>();
+
+  SlivkaParamSet(SlivkaService service)
+  {
+    this.service = service;
+    for (Parameter param : service.getParameters())
+    {
+      Object defaultValue = param.getDefault() instanceof List
+          ? ((List<?>) param.getDefault()).get(0)
+          : param.getDefault();
+      if (param instanceof Parameter.FlagParameter)
+      {
+        args.add(new BooleanOption(param.getId(), param.getDescription(),
+            param.getName(), param.isRequired(), (Boolean) defaultValue, null));
+      }
+      else if (param instanceof Parameter.TextParameter)
+      {
+        args.add(new StringParameter(param.getId(), param.getDescription(),
+            param.isRequired(), (String) defaultValue, (String) defaultValue));
+      }
+      else if (param instanceof Parameter.IntegerParameter)
+      {
+        Integer min = ((Parameter.IntegerParameter) param).getMin();
+        Integer max = ((Parameter.IntegerParameter) param).getMax();
+        Integer defVal = defaultValue != null
+            ? ((Number) defaultValue).intValue()
+            : null;
+        args.add(new IntegerParameter(param.getId(), param.getDescription(),
+            param.isRequired(), defVal, (min == null) ? Integer.MIN_VALUE : min,
+            (max == null) ? Integer.MAX_VALUE : max));
+      }
+      else if (param instanceof Parameter.DecimalParameter)
+      {
+        Double min = ((Parameter.DecimalParameter) param).getMin();
+        Double max = ((Parameter.DecimalParameter) param).getMax();
+        Double defVal = defaultValue != null
+            ? ((Number) defaultValue).doubleValue()
+            : null;
+        args.add(new DoubleParameter(param.getId(), param.getDescription(),
+            param.isRequired(), defVal, (min == null) ? -Double.MAX_VALUE : min,
+            (max == null) ? Double.MAX_VALUE : max));
+      }
+      else if (param instanceof Parameter.ChoiceParameter)
+      {
+        List<String> choices = ((Parameter.ChoiceParameter) param)
+            .getChoices();
+        if (param.isArray())
+        {
+          int i = 0;
+          List<?> selected = param.getDefault() != null
+              ? (List<?>) param.getDefault()
+              : Collections.EMPTY_LIST;
+          for (String choice : choices)
+          {
+            args.add(new BooleanOption(
+                String.format("%s$%d", param.getId(), i++),
+                param.getDescription(), choice, param.isRequired(),
+                selected.contains(choice), choice, null));
+          }
+        }
+        else
+        {
+          args.add(new StringParameter(
+              param.getId(), param.getDescription(),
+              param.isRequired(), (String) param.getDefault(),
+              (String) defaultValue, choices, choices));
+        }
+      }
+      else if (param instanceof Parameter.FileParameter)
+      {
+        // skip: files are provided from sequences
+      }
+      else
+      {
+        String defaultVal = param.getDefault() != null
+            ? param.getDefault().toString()
+            : null;
+        args.add(new StringParameter(param.getId(), param.getDescription(),
+            param.isRequired(), defaultVal, defaultVal));
+      }
+    }
+  }
+
+  @Override
+  public String getName()
+  {
+    return "Default";
+  }
+
+  @Override
+  public String getDescription()
+  {
+    return service.getDescription();
+  }
+
+  @Override
+  public String[] getApplicableUrls()
+  {
+    return new String[] { service.getUrl().toString() };
+  }
+
+  @Override
+  public String getSourceFile()
+  {
+    return null;
+  }
+
+  @Override
+  public void setSourceFile(String newfile)
+  {
+  }
+
+  @Override
+  public boolean isModifiable()
+  {
+    return true;
+  }
+
+  @Override
+  public List<ArgumentI> getArguments()
+  {
+    return args;
+  }
+
+  @Override
+  public void setArguments(List<ArgumentI> args)
+  {
+    throw new RuntimeException();
+  }
+
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaWSDiscoverer.java b/src/jalview/ws/slivkaws/SlivkaWSDiscoverer.java
new file mode 100644 (file)
index 0000000..d21d5d1
--- /dev/null
@@ -0,0 +1,238 @@
+package jalview.ws.slivkaws;
+
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.ws.ServiceChangeListener;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.api.ServiceWithParameters;
+import javajs.http.HttpClientFactory;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import compbio.data.msa.Category;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public class SlivkaWSDiscoverer implements WSDiscovererI
+{
+  private static final String SLIVKA_HOST_URLS = "SLIVKAHOSTURLS";
+
+  private static final String COMPBIO_SLIVKA = "https://www.compbio.dundee.ac.uk/slivka/";
+
+  private static SlivkaWSDiscoverer instance = null;
+
+  private List<ServiceWithParameters> services = List.of();
+
+  private SlivkaWSDiscoverer()
+  {
+  }
+
+  public static SlivkaWSDiscoverer getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new SlivkaWSDiscoverer();
+    }
+    return instance;
+  }
+
+  private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
+
+  @Override
+  public void addServiceChangeListener(ServiceChangeListener l)
+  {
+    serviceListeners.add(l);
+  }
+
+  @Override
+  public void removeServiceChangeListener(ServiceChangeListener l)
+  {
+    serviceListeners.remove(l);
+  }
+
+  public void notifyServiceListeners(List<ServiceWithParameters> services)
+  {
+    for (var listener : serviceListeners)
+    {
+      listener.servicesChanged(this, services);
+    }
+  }
+
+  private final ExecutorService executor = Executors
+          .newSingleThreadExecutor();
+
+  private Vector<Future<?>> discoveryTasks = new Vector<>();
+
+  public CompletableFuture<WSDiscovererI> startDiscoverer()
+  {
+    CompletableFuture<WSDiscovererI> task = CompletableFuture
+            .supplyAsync(() -> {
+              reloadServices();
+              return SlivkaWSDiscoverer.this;
+            }, executor);
+    discoveryTasks.add(task);
+    return task;
+  }
+
+  private List<ServiceWithParameters> reloadServices()
+  {
+    Console.info("Reloading Slivka services");
+    notifyServiceListeners(Collections.emptyList());
+    ArrayList<ServiceWithParameters> instances = new ArrayList<>();
+
+    for (String url : getServiceUrls())
+    {
+      SlivkaClient client = new SlivkaClient(url);
+
+      List<SlivkaService> services;
+      try
+      {
+        services = client.getServices();
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+        continue;
+      }
+      for (SlivkaService service : services)
+      {
+        SlivkaWSInstance newInstance = null;
+        for (String classifier : service.classifiers)
+        {
+          String[] path = classifier.split("\\s*::\\s*");
+          if (path.length >= 3 && path[0].toLowerCase().equals("operation")
+                  && path[1].toLowerCase().equals("analysis"))
+          {
+            switch (path[path.length - 1].toLowerCase())
+            {
+            case "rna secondary structure prediction":
+              newInstance = new RNAalifoldServiceInstance(client,
+                      service, "Secondary Structure Prediction");
+              break;
+            case "sequence alignment analysis (conservation)":
+              newInstance = new SlivkaAnnotationServiceInstance(client,
+                      service, Category.CATEGORY_CONSERVATION);
+              break;
+            case "protein sequence analysis":
+              newInstance = new SlivkaAnnotationServiceInstance(client,
+                      service, Category.CATEGORY_DISORDER);
+              break;
+            case "protein secondary structure prediction":
+              newInstance = new SlivkaAnnotationServiceInstance(client,
+                      service, "Secondary Structure Prediction");
+              break;
+            case "multiple sequence alignment":
+              newInstance = new SlivkaMsaServiceInstance(client, service,
+                      Category.CATEGORY_ALIGNMENT);
+              break;
+            }
+          }
+          if (newInstance != null)
+            break;
+        }
+        if (newInstance != null)
+          instances.add(newInstance);
+      }
+    }
+
+    services = instances;
+    Console.info("Slivka services reloading finished");
+    notifyServiceListeners(instances);
+    return instances;
+  }
+
+  @Override
+  public List<ServiceWithParameters> getServices()
+  {
+    return services;
+  }
+
+  @Override
+  public boolean hasServices()
+  {
+    return !isRunning() && services.size() > 0;
+  }
+
+  @Override
+  public boolean isRunning()
+  {
+    return !discoveryTasks.stream().allMatch(Future::isDone);
+  }
+
+  @Override
+  public void setServiceUrls(List<String> wsUrls)
+  {
+    if (wsUrls != null && !wsUrls.isEmpty())
+    {
+      Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls));
+    }
+    else
+    {
+      Cache.removeProperty(SLIVKA_HOST_URLS);
+    }
+  }
+
+  @Override
+  public List<String> getServiceUrls()
+  {
+    String surls = Cache.getDefault(SLIVKA_HOST_URLS, COMPBIO_SLIVKA);
+    String[] urls = surls.split(",");
+    ArrayList<String> valid = new ArrayList<>(urls.length);
+    for (String url : urls)
+    {
+      try
+      {
+        new URL(url);
+        valid.add(url);
+      } catch (MalformedURLException e)
+      {
+        Console.warn("Problem whilst trying to make a URL from '"
+                + ((url != null) ? url : "<null>") + "'");
+        Console.warn(
+                "This was probably due to a malformed comma separated list"
+                        + " in the " + SLIVKA_HOST_URLS
+                        + " entry of $(HOME)/.jalview_properties)");
+        Console.debug("Exception was ", e);
+      }
+    }
+    return valid;
+  }
+
+  @Override
+  public boolean testServiceUrl(URL url)
+  {
+    return getServerStatusFor(url.toString()) == STATUS_OK;
+  }
+
+  @Override
+  public int getServerStatusFor(String url)
+  {
+    try
+    {
+      List<?> services = new SlivkaClient(url).getServices();
+      return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
+    } catch (IOException | org.json.JSONException e)
+    {
+      Console.error("Slivka could not retrieve services list", e);
+      return STATUS_INVALID;
+    }
+  }
+
+  @Override
+  public String getErrorMessages()
+  {
+    // TODO Auto-generated method stub
+    return "";
+  }
+}
diff --git a/src/jalview/ws/slivkaws/SlivkaWSInstance.java b/src/jalview/ws/slivkaws/SlivkaWSInstance.java
new file mode 100644 (file)
index 0000000..613c702
--- /dev/null
@@ -0,0 +1,268 @@
+package jalview.ws.slivkaws;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.WebserviceInfo;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FormatAdapter;
+import jalview.ws.api.JalviewServiceEndpointProviderI;
+import jalview.ws.api.JalviewWebServiceI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.gui.WsJob;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.ParamManager;
+import jalview.ws.params.WsParamSetI;
+import javajs.http.ClientProtocolException;
+
+import java.util.Collection;
+import uk.ac.dundee.compbio.slivkaclient.Job;
+import uk.ac.dundee.compbio.slivkaclient.JobRequest;
+import uk.ac.dundee.compbio.slivkaclient.Parameter;
+import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
+import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
+
+public abstract class SlivkaWSInstance extends ServiceWithParameters
+    implements JalviewServiceEndpointProviderI, JalviewWebServiceI
+{
+  protected final SlivkaClient client;
+
+  protected final SlivkaService service;
+
+  protected SlivkaDatastore store = null;
+
+  protected static final EnumMap<Job.Status, WsJob.JobState> stateMap = new EnumMap<>(Job.Status.class);
+  {
+    stateMap.put(Job.Status.PENDING, WsJob.JobState.QUEUED);
+    stateMap.put(Job.Status.REJECTED, WsJob.JobState.INVALID);
+    stateMap.put(Job.Status.ACCEPTED, WsJob.JobState.QUEUED);
+    stateMap.put(Job.Status.QUEUED, WsJob.JobState.QUEUED);
+    stateMap.put(Job.Status.RUNNING, WsJob.JobState.RUNNING);
+    stateMap.put(Job.Status.COMPLETED, WsJob.JobState.FINISHED);
+    stateMap.put(Job.Status.INTERRUPTED, WsJob.JobState.CANCELLED);
+    stateMap.put(Job.Status.DELETED, WsJob.JobState.CANCELLED);
+    stateMap.put(Job.Status.FAILED, WsJob.JobState.FAILED);
+    stateMap.put(Job.Status.ERROR, WsJob.JobState.SERVERERROR);
+    stateMap.put(Job.Status.UNKNOWN, WsJob.JobState.UNKNOWN);
+  }
+  protected final Set<WsJob.JobState> failedStates = new HashSet<>(Arrays.asList(
+      WsJob.JobState.INVALID, WsJob.JobState.BROKEN, WsJob.JobState.FAILED,
+      WsJob.JobState.SERVERERROR, WsJob.JobState.CANCELLED
+  ));
+
+  public SlivkaWSInstance(SlivkaClient client, SlivkaService service, String action)
+  {
+    super(action, action, service.getName(), "Slivka", client.getUrl().toString());
+    this.client = client;
+    this.service = service;
+  }
+
+  protected final JobId submit(List<SequenceI> sequences,
+          WsParamSetI preset, List<ArgumentI> args) throws Throwable
+  {
+    var parameters = service.getParameters();
+    var request = new JobRequest();
+    for (Parameter param : parameters)
+    {
+      if (param instanceof Parameter.FileParameter)
+      {
+        FormatAdapter fa = new FormatAdapter();
+        fa.setNewlineString("\r\n");
+        Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
+        FileFormat format;
+        switch (fileParam.getMediaType())
+        {
+        case "application/pfam":
+          format = FileFormat.Pfam;
+          break;
+        case "application/stockholm":
+          format = FileFormat.Stockholm;
+          break;
+        default:
+        case "application/fasta":
+          format = FileFormat.Fasta;
+          break;
+        }
+        
+        // we avoid any use of Jalview's user facing export routines here
+        
+        InputStream stream = new ByteArrayInputStream(format.getWriter(null)
+                .print(sequences.toArray(new SequenceI[0]), false)
+                .getBytes());
+        request.addFile(param.getId(), stream);
+      }
+    }
+    if (args != null)
+    {
+      for (ArgumentI arg : args)
+      {
+        // multiple choice field names are name$number to avoid duplications
+        // the number is stripped here
+        String paramId = arg.getName().split("\\$", 2)[0];
+        Parameter param = service.getParameter(paramId);
+        if (param instanceof Parameter.FlagParameter) {
+          if (arg.getValue() != null && !arg.getValue().isBlank())
+            request.addData(paramId, true);
+          else
+            request.addData(paramId, false);
+        }
+        else
+        {
+          request.addData(paramId, arg.getValue());
+        }
+      }
+    }
+    var job = service.submitJob(request);
+    return new JobId(service.getName(), service.getName(), job.getId());
+  }
+
+  @Override
+  public final void updateStatus(WsJob job)
+  {
+    try
+    {
+      var slivkaJob = client.getJob(job.getJobId());
+      job.setState(stateMap.get(slivkaJob.getStatus()));
+    } catch (IOException e)
+    {
+      throw new IOError(e);
+    }
+  }
+
+  @Override
+  public final boolean updateJobProgress(WsJob job) throws IOException
+  {      
+    var slivkaJob = client.getJob(job.getJobId());
+    Collection<RemoteFile> files = slivkaJob.getResults();
+    RemoteFile logFile=null;
+    for (RemoteFile f : files)
+    {
+      if (f.getLabel().equals("log"))
+      {
+        logFile = f; break;
+      }
+    }
+
+    boolean newContent = false;
+    if (logFile!=null)
+    {
+      ByteArrayOutputStream output = new ByteArrayOutputStream();
+      logFile.writeTo(output);
+      if (output.size() > job.getNextChunk())
+      {
+        newContent = true;
+        job.setStatus(output.toString("UTF-8"));
+        job.setnextChunk(output.size());
+      }
+    }
+    if (failedStates.contains(job.getJobState()))
+    {
+      
+      RemoteFile errLogFile = null;
+      for (RemoteFile f : files)
+      {
+        if (f.getLabel().equals("error-log"))
+        {
+          errLogFile = f;
+          break;
+        }
+      }
+
+      if (errLogFile!=null)
+      {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        errLogFile.writeTo(output);
+        if (output.size() > 0)
+        {
+          newContent = true;
+          job.setStatus(job.getStatus() + "\n" + output.toString("UTF-8"));
+        }
+      }
+    }
+    return newContent;
+  }
+
+  @Override
+  public final boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
+  {
+    if (_lex instanceof ClientProtocolException)
+    {
+      j.setState(WsJob.JobState.INVALID);
+      j.setStatus(_lex.getMessage());
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public final boolean handleCollectionException(Exception e, WsJob msjob, WebserviceInfo wsInfo)
+  {
+    // TODO
+    return false;
+  }
+
+  final SlivkaService getService()
+  {
+    return service;
+  }
+
+  @Override
+  public final Object getEndpoint()
+  {
+    return this;
+  }
+
+  @Override
+  public final void initParamStore(ParamManager userParameterStore)
+  {
+    if (store == null)
+    {
+      store = new SlivkaDatastore(service);
+    }
+  }
+
+  @Override
+  public boolean hasParameters()
+  {
+    return true;
+  }
+
+  @Override
+  public final ParamDatastoreI getParamStore()
+  {
+    if (store == null)
+    {
+      initParamStore(null);
+    }
+    return store;
+  }
+  
+  public static AlignmentI readAlignment(RemoteFile f) throws IOException
+  {
+    final var mimetype = f.getMediaType();
+    FileFormat format;
+    if (mimetype.equals("application/clustal"))
+      format = FileFormat.Clustal;
+    else if (mimetype.equals("application/fasta"))
+      format = FileFormat.Fasta;
+    else
+      return null;
+    return new FormatAdapter().readFile(f.getContentUrl().toString(),
+        DataSourceType.URL, format);
+  }
+
+}
index 9518eaa..c0bef07 100644 (file)
  */
 package jalview.ws.uimodel;
 
+/**
+ * configures annotation worker style web service clients
+ * 
+ * @author jprocter
+ *
+ */
 public class AlignAnalysisUIText
 {
 
@@ -54,6 +60,9 @@ public class AlignAnalysisUIText
     return isPr;
   }
 
+  /**
+   * @return true if service can accept sequences with gaps
+   */
   public boolean isAA()
   {
     return isAA;
@@ -63,9 +72,19 @@ public class AlignAnalysisUIText
 
   private boolean isAA;
 
+  private boolean filterSymbols;
+
+
+  private boolean needsAlignedSeqs;
+
+  private int min_valid_seqs;
+
+
   public AlignAnalysisUIText(String serviceType, Class<?> client,
           String calcId, boolean acceptNucl, boolean acceptProt,
-          boolean acceptGaps, String toggle, String toggleTooltip,
+          boolean acceptGaps, boolean alignedSeq,
+          boolean filterNonStandardSymbols, int minSeq, String toggle,
+          String toggleTooltip,
           String settings, String settingsTooltip)
   {
     this.serviceType = serviceType;
@@ -73,11 +92,35 @@ public class AlignAnalysisUIText
     isNa = acceptNucl;
     isPr = acceptProt;
     isAA = acceptGaps;
+    this.needsAlignedSeqs = alignedSeq;
+    this.filterSymbols = filterNonStandardSymbols;
     this.client = client;
     this.AAconToggle = toggle;
     this.AAconToggleTooltip = toggleTooltip;
     this.AAeditSettings = settings;
     this.AAeditSettingsTooltip = settingsTooltip;
+    this.min_valid_seqs = minSeq;
+  }
+
+  /**
+   * 
+   * @return true if non-standard nucleotides and amino acids should be replaced
+   *         or omitted
+   * 
+   */
+  public boolean isFilterSymbols()
+  {
+    return filterSymbols;
+  }
+
+  /**
+   * 
+   * @return true if service needs sequences all the same length (ie padded with
+   *         gaps if necessary)
+   */
+  public boolean isNeedsAlignedSeqs()
+  {
+    return needsAlignedSeqs;
   }
 
   public Class getClient()
@@ -130,4 +173,9 @@ public class AlignAnalysisUIText
     AAeditSettingsTooltip = aAeditSettingsTooltip;
   }
 
+  public int getMinimumSequences()
+  {
+    return min_valid_seqs;
+  }
+
 }
index e2fb1b8..a00e8b3 100644 (file)
 
 package jalview.ws.utils;
 
-import jalview.util.Platform;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.net.URL;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
+import jalview.util.Platform;
 
 public class UrlDownloadClient
 {
+  public UrlDownloadClient()
+  {
+
+  }
+
   /**
    * Download and save a file from a URL
    * 
@@ -49,67 +46,11 @@ public class UrlDownloadClient
           throws IOException
   {
 
-    FileOutputStream fos = null;
-    ReadableByteChannel rbc = null;
-    Path temp = null;
-    try
-    {
-      temp = Files.createTempFile(".jalview_", ".tmp");
-
-      URL url = new URL(urlstring);
-      rbc = Channels.newChannel(url.openStream());
-      fos = new FileOutputStream(temp.toString());
-      fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
-
-      // copy tempfile to outfile once our download completes
-      // incase something goes wrong
-      Files.copy(temp, Paths.get(outfile),
-              StandardCopyOption.REPLACE_EXISTING);
-    } catch (IOException e)
-    {
-      throw e;
-    } finally
-    {
-      try
-      {
-        if (fos != null)
-        {
-          fos.close();
-        }
-      } catch (IOException e)
-      {
-        System.out.println(
-                "Exception while closing download file output stream: "
-                        + e.getMessage());
-      }
-      try
-      {
-        if (rbc != null)
-        {
-          rbc.close();
-        }
-      } catch (IOException e)
-      {
-        System.out.println("Exception while closing download channel: "
-                + e.getMessage());
-      }
-      try
-      {
-        if (temp != null)
-        {
-          Files.deleteIfExists(temp);
-        }
-      } catch (IOException e)
-      {
-        System.out.println("Exception while deleting download temp file: "
-                + e.getMessage());
-      }
-    }
+      Platform.download(urlstring, outfile);
 
   }
 
-  public static void download(String urlstring, File tempFile)
-          throws IOException
+  public static void download(String urlstring, File tempFile) throws IOException
   {
     if (!Platform.setFileBytes(tempFile, urlstring))
     {
index 4995dd3..b552b94 100644 (file)
@@ -1,10 +1,11 @@
 //
-// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
-// See https://eclipse-ee4j.github.io/jaxb-ri 
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
 // Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
+
 package jalview.xml.binding.jalview;
 
 import java.util.ArrayList;
@@ -14,58 +15,62 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlType;
 
+
 /**
- * &lt;p&gt;Java class for DoubleVector complex type.
+ * <p>Java class for DoubleVector complex type.
  * 
- * &lt;p&gt;The following schema fragment specifies the expected content
- * contained within this class.
+ * <p>The following schema fragment specifies the expected content contained within this class.
  * 
- * &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;
+ * <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>
  * 
  * 
  */
 @XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(
-  name = "DoubleVector",
-  namespace = "www.jalview.org",
-  propOrder =
-  { "v" })
-public class DoubleVector
-{
+@XmlType(name = "DoubleVector", namespace = "www.jalview.org", propOrder = {
+    "v"
+})
+public class DoubleVector {
 
-  @XmlElement(type = Double.class)
-  protected List<Double> v;
+    @XmlElement(type = Double.class)
+    protected List<Double> v;
 
-  /**
-   * Gets the value of the v property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the v property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getV().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link Double }
-   * 
-   * 
-   */
-  public List<Double> getV()
-  {
-    if (v == null)
-    {
-      v = new ArrayList<Double>();
+    /**
+     * Gets the value of the v property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the v property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getV().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Double }
+     * 
+     * 
+     */
+    public List<Double> getV() {
+        if (v == null) {
+            v = new ArrayList<Double>();
+        }
+        return this.v;
     }
-    return this.v;
-  }
 
 }
index 9909b08..41c3e7b 100644 (file)
@@ -5,6 +5,7 @@
 // Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
+
 package jalview.xml.binding.jalview;
 
 import java.util.ArrayList;
@@ -21,6393 +22,6385 @@ import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import javax.xml.datatype.XMLGregorianCalendar;
 
+
 /**
  * &lt;p&gt;Java class for JalviewModel complex type.
  * 
- * &lt;p&gt;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.
  * 
- * &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;
+ * &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;
  * 
  * 
  */
 @XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(
-  name = "JalviewModel",
-  namespace = "www.jalview.org",
-  propOrder =
-  { "creationDate", "version", "vamsasModel", "jSeq", "jGroup", "viewport",
-      "userColours", "tree", "pcaViewer", "featureSettings" })
-public class JalviewModel
-{
-
-  @XmlElement(required = true)
-  @XmlSchemaType(name = "dateTime")
-  protected XMLGregorianCalendar creationDate;
-
-  @XmlElement(required = true)
-  protected String version;
-
-  @XmlElement(required = true)
-  protected VAMSAS vamsasModel;
-
-  @XmlElement(name = "JSeq")
-  protected List<JalviewModel.JSeq> jSeq;
-
-  @XmlElement(name = "JGroup")
-  protected List<JalviewModel.JGroup> jGroup;
-
-  @XmlElement(name = "Viewport")
-  protected List<JalviewModel.Viewport> viewport;
-
-  @XmlElement(name = "UserColours")
-  protected List<JalviewModel.UserColours> userColours;
-
-  protected List<JalviewModel.Tree> tree;
-
-  @XmlElement(name = "PcaViewer")
-  protected List<JalviewModel.PcaViewer> pcaViewer;
-
-  @XmlElement(name = "FeatureSettings")
-  protected JalviewModel.FeatureSettings featureSettings;
-
-  /**
-   * Gets the value of the creationDate property.
-   * 
-   * @return possible object is {@link XMLGregorianCalendar }
-   * 
-   */
-  public XMLGregorianCalendar getCreationDate()
-  {
-    return creationDate;
-  }
-
-  /**
-   * Sets the value of the creationDate property.
-   * 
-   * @param value
-   *          allowed object is {@link XMLGregorianCalendar }
-   * 
-   */
-  public void setCreationDate(XMLGregorianCalendar value)
-  {
-    this.creationDate = value;
-  }
-
-  /**
-   * Gets the value of the version property.
-   * 
-   * @return possible object is {@link String }
-   * 
-   */
-  public String getVersion()
-  {
-    return version;
-  }
-
-  /**
-   * Sets the value of the version property.
-   * 
-   * @param value
-   *          allowed object is {@link String }
-   * 
-   */
-  public void setVersion(String value)
-  {
-    this.version = value;
-  }
-
-  /**
-   * Gets the value of the vamsasModel property.
-   * 
-   * @return possible object is {@link VAMSAS }
-   * 
-   */
-  public VAMSAS getVamsasModel()
-  {
-    return vamsasModel;
-  }
-
-  /**
-   * Sets the value of the vamsasModel property.
-   * 
-   * @param value
-   *          allowed object is {@link VAMSAS }
-   * 
-   */
-  public void setVamsasModel(VAMSAS value)
-  {
-    this.vamsasModel = value;
-  }
-
-  /**
-   * Gets the value of the jSeq property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the jSeq property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getJSeq().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.JSeq }
-   * 
-   * 
-   */
-  public List<JalviewModel.JSeq> getJSeq()
-  {
-    if (jSeq == null)
-    {
-      jSeq = new ArrayList<JalviewModel.JSeq>();
-    }
-    return this.jSeq;
-  }
-
-  /**
-   * Gets the value of the jGroup property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the jGroup property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getJGroup().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.JGroup }
-   * 
-   * 
-   */
-  public List<JalviewModel.JGroup> getJGroup()
-  {
-    if (jGroup == null)
-    {
-      jGroup = new ArrayList<JalviewModel.JGroup>();
-    }
-    return this.jGroup;
-  }
-
-  /**
-   * Gets the value of the viewport property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the viewport property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getViewport().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.Viewport }
-   * 
-   * 
-   */
-  public List<JalviewModel.Viewport> getViewport()
-  {
-    if (viewport == null)
-    {
-      viewport = new ArrayList<JalviewModel.Viewport>();
-    }
-    return this.viewport;
-  }
-
-  /**
-   * Gets the value of the userColours property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the userColours property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getUserColours().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.UserColours }
-   * 
-   * 
-   */
-  public List<JalviewModel.UserColours> getUserColours()
-  {
-    if (userColours == null)
-    {
-      userColours = new ArrayList<JalviewModel.UserColours>();
-    }
-    return this.userColours;
-  }
-
-  /**
-   * Gets the value of the tree property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the tree property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getTree().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.Tree }
-   * 
-   * 
-   */
-  public List<JalviewModel.Tree> getTree()
-  {
-    if (tree == null)
-    {
-      tree = new ArrayList<JalviewModel.Tree>();
-    }
-    return this.tree;
-  }
-
-  /**
-   * Gets the value of the pcaViewer property.
-   * 
-   * &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
-   * &lt;CODE&gt;set&lt;/CODE&gt; method for the pcaViewer property.
-   * 
-   * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-   * getPcaViewer().add(newItem); &lt;/pre&gt;
-   * 
-   * 
-   * &lt;p&gt; Objects of the following type(s) are allowed in the list
-   * {@link JalviewModel.PcaViewer }
-   * 
-   * 
-   */
-  public List<JalviewModel.PcaViewer> getPcaViewer()
-  {
-    if (pcaViewer == null)
-    {
-      pcaViewer = new ArrayList<JalviewModel.PcaViewer>();
-    }
-    return this.pcaViewer;
-  }
-
-  /**
-   * Gets the value of the featureSettings property.
-   * 
-   * @return possible object is {@link JalviewModel.FeatureSettings }
-   * 
-   */
-  public JalviewModel.FeatureSettings getFeatureSettings()
-  {
-    return featureSettings;
-  }
-
-  /**
-   * Sets the value of the featureSettings property.
-   * 
-   * @param value
-   *          allowed object is {@link JalviewModel.FeatureSettings }
-   * 
-   */
-  public void setFeatureSettings(JalviewModel.FeatureSettings value)
-  {
-    this.featureSettings = value;
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(name = "", propOrder = { "setting", "group" })
-  public static class FeatureSettings
-  {
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.FeatureSettings.Setting> setting;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.FeatureSettings.Group> group;
-
-    /**
-     * Gets the value of the setting property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the setting property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getSetting().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.FeatureSettings.Setting }
-     * 
-     * 
-     */
-    public List<JalviewModel.FeatureSettings.Setting> getSetting()
-    {
-      if (setting == null)
-      {
-        setting = new ArrayList<JalviewModel.FeatureSettings.Setting>();
-      }
-      return this.setting;
-    }
-
-    /**
-     * Gets the value of the group property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the group property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getGroup().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.FeatureSettings.Group }
-     * 
-     * 
-     */
-    public List<JalviewModel.FeatureSettings.Group> getGroup()
-    {
-      if (group == null)
-      {
-        group = new ArrayList<JalviewModel.FeatureSettings.Group>();
-      }
-      return this.group;
-    }
-
-    /**
-     * &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="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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class Group
-    {
-
-      @XmlAttribute(name = "name", required = true)
-      protected String name;
-
-      @XmlAttribute(name = "display", required = true)
-      protected boolean display;
-
-      /**
-       * Gets the value of the name property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getName()
-      {
-        return name;
-      }
-
-      /**
-       * Sets the value of the name property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setName(String value)
-      {
-        this.name = value;
-      }
-
-      /**
-       * Gets the value of the display property.
-       * 
-       */
-      public boolean isDisplay()
-      {
-        return display;
-      }
-
-      /**
-       * Sets the value of the display property.
-       * 
-       */
-      public void setDisplay(boolean value)
-      {
-        this.display = value;
-      }
-
-    }
-
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "", propOrder = { "attributeName", "matcherSet" })
-    public static class Setting
-    {
-
-      @XmlElement(namespace = "www.jalview.org")
-      protected List<String> attributeName;
-
-      @XmlElement(namespace = "www.jalview.org")
-      protected FeatureMatcherSet matcherSet;
-
-      @XmlAttribute(name = "type", required = true)
-      protected String type;
-
-      @XmlAttribute(name = "colour", required = true)
-      protected int colour;
-
-      @XmlAttribute(name = "display", required = true)
-      protected boolean display;
-
-      @XmlAttribute(name = "order")
-      protected Float order;
-
-      @XmlAttribute(name = "mincolour")
-      protected Integer mincolour;
-
-      @XmlAttribute(name = "noValueColour")
-      protected NoValueColour noValueColour;
-
-      @XmlAttribute(name = "threshold")
-      protected Float threshold;
-
-      @XmlAttribute(name = "threshstate")
-      protected Integer threshstate;
-
-      @XmlAttribute(name = "max")
-      protected Float max;
-
-      @XmlAttribute(name = "min")
-      protected Float min;
-
-      @XmlAttribute(name = "colourByLabel")
-      protected Boolean colourByLabel;
-
-      @XmlAttribute(name = "autoScale")
-      protected Boolean autoScale;
-
-      /**
-       * Gets the value of the attributeName property.
-       * 
-       * &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
-       * &lt;CODE&gt;set&lt;/CODE&gt; method for the attributeName property.
-       * 
-       * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-       * getAttributeName().add(newItem); &lt;/pre&gt;
-       * 
-       * 
-       * &lt;p&gt; Objects of the following type(s) are allowed in the list
-       * {@link String }
-       * 
-       * 
-       */
-      public List<String> getAttributeName()
-      {
-        if (attributeName == null)
-        {
-          attributeName = new ArrayList<String>();
-        }
-        return this.attributeName;
-      }
-
-      /**
-       * Gets the value of the matcherSet property.
-       * 
-       * @return possible object is {@link FeatureMatcherSet }
-       * 
-       */
-      public FeatureMatcherSet getMatcherSet()
-      {
-        return matcherSet;
-      }
-
-      /**
-       * Sets the value of the matcherSet property.
-       * 
-       * @param value
-       *          allowed object is {@link FeatureMatcherSet }
-       * 
-       */
-      public void setMatcherSet(FeatureMatcherSet value)
-      {
-        this.matcherSet = value;
-      }
-
-      /**
-       * Gets the value of the type property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getType()
-      {
-        return type;
-      }
-
-      /**
-       * Sets the value of the type property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setType(String value)
-      {
-        this.type = value;
-      }
-
-      /**
-       * Gets the value of the colour property.
-       * 
-       */
-      public int getColour()
-      {
-        return colour;
-      }
-
-      /**
-       * Sets the value of the colour property.
-       * 
-       */
-      public void setColour(int value)
-      {
-        this.colour = value;
-      }
-
-      /**
-       * Gets the value of the display property.
-       * 
-       */
-      public boolean isDisplay()
-      {
-        return display;
-      }
-
-      /**
-       * Sets the value of the display property.
-       * 
-       */
-      public void setDisplay(boolean value)
-      {
-        this.display = value;
-      }
-
-      /**
-       * Gets the value of the order property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getOrder()
-      {
-        return order;
-      }
-
-      /**
-       * Sets the value of the order property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setOrder(Float value)
-      {
-        this.order = value;
-      }
-
-      /**
-       * Gets the value of the mincolour property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getMincolour()
-      {
-        return mincolour;
-      }
-
-      /**
-       * Sets the value of the mincolour property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setMincolour(Integer value)
-      {
-        this.mincolour = value;
-      }
-
-      /**
-       * Gets the value of the noValueColour property.
-       * 
-       * @return possible object is {@link NoValueColour }
-       * 
-       */
-      public NoValueColour getNoValueColour()
-      {
-        if (noValueColour == null)
-        {
-          return NoValueColour.MIN;
-        }
-        else
-        {
-          return noValueColour;
-        }
-      }
-
-      /**
-       * Sets the value of the noValueColour property.
-       * 
-       * @param value
-       *          allowed object is {@link NoValueColour }
-       * 
-       */
-      public void setNoValueColour(NoValueColour value)
-      {
-        this.noValueColour = value;
-      }
-
-      /**
-       * Gets the value of the threshold property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getThreshold()
-      {
-        return threshold;
-      }
-
-      /**
-       * Sets the value of the threshold property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setThreshold(Float value)
-      {
-        this.threshold = value;
-      }
-
-      /**
-       * Gets the value of the threshstate property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getThreshstate()
-      {
-        return threshstate;
-      }
-
-      /**
-       * Sets the value of the threshstate property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setThreshstate(Integer value)
-      {
-        this.threshstate = value;
-      }
-
-      /**
-       * Gets the value of the max property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getMax()
-      {
-        return max;
-      }
-
-      /**
-       * Sets the value of the max property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setMax(Float value)
-      {
-        this.max = value;
-      }
-
-      /**
-       * Gets the value of the min property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getMin()
-      {
-        return min;
-      }
-
-      /**
-       * Sets the value of the min property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setMin(Float value)
-      {
-        this.min = value;
-      }
-
-      /**
-       * Gets the value of the colourByLabel property.
-       * 
-       * @return possible object is {@link Boolean }
-       * 
-       */
-      public Boolean isColourByLabel()
-      {
-        return colourByLabel;
-      }
-
-      /**
-       * Sets the value of the colourByLabel property.
-       * 
-       * @param value
-       *          allowed object is {@link Boolean }
-       * 
-       */
-      public void setColourByLabel(Boolean value)
-      {
-        this.colourByLabel = value;
-      }
-
-      /**
-       * Gets the value of the autoScale property.
-       * 
-       * @return possible object is {@link Boolean }
-       * 
-       */
-      public Boolean isAutoScale()
-      {
-        return autoScale;
-      }
-
-      /**
-       * Sets the value of the autoScale property.
-       * 
-       * @param value
-       *          allowed object is {@link Boolean }
-       * 
-       */
-      public void setAutoScale(Boolean value)
-      {
-        this.autoScale = value;
-      }
-
-    }
-
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(name = "", propOrder = { "seq", "annotationColours" })
-  public static class JGroup
-  {
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected List<String> seq;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected AnnotationColourScheme annotationColours;
-
-    @XmlAttribute(name = "start")
-    protected Integer start;
-
-    @XmlAttribute(name = "end")
-    protected Integer end;
-
-    @XmlAttribute(name = "name")
-    protected String name;
-
-    @XmlAttribute(name = "colour")
-    protected String colour;
-
-    @XmlAttribute(name = "consThreshold")
-    protected Integer consThreshold;
-
-    @XmlAttribute(name = "pidThreshold")
-    protected Integer pidThreshold;
-
-    @XmlAttribute(name = "outlineColour")
-    protected Integer outlineColour;
-
-    @XmlAttribute(name = "displayBoxes")
-    protected Boolean displayBoxes;
-
-    @XmlAttribute(name = "displayText")
-    protected Boolean displayText;
-
-    @XmlAttribute(name = "colourText")
-    protected Boolean colourText;
-
-    @XmlAttribute(name = "textCol1")
-    protected Integer textCol1;
-
-    @XmlAttribute(name = "textCol2")
-    protected Integer textCol2;
-
-    @XmlAttribute(name = "textColThreshold")
-    protected Integer textColThreshold;
-
-    @XmlAttribute(name = "showUnconserved")
-    protected Boolean showUnconserved;
-
-    @XmlAttribute(name = "ignoreGapsinConsensus")
-    protected Boolean ignoreGapsinConsensus;
-
-    @XmlAttribute(name = "showConsensusHistogram")
-    protected Boolean showConsensusHistogram;
-
-    @XmlAttribute(name = "showSequenceLogo")
-    protected Boolean showSequenceLogo;
-
-    @XmlAttribute(name = "normaliseSequenceLogo")
-    protected Boolean normaliseSequenceLogo;
-
-    @XmlAttribute(name = "id")
-    protected String id;
-
-    /**
-     * Gets the value of the seq property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the seq property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getSeq().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link String }
-     * 
-     * 
-     */
-    public List<String> getSeq()
-    {
-      if (seq == null)
-      {
-        seq = new ArrayList<String>();
-      }
-      return this.seq;
-    }
-
-    /**
-     * Gets the value of the annotationColours property.
-     * 
-     * @return possible object is {@link AnnotationColourScheme }
-     * 
-     */
-    public AnnotationColourScheme getAnnotationColours()
-    {
-      return annotationColours;
-    }
-
-    /**
-     * Sets the value of the annotationColours property.
+@XmlType(name = "JalviewModel", namespace = "www.jalview.org", propOrder = {
+    "creationDate",
+    "version",
+    "vamsasModel",
+    "jSeq",
+    "jGroup",
+    "viewport",
+    "userColours",
+    "tree",
+    "pcaViewer",
+    "featureSettings"
+})
+public class JalviewModel {
+
+    @XmlElement(required = true)
+    @XmlSchemaType(name = "dateTime")
+    protected XMLGregorianCalendar creationDate;
+    @XmlElement(required = true)
+    protected String version;
+    @XmlElement(required = true)
+    protected VAMSAS vamsasModel;
+    @XmlElement(name = "JSeq")
+    protected List<JalviewModel.JSeq> jSeq;
+    @XmlElement(name = "JGroup")
+    protected List<JalviewModel.JGroup> jGroup;
+    @XmlElement(name = "Viewport")
+    protected List<JalviewModel.Viewport> viewport;
+    @XmlElement(name = "UserColours")
+    protected List<JalviewModel.UserColours> userColours;
+    protected List<JalviewModel.Tree> tree;
+    @XmlElement(name = "PcaViewer")
+    protected List<JalviewModel.PcaViewer> pcaViewer;
+    @XmlElement(name = "FeatureSettings")
+    protected JalviewModel.FeatureSettings featureSettings;
+
+    /**
+     * Gets the value of the creationDate property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public XMLGregorianCalendar getCreationDate() {
+        return creationDate;
+    }
+
+    /**
+     * Sets the value of the creationDate property.
      * 
      * @param value
-     *          allowed object is {@link AnnotationColourScheme }
-     * 
+     *     allowed object is
+     *     {@link XMLGregorianCalendar }
+     *     
      */
-    public void setAnnotationColours(AnnotationColourScheme value)
-    {
-      this.annotationColours = value;
+    public void setCreationDate(XMLGregorianCalendar value) {
+        this.creationDate = value;
     }
 
     /**
-     * Gets the value of the start property.
-     * 
-     * @return possible object is {@link Integer }
+     * Gets the value of the version property.
      * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
      */
-    public Integer getStart()
-    {
-      return start;
+    public String getVersion() {
+        return version;
     }
 
     /**
-     * Sets the value of the start property.
+     * Sets the value of the version property.
      * 
      * @param value
-     *          allowed object is {@link Integer }
-     * 
+     *     allowed object is
+     *     {@link String }
+     *     
      */
-    public void setStart(Integer value)
-    {
-      this.start = value;
+    public void setVersion(String value) {
+        this.version = value;
     }
 
     /**
-     * Gets the value of the end property.
-     * 
-     * @return possible object is {@link Integer }
+     * Gets the value of the vamsasModel property.
      * 
+     * @return
+     *     possible object is
+     *     {@link VAMSAS }
+     *     
      */
-    public Integer getEnd()
-    {
-      return end;
+    public VAMSAS getVamsasModel() {
+        return vamsasModel;
     }
 
     /**
-     * Sets the value of the end property.
+     * Sets the value of the vamsasModel property.
      * 
      * @param value
-     *          allowed object is {@link Integer }
-     * 
+     *     allowed object is
+     *     {@link VAMSAS }
+     *     
      */
-    public void setEnd(Integer value)
-    {
-      this.end = value;
+    public void setVamsasModel(VAMSAS value) {
+        this.vamsasModel = value;
     }
 
     /**
-     * Gets the value of the name property.
+     * Gets the value of the jSeq property.
      * 
-     * @return possible object is {@link String }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the jSeq property.
      * 
-     */
-    public String getName()
-    {
-      return name;
-    }
-
-    /**
-     * Sets the value of the name property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getJSeq().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @param value
-     *          allowed object is {@link String }
      * 
-     */
-    public void setName(String value)
-    {
-      this.name = value;
-    }
-
-    /**
-     * Gets the value of the colour property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.JSeq }
      * 
-     * @return possible object is {@link String }
      * 
      */
-    public String getColour()
-    {
-      return colour;
+    public List<JalviewModel.JSeq> getJSeq() {
+        if (jSeq == null) {
+            jSeq = new ArrayList<JalviewModel.JSeq>();
+        }
+        return this.jSeq;
     }
 
     /**
-     * Sets the value of the colour property.
+     * Gets the value of the jGroup property.
      * 
-     * @param value
-     *          allowed object is {@link String }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the jGroup property.
      * 
-     */
-    public void setColour(String value)
-    {
-      this.colour = value;
-    }
-
-    /**
-     * Gets the value of the consThreshold property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getJGroup().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @return possible object is {@link Integer }
      * 
-     */
-    public Integer getConsThreshold()
-    {
-      return consThreshold;
-    }
-
-    /**
-     * Sets the value of the consThreshold property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.JGroup }
      * 
-     * @param value
-     *          allowed object is {@link Integer }
      * 
      */
-    public void setConsThreshold(Integer value)
-    {
-      this.consThreshold = value;
+    public List<JalviewModel.JGroup> getJGroup() {
+        if (jGroup == null) {
+            jGroup = new ArrayList<JalviewModel.JGroup>();
+        }
+        return this.jGroup;
     }
 
     /**
-     * Gets the value of the pidThreshold property.
+     * Gets the value of the viewport property.
      * 
-     * @return possible object is {@link Integer }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the viewport property.
      * 
-     */
-    public Integer getPidThreshold()
-    {
-      return pidThreshold;
-    }
-
-    /**
-     * Sets the value of the pidThreshold property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getViewport().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @param value
-     *          allowed object is {@link Integer }
      * 
-     */
-    public void setPidThreshold(Integer value)
-    {
-      this.pidThreshold = value;
-    }
-
-    /**
-     * Gets the value of the outlineColour property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.Viewport }
      * 
-     * @return possible object is {@link Integer }
      * 
      */
-    public Integer getOutlineColour()
-    {
-      return outlineColour;
+    public List<JalviewModel.Viewport> getViewport() {
+        if (viewport == null) {
+            viewport = new ArrayList<JalviewModel.Viewport>();
+        }
+        return this.viewport;
     }
 
     /**
-     * Sets the value of the outlineColour property.
+     * Gets the value of the userColours property.
      * 
-     * @param value
-     *          allowed object is {@link Integer }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the userColours property.
      * 
-     */
-    public void setOutlineColour(Integer value)
-    {
-      this.outlineColour = value;
-    }
-
-    /**
-     * Gets the value of the displayBoxes property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getUserColours().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @return possible object is {@link Boolean }
      * 
-     */
-    public Boolean isDisplayBoxes()
-    {
-      return displayBoxes;
-    }
-
-    /**
-     * Sets the value of the displayBoxes property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.UserColours }
      * 
-     * @param value
-     *          allowed object is {@link Boolean }
      * 
      */
-    public void setDisplayBoxes(Boolean value)
-    {
-      this.displayBoxes = value;
+    public List<JalviewModel.UserColours> getUserColours() {
+        if (userColours == null) {
+            userColours = new ArrayList<JalviewModel.UserColours>();
+        }
+        return this.userColours;
     }
 
     /**
-     * Gets the value of the displayText property.
+     * Gets the value of the tree property.
      * 
-     * @return possible object is {@link Boolean }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the tree property.
      * 
-     */
-    public Boolean isDisplayText()
-    {
-      return displayText;
-    }
-
-    /**
-     * Sets the value of the displayText property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getTree().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @param value
-     *          allowed object is {@link Boolean }
      * 
-     */
-    public void setDisplayText(Boolean value)
-    {
-      this.displayText = value;
-    }
-
-    /**
-     * Gets the value of the colourText property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.Tree }
      * 
-     * @return possible object is {@link Boolean }
      * 
      */
-    public Boolean isColourText()
-    {
-      return colourText;
+    public List<JalviewModel.Tree> getTree() {
+        if (tree == null) {
+            tree = new ArrayList<JalviewModel.Tree>();
+        }
+        return this.tree;
     }
 
     /**
-     * Sets the value of the colourText property.
+     * Gets the value of the pcaViewer property.
      * 
-     * @param value
-     *          allowed object is {@link Boolean }
+     * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the pcaViewer property.
      * 
-     */
-    public void setColourText(Boolean value)
-    {
-      this.colourText = value;
-    }
-
-    /**
-     * Gets the value of the textCol1 property.
+     * &lt;p&gt;
+     * For example, to add a new item, do as follows:
+     * &lt;pre&gt;
+     *    getPcaViewer().add(newItem);
+     * &lt;/pre&gt;
      * 
-     * @return possible object is {@link Integer }
      * 
-     */
-    public Integer getTextCol1()
-    {
-      return textCol1;
-    }
-
-    /**
-     * Sets the value of the textCol1 property.
+     * &lt;p&gt;
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModel.PcaViewer }
      * 
-     * @param value
-     *          allowed object is {@link Integer }
      * 
      */
-    public void setTextCol1(Integer value)
-    {
-      this.textCol1 = value;
+    public List<JalviewModel.PcaViewer> getPcaViewer() {
+        if (pcaViewer == null) {
+            pcaViewer = new ArrayList<JalviewModel.PcaViewer>();
+        }
+        return this.pcaViewer;
     }
 
     /**
-     * Gets the value of the textCol2 property.
-     * 
-     * @return possible object is {@link Integer }
+     * Gets the value of the featureSettings property.
      * 
+     * @return
+     *     possible object is
+     *     {@link JalviewModel.FeatureSettings }
+     *     
      */
-    public Integer getTextCol2()
-    {
-      return textCol2;
+    public JalviewModel.FeatureSettings getFeatureSettings() {
+        return featureSettings;
     }
 
     /**
-     * Sets the value of the textCol2 property.
+     * Sets the value of the featureSettings property.
      * 
      * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setTextCol2(Integer value)
-    {
-      this.textCol2 = value;
-    }
-
-    /**
-     * Gets the value of the textColThreshold property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
+     *     allowed object is
+     *     {@link JalviewModel.FeatureSettings }
+     *     
      */
-    public Integer getTextColThreshold()
-    {
-      return textColThreshold;
+    public void setFeatureSettings(JalviewModel.FeatureSettings value) {
+        this.featureSettings = value;
     }
 
-    /**
-     * Sets the value of the textColThreshold property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setTextColThreshold(Integer value)
-    {
-      this.textColThreshold = value;
-    }
 
     /**
-     * Gets the value of the showUnconserved property.
-     * 
-     * @return possible object is {@link Boolean }
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     */
-    public Boolean isShowUnconserved()
-    {
-      return showUnconserved;
-    }
-
-    /**
-     * Sets the value of the showUnconserved property.
+     * &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;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;
      * 
-     * @param value
-     *          allowed object is {@link Boolean }
      * 
      */
-    public void setShowUnconserved(Boolean value)
-    {
-      this.showUnconserved = value;
-    }
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "setting",
+        "group"
+    })
+    public static class FeatureSettings {
 
-    /**
-     * Gets the value of the ignoreGapsinConsensus property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isIgnoreGapsinConsensus()
-    {
-      if (ignoreGapsinConsensus == null)
-      {
-        return true;
-      }
-      else
-      {
-        return ignoreGapsinConsensus;
-      }
-    }
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.FeatureSettings.Setting> setting;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.FeatureSettings.Group> group;
 
-    /**
-     * Sets the value of the ignoreGapsinConsensus property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setIgnoreGapsinConsensus(Boolean value)
-    {
-      this.ignoreGapsinConsensus = value;
-    }
+        /**
+         * Gets the value of the setting property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the setting property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getSetting().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.FeatureSettings.Setting }
+         * 
+         * 
+         */
+        public List<JalviewModel.FeatureSettings.Setting> getSetting() {
+            if (setting == null) {
+                setting = new ArrayList<JalviewModel.FeatureSettings.Setting>();
+            }
+            return this.setting;
+        }
 
-    /**
-     * Gets the value of the showConsensusHistogram property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowConsensusHistogram()
-    {
-      if (showConsensusHistogram == null)
-      {
-        return true;
-      }
-      else
-      {
-        return showConsensusHistogram;
-      }
-    }
+        /**
+         * Gets the value of the group property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the group property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getGroup().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.FeatureSettings.Group }
+         * 
+         * 
+         */
+        public List<JalviewModel.FeatureSettings.Group> getGroup() {
+            if (group == null) {
+                group = new ArrayList<JalviewModel.FeatureSettings.Group>();
+            }
+            return this.group;
+        }
 
-    /**
-     * Sets the value of the showConsensusHistogram property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowConsensusHistogram(Boolean value)
-    {
-      this.showConsensusHistogram = value;
-    }
 
-    /**
-     * Gets the value of the showSequenceLogo property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowSequenceLogo()
-    {
-      if (showSequenceLogo == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showSequenceLogo;
-      }
-    }
+        /**
+         * &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="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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class Group {
+
+            @XmlAttribute(name = "name", required = true)
+            protected String name;
+            @XmlAttribute(name = "display", required = true)
+            protected boolean display;
+
+            /**
+             * Gets the value of the name property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getName() {
+                return name;
+            }
+
+            /**
+             * Sets the value of the name property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setName(String value) {
+                this.name = value;
+            }
+
+            /**
+             * Gets the value of the display property.
+             * 
+             */
+            public boolean isDisplay() {
+                return display;
+            }
+
+            /**
+             * Sets the value of the display property.
+             * 
+             */
+            public void setDisplay(boolean value) {
+                this.display = value;
+            }
 
-    /**
-     * Sets the value of the showSequenceLogo property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowSequenceLogo(Boolean value)
-    {
-      this.showSequenceLogo = value;
-    }
+        }
 
-    /**
-     * Gets the value of the normaliseSequenceLogo property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isNormaliseSequenceLogo()
-    {
-      if (normaliseSequenceLogo == null)
-      {
-        return false;
-      }
-      else
-      {
-        return normaliseSequenceLogo;
-      }
-    }
 
-    /**
-     * Sets the value of the normaliseSequenceLogo property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setNormaliseSequenceLogo(Boolean value)
-    {
-      this.normaliseSequenceLogo = value;
-    }
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "attributeName",
+            "matcherSet"
+        })
+        public static class Setting {
+
+            @XmlElement(namespace = "www.jalview.org")
+            protected List<String> attributeName;
+            @XmlElement(namespace = "www.jalview.org")
+            protected FeatureMatcherSet matcherSet;
+            @XmlAttribute(name = "type", required = true)
+            protected String type;
+            @XmlAttribute(name = "colour", required = true)
+            protected int colour;
+            @XmlAttribute(name = "display", required = true)
+            protected boolean display;
+            @XmlAttribute(name = "order")
+            protected Float order;
+            @XmlAttribute(name = "mincolour")
+            protected Integer mincolour;
+            @XmlAttribute(name = "noValueColour")
+            protected NoValueColour noValueColour;
+            @XmlAttribute(name = "threshold")
+            protected Float threshold;
+            @XmlAttribute(name = "threshstate")
+            protected Integer threshstate;
+            @XmlAttribute(name = "max")
+            protected Float max;
+            @XmlAttribute(name = "min")
+            protected Float min;
+            @XmlAttribute(name = "colourByLabel")
+            protected Boolean colourByLabel;
+            @XmlAttribute(name = "autoScale")
+            protected Boolean autoScale;
+
+            /**
+             * Gets the value of the attributeName property.
+             * 
+             * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the attributeName property.
+             * 
+             * &lt;p&gt;
+             * For example, to add a new item, do as follows:
+             * &lt;pre&gt;
+             *    getAttributeName().add(newItem);
+             * &lt;/pre&gt;
+             * 
+             * 
+             * &lt;p&gt;
+             * Objects of the following type(s) are allowed in the list
+             * {@link String }
+             * 
+             * 
+             */
+            public List<String> getAttributeName() {
+                if (attributeName == null) {
+                    attributeName = new ArrayList<String>();
+                }
+                return this.attributeName;
+            }
+
+            /**
+             * Gets the value of the matcherSet property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link FeatureMatcherSet }
+             *     
+             */
+            public FeatureMatcherSet getMatcherSet() {
+                return matcherSet;
+            }
+
+            /**
+             * Sets the value of the matcherSet property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link FeatureMatcherSet }
+             *     
+             */
+            public void setMatcherSet(FeatureMatcherSet value) {
+                this.matcherSet = value;
+            }
+
+            /**
+             * Gets the value of the type property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getType() {
+                return type;
+            }
+
+            /**
+             * Sets the value of the type property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setType(String value) {
+                this.type = value;
+            }
+
+            /**
+             * Gets the value of the colour property.
+             * 
+             */
+            public int getColour() {
+                return colour;
+            }
+
+            /**
+             * Sets the value of the colour property.
+             * 
+             */
+            public void setColour(int value) {
+                this.colour = value;
+            }
+
+            /**
+             * Gets the value of the display property.
+             * 
+             */
+            public boolean isDisplay() {
+                return display;
+            }
+
+            /**
+             * Sets the value of the display property.
+             * 
+             */
+            public void setDisplay(boolean value) {
+                this.display = value;
+            }
+
+            /**
+             * Gets the value of the order property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getOrder() {
+                return order;
+            }
+
+            /**
+             * Sets the value of the order property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setOrder(Float value) {
+                this.order = value;
+            }
+
+            /**
+             * Gets the value of the mincolour property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getMincolour() {
+                return mincolour;
+            }
+
+            /**
+             * Sets the value of the mincolour property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setMincolour(Integer value) {
+                this.mincolour = value;
+            }
+
+            /**
+             * Gets the value of the noValueColour property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link NoValueColour }
+             *     
+             */
+            public NoValueColour getNoValueColour() {
+                if (noValueColour == null) {
+                    return NoValueColour.MIN;
+                } else {
+                    return noValueColour;
+                }
+            }
+
+            /**
+             * Sets the value of the noValueColour property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link NoValueColour }
+             *     
+             */
+            public void setNoValueColour(NoValueColour value) {
+                this.noValueColour = value;
+            }
+
+            /**
+             * Gets the value of the threshold property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getThreshold() {
+                return threshold;
+            }
+
+            /**
+             * Sets the value of the threshold property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setThreshold(Float value) {
+                this.threshold = value;
+            }
+
+            /**
+             * Gets the value of the threshstate property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getThreshstate() {
+                return threshstate;
+            }
+
+            /**
+             * Sets the value of the threshstate property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setThreshstate(Integer value) {
+                this.threshstate = value;
+            }
+
+            /**
+             * Gets the value of the max property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getMax() {
+                return max;
+            }
+
+            /**
+             * Sets the value of the max property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setMax(Float value) {
+                this.max = value;
+            }
+
+            /**
+             * Gets the value of the min property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getMin() {
+                return min;
+            }
+
+            /**
+             * Sets the value of the min property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setMin(Float value) {
+                this.min = value;
+            }
+
+            /**
+             * Gets the value of the colourByLabel property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public Boolean isColourByLabel() {
+                return colourByLabel;
+            }
+
+            /**
+             * Sets the value of the colourByLabel property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setColourByLabel(Boolean value) {
+                this.colourByLabel = value;
+            }
+
+            /**
+             * Gets the value of the autoScale property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public Boolean isAutoScale() {
+                return autoScale;
+            }
+
+            /**
+             * Sets the value of the autoScale property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setAutoScale(Boolean value) {
+                this.autoScale = value;
+            }
 
-    /**
-     * Gets the value of the id property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getId()
-    {
-      return id;
-    }
+        }
 
-    /**
-     * Sets the value of the id property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setId(String value)
-    {
-      this.id = value;
     }
 
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(
-    name = "",
-    propOrder =
-    { "features", "pdbids", "hiddenSequences", "rnaViewer" })
-  public static class JSeq
-  {
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<Feature> features;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.JSeq.Pdbids> pdbids;
-
-    @XmlElement(namespace = "www.jalview.org", type = Integer.class)
-    protected List<Integer> hiddenSequences;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.JSeq.RnaViewer> rnaViewer;
-
-    @XmlAttribute(name = "colour")
-    protected Integer colour;
-
-    @XmlAttribute(name = "start", required = true)
-    protected int start;
-
-    @XmlAttribute(name = "end", required = true)
-    protected int end;
-
-    @XmlAttribute(name = "id", required = true)
-    protected String id;
-
-    @XmlAttribute(name = "hidden")
-    protected Boolean hidden;
-
-    @XmlAttribute(name = "viewreference")
-    protected Boolean viewreference;
 
     /**
-     * Gets the value of the features property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the features property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getFeatures().add(newItem); &lt;/pre&gt;
-     * 
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link Feature }
+     * &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;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;
      * 
      * 
      */
-    public List<Feature> getFeatures()
-    {
-      if (features == null)
-      {
-        features = new ArrayList<Feature>();
-      }
-      return this.features;
-    }
-
-    /**
-     * Gets the value of the pdbids property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the pdbids property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getPdbids().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.JSeq.Pdbids }
-     * 
-     * 
-     */
-    public List<JalviewModel.JSeq.Pdbids> getPdbids()
-    {
-      if (pdbids == null)
-      {
-        pdbids = new ArrayList<JalviewModel.JSeq.Pdbids>();
-      }
-      return this.pdbids;
-    }
-
-    /**
-     * Gets the value of the hiddenSequences property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenSequences property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getHiddenSequences().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link Integer }
-     * 
-     * 
-     */
-    public List<Integer> getHiddenSequences()
-    {
-      if (hiddenSequences == null)
-      {
-        hiddenSequences = new ArrayList<Integer>();
-      }
-      return this.hiddenSequences;
-    }
-
-    /**
-     * Gets the value of the rnaViewer property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the rnaViewer property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getRnaViewer().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.JSeq.RnaViewer }
-     * 
-     * 
-     */
-    public List<JalviewModel.JSeq.RnaViewer> getRnaViewer()
-    {
-      if (rnaViewer == null)
-      {
-        rnaViewer = new ArrayList<JalviewModel.JSeq.RnaViewer>();
-      }
-      return this.rnaViewer;
-    }
-
-    /**
-     * Gets the value of the colour property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getColour()
-    {
-      return colour;
-    }
-
-    /**
-     * Sets the value of the colour property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setColour(Integer value)
-    {
-      this.colour = value;
-    }
-
-    /**
-     * Gets the value of the start property.
-     * 
-     */
-    public int getStart()
-    {
-      return start;
-    }
-
-    /**
-     * Sets the value of the start property.
-     * 
-     */
-    public void setStart(int value)
-    {
-      this.start = value;
-    }
-
-    /**
-     * Gets the value of the end property.
-     * 
-     */
-    public int getEnd()
-    {
-      return end;
-    }
-
-    /**
-     * Sets the value of the end property.
-     * 
-     */
-    public void setEnd(int value)
-    {
-      this.end = value;
-    }
-
-    /**
-     * Gets the value of the id property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getId()
-    {
-      return id;
-    }
-
-    /**
-     * Sets the value of the id property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setId(String value)
-    {
-      this.id = value;
-    }
-
-    /**
-     * Gets the value of the hidden property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isHidden()
-    {
-      return hidden;
-    }
-
-    /**
-     * Sets the value of the hidden property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setHidden(Boolean value)
-    {
-      this.hidden = value;
-    }
-
-    /**
-     * Gets the value of the viewreference property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isViewreference()
-    {
-      return viewreference;
-    }
-
-    /**
-     * Sets the value of the viewreference property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setViewreference(Boolean value)
-    {
-      this.viewreference = value;
-    }
-
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "", propOrder = { "structureState" })
-    public static class Pdbids extends Pdbentry
-    {
-
-      @XmlElement(namespace = "www.jalview.org")
-      protected List<JalviewModel.JSeq.Pdbids.StructureState> structureState;
-
-      /**
-       * Gets the value of the structureState property.
-       * 
-       * &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
-       * &lt;CODE&gt;set&lt;/CODE&gt; method for the structureState property.
-       * 
-       * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-       * getStructureState().add(newItem); &lt;/pre&gt;
-       * 
-       * 
-       * &lt;p&gt; Objects of the following type(s) are allowed in the list
-       * {@link JalviewModel.JSeq.Pdbids.StructureState }
-       * 
-       * 
-       */
-      public List<JalviewModel.JSeq.Pdbids.StructureState> getStructureState()
-      {
-        if (structureState == null)
-        {
-          structureState = new ArrayList<JalviewModel.JSeq.Pdbids.StructureState>();
-        }
-        return this.structureState;
-      }
-
-      /**
-       * &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;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;
-       * 
-       * 
-       */
-      @XmlAccessorType(XmlAccessType.FIELD)
-      @XmlType(name = "", propOrder = { "value" })
-      public static class StructureState
-      {
-
-        @XmlValue
-        protected String value;
-
-        @XmlAttribute(name = "visible")
-        protected Boolean visible;
-
-        @XmlAttribute(name = "viewId")
-        protected String viewId;
-
-        @XmlAttribute(name = "alignwithAlignPanel")
-        protected Boolean alignwithAlignPanel;
-
-        @XmlAttribute(name = "colourwithAlignPanel")
-        protected Boolean colourwithAlignPanel;
-
-        @XmlAttribute(name = "colourByJmol")
-        protected Boolean colourByJmol;
-
-        @XmlAttribute(name = "type")
-        protected String type;
-
-        @XmlAttribute(name = "width")
-        protected Integer width;
-
-        @XmlAttribute(name = "height")
-        protected Integer height;
-
-        @XmlAttribute(name = "xpos")
-        protected Integer xpos;
-
-        @XmlAttribute(name = "ypos")
-        protected Integer ypos;
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "seq",
+        "annotationColours"
+    })
+    public static class JGroup {
+
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected List<String> seq;
+        @XmlElement(namespace = "www.jalview.org")
+        protected AnnotationColourScheme annotationColours;
+        @XmlAttribute(name = "start")
+        protected Integer start;
+        @XmlAttribute(name = "end")
+        protected Integer end;
+        @XmlAttribute(name = "name")
+        protected String name;
+        @XmlAttribute(name = "colour")
+        protected String colour;
+        @XmlAttribute(name = "consThreshold")
+        protected Integer consThreshold;
+        @XmlAttribute(name = "pidThreshold")
+        protected Integer pidThreshold;
+        @XmlAttribute(name = "outlineColour")
+        protected Integer outlineColour;
+        @XmlAttribute(name = "displayBoxes")
+        protected Boolean displayBoxes;
+        @XmlAttribute(name = "displayText")
+        protected Boolean displayText;
+        @XmlAttribute(name = "colourText")
+        protected Boolean colourText;
+        @XmlAttribute(name = "textCol1")
+        protected Integer textCol1;
+        @XmlAttribute(name = "textCol2")
+        protected Integer textCol2;
+        @XmlAttribute(name = "textColThreshold")
+        protected Integer textColThreshold;
+        @XmlAttribute(name = "showUnconserved")
+        protected Boolean showUnconserved;
+        @XmlAttribute(name = "ignoreGapsinConsensus")
+        protected Boolean ignoreGapsinConsensus;
+        @XmlAttribute(name = "showConsensusHistogram")
+        protected Boolean showConsensusHistogram;
+        @XmlAttribute(name = "showSequenceLogo")
+        protected Boolean showSequenceLogo;
+        @XmlAttribute(name = "normaliseSequenceLogo")
+        protected Boolean normaliseSequenceLogo;
+        @XmlAttribute(name = "id")
+        protected String id;
 
         /**
-         * Gets the value of the value property.
+         * Gets the value of the seq property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the seq property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getSeq().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link String }
          * 
-         * @return possible object is {@link String }
          * 
          */
-        public String getValue()
-        {
-          return value;
+        public List<String> getSeq() {
+            if (seq == null) {
+                seq = new ArrayList<String>();
+            }
+            return this.seq;
         }
 
         /**
-         * Sets the value of the value property.
-         * 
-         * @param value
-         *          allowed object is {@link String }
+         * Gets the value of the annotationColours property.
          * 
+         * @return
+         *     possible object is
+         *     {@link AnnotationColourScheme }
+         *     
          */
-        public void setValue(String value)
-        {
-          this.value = value;
+        public AnnotationColourScheme getAnnotationColours() {
+            return annotationColours;
         }
 
         /**
-         * Gets the value of the visible property.
+         * Sets the value of the annotationColours property.
          * 
-         * @return possible object is {@link Boolean }
+         * @param value
+         *     allowed object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public void setAnnotationColours(AnnotationColourScheme value) {
+            this.annotationColours = value;
+        }
+
+        /**
+         * Gets the value of the start property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public Boolean isVisible()
-        {
-          return visible;
+        public Integer getStart() {
+            return start;
         }
 
         /**
-         * Sets the value of the visible property.
+         * Sets the value of the start property.
          * 
          * @param value
-         *          allowed object is {@link Boolean }
-         * 
+         *     allowed object is
+         *     {@link Integer }
+         *     
          */
-        public void setVisible(Boolean value)
-        {
-          this.visible = value;
+        public void setStart(Integer value) {
+            this.start = value;
         }
 
         /**
-         * Gets the value of the viewId property.
-         * 
-         * @return possible object is {@link String }
+         * Gets the value of the end property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public String getViewId()
-        {
-          return viewId;
+        public Integer getEnd() {
+            return end;
         }
 
         /**
-         * Sets the value of the viewId property.
+         * Sets the value of the end property.
          * 
          * @param value
-         *          allowed object is {@link String }
-         * 
+         *     allowed object is
+         *     {@link Integer }
+         *     
          */
-        public void setViewId(String value)
-        {
-          this.viewId = value;
+        public void setEnd(Integer value) {
+            this.end = value;
         }
 
         /**
-         * Gets the value of the alignwithAlignPanel property.
-         * 
-         * @return possible object is {@link Boolean }
+         * Gets the value of the name property.
          * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
          */
-        public boolean isAlignwithAlignPanel()
-        {
-          if (alignwithAlignPanel == null)
-          {
-            return true;
-          }
-          else
-          {
-            return alignwithAlignPanel;
-          }
+        public String getName() {
+            return name;
         }
 
         /**
-         * Sets the value of the alignwithAlignPanel property.
+         * Sets the value of the name property.
          * 
          * @param value
-         *          allowed object is {@link Boolean }
-         * 
+         *     allowed object is
+         *     {@link String }
+         *     
          */
-        public void setAlignwithAlignPanel(Boolean value)
-        {
-          this.alignwithAlignPanel = value;
+        public void setName(String value) {
+            this.name = value;
         }
 
         /**
-         * Gets the value of the colourwithAlignPanel property.
-         * 
-         * @return possible object is {@link Boolean }
+         * Gets the value of the colour property.
          * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
          */
-        public boolean isColourwithAlignPanel()
-        {
-          if (colourwithAlignPanel == null)
-          {
-            return false;
-          }
-          else
-          {
-            return colourwithAlignPanel;
-          }
+        public String getColour() {
+            return colour;
         }
 
         /**
-         * Sets the value of the colourwithAlignPanel property.
+         * Sets the value of the colour property.
          * 
          * @param value
-         *          allowed object is {@link Boolean }
-         * 
+         *     allowed object is
+         *     {@link String }
+         *     
          */
-        public void setColourwithAlignPanel(Boolean value)
-        {
-          this.colourwithAlignPanel = value;
+        public void setColour(String value) {
+            this.colour = value;
         }
 
         /**
-         * Gets the value of the colourByJmol property.
-         * 
-         * @return possible object is {@link Boolean }
+         * Gets the value of the consThreshold property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public boolean isColourByJmol()
-        {
-          if (colourByJmol == null)
-          {
-            return true;
-          }
-          else
-          {
-            return colourByJmol;
-          }
+        public Integer getConsThreshold() {
+            return consThreshold;
         }
 
         /**
-         * Sets the value of the colourByJmol property.
+         * Sets the value of the consThreshold property.
          * 
          * @param value
-         *          allowed object is {@link Boolean }
-         * 
+         *     allowed object is
+         *     {@link Integer }
+         *     
          */
-        public void setColourByJmol(Boolean value)
-        {
-          this.colourByJmol = value;
+        public void setConsThreshold(Integer value) {
+            this.consThreshold = value;
         }
 
         /**
-         * Gets the value of the type property.
-         * 
-         * @return possible object is {@link String }
+         * Gets the value of the pidThreshold property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public String getType()
-        {
-          return type;
+        public Integer getPidThreshold() {
+            return pidThreshold;
         }
 
         /**
-         * Sets the value of the type property.
+         * Sets the value of the pidThreshold property.
          * 
          * @param value
-         *          allowed object is {@link String }
-         * 
+         *     allowed object is
+         *     {@link Integer }
+         *     
          */
-        public void setType(String value)
-        {
-          this.type = value;
+        public void setPidThreshold(Integer value) {
+            this.pidThreshold = value;
         }
 
         /**
-         * Gets the value of the width property.
-         * 
-         * @return possible object is {@link Integer }
+         * Gets the value of the outlineColour property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public Integer getWidth()
-        {
-          return width;
+        public Integer getOutlineColour() {
+            return outlineColour;
         }
 
         /**
-         * Sets the value of the width property.
+         * Sets the value of the outlineColour property.
          * 
          * @param value
-         *          allowed object is {@link Integer }
-         * 
+         *     allowed object is
+         *     {@link Integer }
+         *     
          */
-        public void setWidth(Integer value)
-        {
-          this.width = value;
+        public void setOutlineColour(Integer value) {
+            this.outlineColour = value;
         }
 
         /**
-         * Gets the value of the height property.
-         * 
-         * @return possible object is {@link Integer }
+         * Gets the value of the displayBoxes property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public Integer getHeight()
-        {
-          return height;
+        public Boolean isDisplayBoxes() {
+            return displayBoxes;
         }
 
         /**
-         * Sets the value of the height property.
+         * Sets the value of the displayBoxes property.
          * 
          * @param value
-         *          allowed object is {@link Integer }
-         * 
+         *     allowed object is
+         *     {@link Boolean }
+         *     
          */
-        public void setHeight(Integer value)
-        {
-          this.height = value;
+        public void setDisplayBoxes(Boolean value) {
+            this.displayBoxes = value;
         }
 
         /**
-         * Gets the value of the xpos property.
-         * 
-         * @return possible object is {@link Integer }
+         * Gets the value of the displayText property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public Integer getXpos()
-        {
-          return xpos;
+        public Boolean isDisplayText() {
+            return displayText;
         }
 
         /**
-         * Sets the value of the xpos property.
+         * Sets the value of the displayText property.
          * 
          * @param value
-         *          allowed object is {@link Integer }
-         * 
+         *     allowed object is
+         *     {@link Boolean }
+         *     
          */
-        public void setXpos(Integer value)
-        {
-          this.xpos = value;
+        public void setDisplayText(Boolean value) {
+            this.displayText = value;
         }
 
         /**
-         * Gets the value of the ypos property.
-         * 
-         * @return possible object is {@link Integer }
+         * Gets the value of the colourText property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public Integer getYpos()
-        {
-          return ypos;
+        public Boolean isColourText() {
+            return colourText;
         }
 
         /**
-         * Sets the value of the ypos property.
+         * Sets the value of the colourText property.
          * 
          * @param value
-         *          allowed object is {@link Integer }
-         * 
+         *     allowed object is
+         *     {@link Boolean }
+         *     
          */
-        public void setYpos(Integer value)
-        {
-          this.ypos = value;
+        public void setColourText(Boolean value) {
+            this.colourText = value;
         }
 
-      }
-
-    }
-
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "", propOrder = { "secondaryStructure" })
-    public static class RnaViewer
-    {
-
-      @XmlElement(namespace = "www.jalview.org", required = true)
-      protected List<JalviewModel.JSeq.RnaViewer.SecondaryStructure> secondaryStructure;
-
-      @XmlAttribute(name = "title")
-      protected String title;
-
-      @XmlAttribute(name = "viewId")
-      protected String viewId;
-
-      @XmlAttribute(name = "dividerLocation")
-      protected Integer dividerLocation;
-
-      @XmlAttribute(name = "selectedRna")
-      protected Integer selectedRna;
-
-      @XmlAttribute(name = "width")
-      protected Integer width;
-
-      @XmlAttribute(name = "height")
-      protected Integer height;
-
-      @XmlAttribute(name = "xpos")
-      protected Integer xpos;
-
-      @XmlAttribute(name = "ypos")
-      protected Integer ypos;
-
-      /**
-       * Gets the value of the secondaryStructure property.
-       * 
-       * &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
-       * &lt;CODE&gt;set&lt;/CODE&gt; method for the secondaryStructure
-       * property.
-       * 
-       * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-       * getSecondaryStructure().add(newItem); &lt;/pre&gt;
-       * 
-       * 
-       * &lt;p&gt; Objects of the following type(s) are allowed in the list
-       * {@link JalviewModel.JSeq.RnaViewer.SecondaryStructure }
-       * 
-       * 
-       */
-      public List<JalviewModel.JSeq.RnaViewer.SecondaryStructure> getSecondaryStructure()
-      {
-        if (secondaryStructure == null)
-        {
-          secondaryStructure = new ArrayList<JalviewModel.JSeq.RnaViewer.SecondaryStructure>();
-        }
-        return this.secondaryStructure;
-      }
-
-      /**
-       * Gets the value of the title property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getTitle()
-      {
-        return title;
-      }
-
-      /**
-       * Sets the value of the title property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setTitle(String value)
-      {
-        this.title = value;
-      }
-
-      /**
-       * Gets the value of the viewId property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getViewId()
-      {
-        return viewId;
-      }
-
-      /**
-       * Sets the value of the viewId property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setViewId(String value)
-      {
-        this.viewId = value;
-      }
-
-      /**
-       * Gets the value of the dividerLocation property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getDividerLocation()
-      {
-        return dividerLocation;
-      }
-
-      /**
-       * Sets the value of the dividerLocation property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setDividerLocation(Integer value)
-      {
-        this.dividerLocation = value;
-      }
-
-      /**
-       * Gets the value of the selectedRna property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getSelectedRna()
-      {
-        return selectedRna;
-      }
-
-      /**
-       * Sets the value of the selectedRna property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setSelectedRna(Integer value)
-      {
-        this.selectedRna = value;
-      }
-
-      /**
-       * Gets the value of the width property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getWidth()
-      {
-        return width;
-      }
-
-      /**
-       * Sets the value of the width property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setWidth(Integer value)
-      {
-        this.width = value;
-      }
-
-      /**
-       * Gets the value of the height property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getHeight()
-      {
-        return height;
-      }
-
-      /**
-       * Sets the value of the height property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setHeight(Integer value)
-      {
-        this.height = value;
-      }
-
-      /**
-       * Gets the value of the xpos property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getXpos()
-      {
-        return xpos;
-      }
-
-      /**
-       * Sets the value of the xpos property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setXpos(Integer value)
-      {
-        this.xpos = value;
-      }
-
-      /**
-       * Gets the value of the ypos property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getYpos()
-      {
-        return ypos;
-      }
-
-      /**
-       * Sets the value of the ypos property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setYpos(Integer value)
-      {
-        this.ypos = value;
-      }
-
-      /**
-       * &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="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;
-       * 
-       * 
-       */
-      @XmlAccessorType(XmlAccessType.FIELD)
-      @XmlType(name = "")
-      public static class SecondaryStructure
-      {
-
-        @XmlAttribute(name = "title")
-        protected String title;
-
-        @XmlAttribute(name = "annotationId", required = true)
-        protected String annotationId;
+        /**
+         * Gets the value of the textCol1 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol1() {
+            return textCol1;
+        }
 
-        @XmlAttribute(name = "gapped")
-        protected Boolean gapped;
+        /**
+         * Sets the value of the textCol1 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol1(Integer value) {
+            this.textCol1 = value;
+        }
 
-        @XmlAttribute(name = "viewerState")
-        protected String viewerState;
+        /**
+         * Gets the value of the textCol2 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol2() {
+            return textCol2;
+        }
 
         /**
-         * Gets the value of the title property.
+         * Sets the value of the textCol2 property.
          * 
-         * @return possible object is {@link String }
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol2(Integer value) {
+            this.textCol2 = value;
+        }
+
+        /**
+         * Gets the value of the textColThreshold property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
          */
-        public String getTitle()
-        {
-          return title;
+        public Integer getTextColThreshold() {
+            return textColThreshold;
         }
 
         /**
-         * Sets the value of the title property.
+         * Sets the value of the textColThreshold property.
          * 
          * @param value
-         *          allowed object is {@link String }
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextColThreshold(Integer value) {
+            this.textColThreshold = value;
+        }
+
+        /**
+         * Gets the value of the showUnconserved property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public void setTitle(String value)
-        {
-          this.title = value;
+        public Boolean isShowUnconserved() {
+            return showUnconserved;
         }
 
         /**
-         * Gets the value of the annotationId property.
+         * Sets the value of the showUnconserved property.
          * 
-         * @return possible object is {@link String }
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowUnconserved(Boolean value) {
+            this.showUnconserved = value;
+        }
+
+        /**
+         * Gets the value of the ignoreGapsinConsensus property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public String getAnnotationId()
-        {
-          return annotationId;
+        public boolean isIgnoreGapsinConsensus() {
+            if (ignoreGapsinConsensus == null) {
+                return true;
+            } else {
+                return ignoreGapsinConsensus;
+            }
         }
 
         /**
-         * Sets the value of the annotationId property.
+         * Sets the value of the ignoreGapsinConsensus property.
          * 
          * @param value
-         *          allowed object is {@link String }
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIgnoreGapsinConsensus(Boolean value) {
+            this.ignoreGapsinConsensus = value;
+        }
+
+        /**
+         * Gets the value of the showConsensusHistogram property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public void setAnnotationId(String value)
-        {
-          this.annotationId = value;
+        public boolean isShowConsensusHistogram() {
+            if (showConsensusHistogram == null) {
+                return true;
+            } else {
+                return showConsensusHistogram;
+            }
         }
 
         /**
-         * Gets the value of the gapped property.
+         * Sets the value of the showConsensusHistogram property.
          * 
-         * @return possible object is {@link Boolean }
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowConsensusHistogram(Boolean value) {
+            this.showConsensusHistogram = value;
+        }
+
+        /**
+         * Gets the value of the showSequenceLogo property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public Boolean isGapped()
-        {
-          return gapped;
+        public boolean isShowSequenceLogo() {
+            if (showSequenceLogo == null) {
+                return false;
+            } else {
+                return showSequenceLogo;
+            }
         }
 
         /**
-         * Sets the value of the gapped property.
+         * Sets the value of the showSequenceLogo property.
          * 
          * @param value
-         *          allowed object is {@link Boolean }
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceLogo(Boolean value) {
+            this.showSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the normaliseSequenceLogo property.
          * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
          */
-        public void setGapped(Boolean value)
-        {
-          this.gapped = value;
+        public boolean isNormaliseSequenceLogo() {
+            if (normaliseSequenceLogo == null) {
+                return false;
+            } else {
+                return normaliseSequenceLogo;
+            }
         }
 
         /**
-         * Gets the value of the viewerState property.
+         * Sets the value of the normaliseSequenceLogo property.
          * 
-         * @return possible object is {@link String }
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setNormaliseSequenceLogo(Boolean value) {
+            this.normaliseSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the id property.
          * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
          */
-        public String getViewerState()
-        {
-          return viewerState;
+        public String getId() {
+            return id;
         }
 
         /**
-         * Sets the value of the viewerState property.
+         * Sets the value of the id property.
          * 
          * @param value
-         *          allowed object is {@link String }
-         * 
+         *     allowed object is
+         *     {@link String }
+         *     
          */
-        public void setViewerState(String value)
-        {
-          this.viewerState = value;
+        public void setId(String value) {
+            this.id = value;
         }
 
-      }
-
     }
 
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(
-    name = "",
-    propOrder =
-    { "sequencePoint", "axis", "seqPointMin", "seqPointMax", "pcaData" })
-  public static class PcaViewer
-  {
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected List<JalviewModel.PcaViewer.SequencePoint> sequencePoint;
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected List<JalviewModel.PcaViewer.Axis> axis;
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected JalviewModel.PcaViewer.SeqPointMin seqPointMin;
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected JalviewModel.PcaViewer.SeqPointMax seqPointMax;
-
-    @XmlElement(namespace = "www.jalview.org", required = true)
-    protected PcaDataType pcaData;
-
-    @XmlAttribute(name = "title")
-    protected String title;
-
-    @XmlAttribute(name = "scoreModelName")
-    protected String scoreModelName;
-
-    @XmlAttribute(name = "xDim")
-    protected Integer xDim;
-
-    @XmlAttribute(name = "yDim")
-    protected Integer yDim;
-
-    @XmlAttribute(name = "zDim")
-    protected Integer zDim;
-
-    @XmlAttribute(name = "bgColour")
-    protected Integer bgColour;
-
-    @XmlAttribute(name = "scaleFactor")
-    protected Float scaleFactor;
-
-    @XmlAttribute(name = "showLabels")
-    protected Boolean showLabels;
-
-    @XmlAttribute(name = "linkToAllViews")
-    protected Boolean linkToAllViews;
-
-    @XmlAttribute(name = "width")
-    protected Integer width;
-
-    @XmlAttribute(name = "height")
-    protected Integer height;
-
-    @XmlAttribute(name = "xpos")
-    protected Integer xpos;
-
-    @XmlAttribute(name = "ypos")
-    protected Integer ypos;
-
-    @XmlAttribute(name = "includeGaps")
-    protected Boolean includeGaps;
-
-    @XmlAttribute(name = "matchGaps")
-    protected Boolean matchGaps;
-
-    @XmlAttribute(name = "includeGappedColumns")
-    protected Boolean includeGappedColumns;
-
-    @XmlAttribute(name = "denominateByShortestLength")
-    protected Boolean denominateByShortestLength;
 
     /**
-     * Gets the value of the sequencePoint property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the sequencePoint property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getSequencePoint().add(newItem); &lt;/pre&gt;
-     * 
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.PcaViewer.SequencePoint }
+     * &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;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;
      * 
      * 
      */
-    public List<JalviewModel.PcaViewer.SequencePoint> getSequencePoint()
-    {
-      if (sequencePoint == null)
-      {
-        sequencePoint = new ArrayList<JalviewModel.PcaViewer.SequencePoint>();
-      }
-      return this.sequencePoint;
-    }
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "features",
+        "pdbids",
+        "hiddenSequences",
+        "rnaViewer",
+        "hmmerProfile"
+    })
+    public static class JSeq {
+
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<Feature> features;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.JSeq.Pdbids> pdbids;
+        @XmlElement(namespace = "www.jalview.org", type = Integer.class)
+        protected List<Integer> hiddenSequences;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.JSeq.RnaViewer> rnaViewer;
+        @XmlElement(namespace = "www.jalview.org")
+        protected String hmmerProfile;
+        @XmlAttribute(name = "colour")
+        protected Integer colour;
+        @XmlAttribute(name = "start", required = true)
+        protected int start;
+        @XmlAttribute(name = "end", required = true)
+        protected int end;
+        @XmlAttribute(name = "id", required = true)
+        protected String id;
+        @XmlAttribute(name = "hidden")
+        protected Boolean hidden;
+        @XmlAttribute(name = "viewreference")
+        protected Boolean viewreference;
 
-    /**
-     * Gets the value of the axis property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the axis property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getAxis().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.PcaViewer.Axis }
-     * 
-     * 
-     */
-    public List<JalviewModel.PcaViewer.Axis> getAxis()
-    {
-      if (axis == null)
-      {
-        axis = new ArrayList<JalviewModel.PcaViewer.Axis>();
-      }
-      return this.axis;
-    }
+        /**
+         * Gets the value of the features property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the features property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getFeatures().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link Feature }
+         * 
+         * 
+         */
+        public List<Feature> getFeatures() {
+            if (features == null) {
+                features = new ArrayList<Feature>();
+            }
+            return this.features;
+        }
 
-    /**
-     * Gets the value of the seqPointMin property.
-     * 
-     * @return possible object is {@link JalviewModel.PcaViewer.SeqPointMin }
-     * 
-     */
-    public JalviewModel.PcaViewer.SeqPointMin getSeqPointMin()
-    {
-      return seqPointMin;
-    }
+        /**
+         * Gets the value of the pdbids property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the pdbids property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getPdbids().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.JSeq.Pdbids }
+         * 
+         * 
+         */
+        public List<JalviewModel.JSeq.Pdbids> getPdbids() {
+            if (pdbids == null) {
+                pdbids = new ArrayList<JalviewModel.JSeq.Pdbids>();
+            }
+            return this.pdbids;
+        }
 
-    /**
-     * Sets the value of the seqPointMin property.
-     * 
-     * @param value
-     *          allowed object is {@link JalviewModel.PcaViewer.SeqPointMin }
-     * 
-     */
-    public void setSeqPointMin(JalviewModel.PcaViewer.SeqPointMin value)
-    {
-      this.seqPointMin = value;
-    }
+        /**
+         * Gets the value of the hiddenSequences property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenSequences property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getHiddenSequences().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link Integer }
+         * 
+         * 
+         */
+        public List<Integer> getHiddenSequences() {
+            if (hiddenSequences == null) {
+                hiddenSequences = new ArrayList<Integer>();
+            }
+            return this.hiddenSequences;
+        }
 
-    /**
-     * Gets the value of the seqPointMax property.
-     * 
-     * @return possible object is {@link JalviewModel.PcaViewer.SeqPointMax }
-     * 
-     */
-    public JalviewModel.PcaViewer.SeqPointMax getSeqPointMax()
-    {
-      return seqPointMax;
-    }
+        /**
+         * Gets the value of the rnaViewer property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the rnaViewer property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getRnaViewer().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.JSeq.RnaViewer }
+         * 
+         * 
+         */
+        public List<JalviewModel.JSeq.RnaViewer> getRnaViewer() {
+            if (rnaViewer == null) {
+                rnaViewer = new ArrayList<JalviewModel.JSeq.RnaViewer>();
+            }
+            return this.rnaViewer;
+        }
 
-    /**
-     * Sets the value of the seqPointMax property.
-     * 
-     * @param value
-     *          allowed object is {@link JalviewModel.PcaViewer.SeqPointMax }
-     * 
-     */
-    public void setSeqPointMax(JalviewModel.PcaViewer.SeqPointMax value)
-    {
-      this.seqPointMax = value;
-    }
+        /**
+         * Gets the value of the hmmerProfile property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getHmmerProfile() {
+            return hmmerProfile;
+        }
 
-    /**
-     * Gets the value of the pcaData property.
-     * 
-     * @return possible object is {@link PcaDataType }
-     * 
-     */
-    public PcaDataType getPcaData()
-    {
-      return pcaData;
-    }
+        /**
+         * Sets the value of the hmmerProfile property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setHmmerProfile(String value) {
+            this.hmmerProfile = value;
+        }
 
-    /**
-     * Sets the value of the pcaData property.
-     * 
-     * @param value
-     *          allowed object is {@link PcaDataType }
-     * 
-     */
-    public void setPcaData(PcaDataType value)
-    {
-      this.pcaData = value;
-    }
+        /**
+         * Gets the value of the colour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getColour() {
+            return colour;
+        }
 
-    /**
-     * Gets the value of the title property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getTitle()
-    {
-      return title;
-    }
+        /**
+         * Sets the value of the colour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setColour(Integer value) {
+            this.colour = value;
+        }
 
-    /**
-     * Sets the value of the title property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setTitle(String value)
-    {
-      this.title = value;
-    }
+        /**
+         * Gets the value of the start property.
+         * 
+         */
+        public int getStart() {
+            return start;
+        }
 
-    /**
-     * Gets the value of the scoreModelName property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getScoreModelName()
-    {
-      return scoreModelName;
-    }
+        /**
+         * Sets the value of the start property.
+         * 
+         */
+        public void setStart(int value) {
+            this.start = value;
+        }
 
-    /**
-     * Sets the value of the scoreModelName property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setScoreModelName(String value)
-    {
-      this.scoreModelName = value;
-    }
+        /**
+         * Gets the value of the end property.
+         * 
+         */
+        public int getEnd() {
+            return end;
+        }
 
-    /**
-     * Gets the value of the xDim property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getXDim()
-    {
-      return xDim;
-    }
+        /**
+         * Sets the value of the end property.
+         * 
+         */
+        public void setEnd(int value) {
+            this.end = value;
+        }
 
-    /**
-     * Sets the value of the xDim property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setXDim(Integer value)
-    {
-      this.xDim = value;
-    }
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
 
-    /**
-     * Gets the value of the yDim property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getYDim()
-    {
-      return yDim;
-    }
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
 
-    /**
-     * Sets the value of the yDim property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setYDim(Integer value)
-    {
-      this.yDim = value;
-    }
+        /**
+         * Gets the value of the hidden property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isHidden() {
+            return hidden;
+        }
 
-    /**
-     * Gets the value of the zDim property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getZDim()
-    {
-      return zDim;
-    }
+        /**
+         * Sets the value of the hidden property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setHidden(Boolean value) {
+            this.hidden = value;
+        }
 
-    /**
-     * Sets the value of the zDim property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setZDim(Integer value)
-    {
-      this.zDim = value;
-    }
+        /**
+         * Gets the value of the viewreference property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isViewreference() {
+            return viewreference;
+        }
 
-    /**
-     * Gets the value of the bgColour property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getBgColour()
-    {
-      return bgColour;
-    }
+        /**
+         * Sets the value of the viewreference property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setViewreference(Boolean value) {
+            this.viewreference = value;
+        }
 
-    /**
-     * Sets the value of the bgColour property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setBgColour(Integer value)
-    {
-      this.bgColour = value;
-    }
 
-    /**
-     * Gets the value of the scaleFactor property.
-     * 
-     * @return possible object is {@link Float }
-     * 
-     */
-    public Float getScaleFactor()
-    {
-      return scaleFactor;
-    }
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "structureState"
+        })
+        public static class Pdbids
+            extends Pdbentry
+        {
 
-    /**
-     * Sets the value of the scaleFactor property.
-     * 
-     * @param value
-     *          allowed object is {@link Float }
-     * 
-     */
-    public void setScaleFactor(Float value)
-    {
-      this.scaleFactor = value;
-    }
+            @XmlElement(namespace = "www.jalview.org")
+            protected List<JalviewModel.JSeq.Pdbids.StructureState> structureState;
+
+            /**
+             * Gets the value of the structureState property.
+             * 
+             * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the structureState property.
+             * 
+             * &lt;p&gt;
+             * For example, to add a new item, do as follows:
+             * &lt;pre&gt;
+             *    getStructureState().add(newItem);
+             * &lt;/pre&gt;
+             * 
+             * 
+             * &lt;p&gt;
+             * Objects of the following type(s) are allowed in the list
+             * {@link JalviewModel.JSeq.Pdbids.StructureState }
+             * 
+             * 
+             */
+            public List<JalviewModel.JSeq.Pdbids.StructureState> getStructureState() {
+                if (structureState == null) {
+                    structureState = new ArrayList<JalviewModel.JSeq.Pdbids.StructureState>();
+                }
+                return this.structureState;
+            }
+
+
+            /**
+             * &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;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;
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "", propOrder = {
+                "value"
+            })
+            public static class StructureState {
+
+                @XmlValue
+                protected String value;
+                @XmlAttribute(name = "visible")
+                protected Boolean visible;
+                @XmlAttribute(name = "viewId")
+                protected String viewId;
+                @XmlAttribute(name = "alignwithAlignPanel")
+                protected Boolean alignwithAlignPanel;
+                @XmlAttribute(name = "colourwithAlignPanel")
+                protected Boolean colourwithAlignPanel;
+                @XmlAttribute(name = "colourByJmol")
+                protected Boolean colourByJmol;
+                @XmlAttribute(name = "type")
+                protected String type;
+                @XmlAttribute(name = "width")
+                protected Integer width;
+                @XmlAttribute(name = "height")
+                protected Integer height;
+                @XmlAttribute(name = "xpos")
+                protected Integer xpos;
+                @XmlAttribute(name = "ypos")
+                protected Integer ypos;
+
+                /**
+                 * Gets the value of the value property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getValue() {
+                    return value;
+                }
+
+                /**
+                 * Sets the value of the value property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setValue(String value) {
+                    this.value = value;
+                }
+
+                /**
+                 * Gets the value of the visible property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public Boolean isVisible() {
+                    return visible;
+                }
+
+                /**
+                 * Sets the value of the visible property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setVisible(Boolean value) {
+                    this.visible = value;
+                }
+
+                /**
+                 * Gets the value of the viewId property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getViewId() {
+                    return viewId;
+                }
+
+                /**
+                 * Sets the value of the viewId property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setViewId(String value) {
+                    this.viewId = value;
+                }
+
+                /**
+                 * Gets the value of the alignwithAlignPanel property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isAlignwithAlignPanel() {
+                    if (alignwithAlignPanel == null) {
+                        return true;
+                    } else {
+                        return alignwithAlignPanel;
+                    }
+                }
+
+                /**
+                 * Sets the value of the alignwithAlignPanel property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setAlignwithAlignPanel(Boolean value) {
+                    this.alignwithAlignPanel = value;
+                }
+
+                /**
+                 * Gets the value of the colourwithAlignPanel property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isColourwithAlignPanel() {
+                    if (colourwithAlignPanel == null) {
+                        return false;
+                    } else {
+                        return colourwithAlignPanel;
+                    }
+                }
+
+                /**
+                 * Sets the value of the colourwithAlignPanel property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setColourwithAlignPanel(Boolean value) {
+                    this.colourwithAlignPanel = value;
+                }
+
+                /**
+                 * Gets the value of the colourByJmol property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isColourByJmol() {
+                    if (colourByJmol == null) {
+                        return true;
+                    } else {
+                        return colourByJmol;
+                    }
+                }
+
+                /**
+                 * Sets the value of the colourByJmol property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setColourByJmol(Boolean value) {
+                    this.colourByJmol = value;
+                }
+
+                /**
+                 * Gets the value of the type property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getType() {
+                    return type;
+                }
+
+                /**
+                 * Sets the value of the type property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setType(String value) {
+                    this.type = value;
+                }
+
+                /**
+                 * Gets the value of the width property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getWidth() {
+                    return width;
+                }
+
+                /**
+                 * Sets the value of the width property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setWidth(Integer value) {
+                    this.width = value;
+                }
+
+                /**
+                 * Gets the value of the height property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getHeight() {
+                    return height;
+                }
+
+                /**
+                 * Sets the value of the height property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setHeight(Integer value) {
+                    this.height = value;
+                }
+
+                /**
+                 * Gets the value of the xpos property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getXpos() {
+                    return xpos;
+                }
+
+                /**
+                 * Sets the value of the xpos property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setXpos(Integer value) {
+                    this.xpos = value;
+                }
+
+                /**
+                 * Gets the value of the ypos property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getYpos() {
+                    return ypos;
+                }
+
+                /**
+                 * Sets the value of the ypos property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setYpos(Integer value) {
+                    this.ypos = value;
+                }
+
+            }
 
-    /**
-     * Gets the value of the showLabels property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowLabels()
-    {
-      return showLabels;
-    }
+        }
+
+
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "secondaryStructure"
+        })
+        public static class RnaViewer {
+
+            @XmlElement(namespace = "www.jalview.org", required = true)
+            protected List<JalviewModel.JSeq.RnaViewer.SecondaryStructure> secondaryStructure;
+            @XmlAttribute(name = "title")
+            protected String title;
+            @XmlAttribute(name = "viewId")
+            protected String viewId;
+            @XmlAttribute(name = "dividerLocation")
+            protected Integer dividerLocation;
+            @XmlAttribute(name = "selectedRna")
+            protected Integer selectedRna;
+            @XmlAttribute(name = "width")
+            protected Integer width;
+            @XmlAttribute(name = "height")
+            protected Integer height;
+            @XmlAttribute(name = "xpos")
+            protected Integer xpos;
+            @XmlAttribute(name = "ypos")
+            protected Integer ypos;
+
+            /**
+             * Gets the value of the secondaryStructure property.
+             * 
+             * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the secondaryStructure property.
+             * 
+             * &lt;p&gt;
+             * For example, to add a new item, do as follows:
+             * &lt;pre&gt;
+             *    getSecondaryStructure().add(newItem);
+             * &lt;/pre&gt;
+             * 
+             * 
+             * &lt;p&gt;
+             * Objects of the following type(s) are allowed in the list
+             * {@link JalviewModel.JSeq.RnaViewer.SecondaryStructure }
+             * 
+             * 
+             */
+            public List<JalviewModel.JSeq.RnaViewer.SecondaryStructure> getSecondaryStructure() {
+                if (secondaryStructure == null) {
+                    secondaryStructure = new ArrayList<JalviewModel.JSeq.RnaViewer.SecondaryStructure>();
+                }
+                return this.secondaryStructure;
+            }
+
+            /**
+             * Gets the value of the title property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getTitle() {
+                return title;
+            }
+
+            /**
+             * Sets the value of the title property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setTitle(String value) {
+                this.title = value;
+            }
+
+            /**
+             * Gets the value of the viewId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getViewId() {
+                return viewId;
+            }
+
+            /**
+             * Sets the value of the viewId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setViewId(String value) {
+                this.viewId = value;
+            }
+
+            /**
+             * Gets the value of the dividerLocation property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getDividerLocation() {
+                return dividerLocation;
+            }
+
+            /**
+             * Sets the value of the dividerLocation property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setDividerLocation(Integer value) {
+                this.dividerLocation = value;
+            }
+
+            /**
+             * Gets the value of the selectedRna property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getSelectedRna() {
+                return selectedRna;
+            }
+
+            /**
+             * Sets the value of the selectedRna property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setSelectedRna(Integer value) {
+                this.selectedRna = value;
+            }
+
+            /**
+             * Gets the value of the width property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getWidth() {
+                return width;
+            }
+
+            /**
+             * Sets the value of the width property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setWidth(Integer value) {
+                this.width = value;
+            }
+
+            /**
+             * Gets the value of the height property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getHeight() {
+                return height;
+            }
+
+            /**
+             * Sets the value of the height property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setHeight(Integer value) {
+                this.height = value;
+            }
+
+            /**
+             * Gets the value of the xpos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getXpos() {
+                return xpos;
+            }
+
+            /**
+             * Sets the value of the xpos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setXpos(Integer value) {
+                this.xpos = value;
+            }
+
+            /**
+             * Gets the value of the ypos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getYpos() {
+                return ypos;
+            }
+
+            /**
+             * Sets the value of the ypos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setYpos(Integer value) {
+                this.ypos = value;
+            }
+
+
+            /**
+             * &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="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;
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class SecondaryStructure {
+
+                @XmlAttribute(name = "title")
+                protected String title;
+                @XmlAttribute(name = "annotationId", required = true)
+                protected String annotationId;
+                @XmlAttribute(name = "gapped")
+                protected Boolean gapped;
+                @XmlAttribute(name = "viewerState")
+                protected String viewerState;
+
+                /**
+                 * Gets the value of the title property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getTitle() {
+                    return title;
+                }
+
+                /**
+                 * Sets the value of the title property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setTitle(String value) {
+                    this.title = value;
+                }
+
+                /**
+                 * Gets the value of the annotationId property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getAnnotationId() {
+                    return annotationId;
+                }
+
+                /**
+                 * Sets the value of the annotationId property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setAnnotationId(String value) {
+                    this.annotationId = value;
+                }
+
+                /**
+                 * Gets the value of the gapped property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public Boolean isGapped() {
+                    return gapped;
+                }
+
+                /**
+                 * Sets the value of the gapped property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setGapped(Boolean value) {
+                    this.gapped = value;
+                }
+
+                /**
+                 * Gets the value of the viewerState property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getViewerState() {
+                    return viewerState;
+                }
+
+                /**
+                 * Sets the value of the viewerState property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setViewerState(String value) {
+                    this.viewerState = value;
+                }
+
+            }
+
+        }
 
-    /**
-     * Sets the value of the showLabels property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowLabels(Boolean value)
-    {
-      this.showLabels = value;
     }
 
+
     /**
-     * Gets the value of the linkToAllViews property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isLinkToAllViews()
-    {
-      return linkToAllViews;
-    }
-
-    /**
-     * Sets the value of the linkToAllViews property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setLinkToAllViews(Boolean value)
-    {
-      this.linkToAllViews = value;
-    }
-
-    /**
-     * Gets the value of the width property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getWidth()
-    {
-      return width;
-    }
-
-    /**
-     * Sets the value of the width property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setWidth(Integer value)
-    {
-      this.width = value;
-    }
-
-    /**
-     * Gets the value of the height property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getHeight()
-    {
-      return height;
-    }
-
-    /**
-     * Sets the value of the height property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setHeight(Integer value)
-    {
-      this.height = value;
-    }
-
-    /**
-     * Gets the value of the xpos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getXpos()
-    {
-      return xpos;
-    }
-
-    /**
-     * Sets the value of the xpos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setXpos(Integer value)
-    {
-      this.xpos = value;
-    }
-
-    /**
-     * Gets the value of the ypos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getYpos()
-    {
-      return ypos;
-    }
-
-    /**
-     * Sets the value of the ypos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setYpos(Integer value)
-    {
-      this.ypos = value;
-    }
-
-    /**
-     * Gets the value of the includeGaps property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isIncludeGaps()
-    {
-      return includeGaps;
-    }
-
-    /**
-     * Sets the value of the includeGaps property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setIncludeGaps(Boolean value)
-    {
-      this.includeGaps = value;
-    }
-
-    /**
-     * Gets the value of the matchGaps property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isMatchGaps()
-    {
-      return matchGaps;
-    }
-
-    /**
-     * Sets the value of the matchGaps property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setMatchGaps(Boolean value)
-    {
-      this.matchGaps = value;
-    }
-
-    /**
-     * Gets the value of the includeGappedColumns property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isIncludeGappedColumns()
-    {
-      return includeGappedColumns;
-    }
-
-    /**
-     * Sets the value of the includeGappedColumns property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setIncludeGappedColumns(Boolean value)
-    {
-      this.includeGappedColumns = value;
-    }
-
-    /**
-     * Gets the value of the denominateByShortestLength property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isDenominateByShortestLength()
-    {
-      return denominateByShortestLength;
-    }
-
-    /**
-     * Sets the value of the denominateByShortestLength property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setDenominateByShortestLength(Boolean value)
-    {
-      this.denominateByShortestLength = value;
-    }
-
-    /**
-     * &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;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;
+     * &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;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;
      * 
      * 
      */
     @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class Axis
-    {
-
-      @XmlAttribute(name = "xPos")
-      protected Float xPos;
-
-      @XmlAttribute(name = "yPos")
-      protected Float yPos;
-
-      @XmlAttribute(name = "zPos")
-      protected Float zPos;
-
-      /**
-       * Gets the value of the xPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getXPos()
-      {
-        return xPos;
-      }
-
-      /**
-       * Sets the value of the xPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setXPos(Float value)
-      {
-        this.xPos = value;
-      }
-
-      /**
-       * Gets the value of the yPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getYPos()
-      {
-        return yPos;
-      }
-
-      /**
-       * Sets the value of the yPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setYPos(Float value)
-      {
-        this.yPos = value;
-      }
-
-      /**
-       * Gets the value of the zPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getZPos()
-      {
-        return zPos;
-      }
-
-      /**
-       * Sets the value of the zPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setZPos(Float value)
-      {
-        this.zPos = value;
-      }
+    @XmlType(name = "", propOrder = {
+        "sequencePoint",
+        "axis",
+        "seqPointMin",
+        "seqPointMax",
+        "pcaData"
+    })
+    public static class PcaViewer {
+
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected List<JalviewModel.PcaViewer.SequencePoint> sequencePoint;
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected List<JalviewModel.PcaViewer.Axis> axis;
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected JalviewModel.PcaViewer.SeqPointMin seqPointMin;
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected JalviewModel.PcaViewer.SeqPointMax seqPointMax;
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected PcaDataType pcaData;
+        @XmlAttribute(name = "title")
+        protected String title;
+        @XmlAttribute(name = "scoreModelName")
+        protected String scoreModelName;
+        @XmlAttribute(name = "xDim")
+        protected Integer xDim;
+        @XmlAttribute(name = "yDim")
+        protected Integer yDim;
+        @XmlAttribute(name = "zDim")
+        protected Integer zDim;
+        @XmlAttribute(name = "bgColour")
+        protected Integer bgColour;
+        @XmlAttribute(name = "scaleFactor")
+        protected Float scaleFactor;
+        @XmlAttribute(name = "showLabels")
+        protected Boolean showLabels;
+        @XmlAttribute(name = "linkToAllViews")
+        protected Boolean linkToAllViews;
+        @XmlAttribute(name = "width")
+        protected Integer width;
+        @XmlAttribute(name = "height")
+        protected Integer height;
+        @XmlAttribute(name = "xpos")
+        protected Integer xpos;
+        @XmlAttribute(name = "ypos")
+        protected Integer ypos;
+        @XmlAttribute(name = "includeGaps")
+        protected Boolean includeGaps;
+        @XmlAttribute(name = "matchGaps")
+        protected Boolean matchGaps;
+        @XmlAttribute(name = "includeGappedColumns")
+        protected Boolean includeGappedColumns;
+        @XmlAttribute(name = "denominateByShortestLength")
+        protected Boolean denominateByShortestLength;
 
-    }
+        /**
+         * Gets the value of the sequencePoint property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the sequencePoint property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getSequencePoint().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.PcaViewer.SequencePoint }
+         * 
+         * 
+         */
+        public List<JalviewModel.PcaViewer.SequencePoint> getSequencePoint() {
+            if (sequencePoint == null) {
+                sequencePoint = new ArrayList<JalviewModel.PcaViewer.SequencePoint>();
+            }
+            return this.sequencePoint;
+        }
 
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class SeqPointMax
-    {
-
-      @XmlAttribute(name = "xPos")
-      protected Float xPos;
-
-      @XmlAttribute(name = "yPos")
-      protected Float yPos;
-
-      @XmlAttribute(name = "zPos")
-      protected Float zPos;
-
-      /**
-       * Gets the value of the xPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getXPos()
-      {
-        return xPos;
-      }
-
-      /**
-       * Sets the value of the xPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setXPos(Float value)
-      {
-        this.xPos = value;
-      }
-
-      /**
-       * Gets the value of the yPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getYPos()
-      {
-        return yPos;
-      }
-
-      /**
-       * Sets the value of the yPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setYPos(Float value)
-      {
-        this.yPos = value;
-      }
-
-      /**
-       * Gets the value of the zPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getZPos()
-      {
-        return zPos;
-      }
-
-      /**
-       * Sets the value of the zPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setZPos(Float value)
-      {
-        this.zPos = value;
-      }
+        /**
+         * Gets the value of the axis property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the axis property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getAxis().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.PcaViewer.Axis }
+         * 
+         * 
+         */
+        public List<JalviewModel.PcaViewer.Axis> getAxis() {
+            if (axis == null) {
+                axis = new ArrayList<JalviewModel.PcaViewer.Axis>();
+            }
+            return this.axis;
+        }
 
-    }
+        /**
+         * Gets the value of the seqPointMin property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link JalviewModel.PcaViewer.SeqPointMin }
+         *     
+         */
+        public JalviewModel.PcaViewer.SeqPointMin getSeqPointMin() {
+            return seqPointMin;
+        }
 
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class SeqPointMin
-    {
-
-      @XmlAttribute(name = "xPos")
-      protected Float xPos;
-
-      @XmlAttribute(name = "yPos")
-      protected Float yPos;
-
-      @XmlAttribute(name = "zPos")
-      protected Float zPos;
-
-      /**
-       * Gets the value of the xPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getXPos()
-      {
-        return xPos;
-      }
-
-      /**
-       * Sets the value of the xPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setXPos(Float value)
-      {
-        this.xPos = value;
-      }
-
-      /**
-       * Gets the value of the yPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getYPos()
-      {
-        return yPos;
-      }
-
-      /**
-       * Sets the value of the yPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setYPos(Float value)
-      {
-        this.yPos = value;
-      }
-
-      /**
-       * Gets the value of the zPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getZPos()
-      {
-        return zPos;
-      }
-
-      /**
-       * Sets the value of the zPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setZPos(Float value)
-      {
-        this.zPos = value;
-      }
+        /**
+         * Sets the value of the seqPointMin property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link JalviewModel.PcaViewer.SeqPointMin }
+         *     
+         */
+        public void setSeqPointMin(JalviewModel.PcaViewer.SeqPointMin value) {
+            this.seqPointMin = value;
+        }
 
-    }
+        /**
+         * Gets the value of the seqPointMax property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link JalviewModel.PcaViewer.SeqPointMax }
+         *     
+         */
+        public JalviewModel.PcaViewer.SeqPointMax getSeqPointMax() {
+            return seqPointMax;
+        }
 
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class SequencePoint
-    {
-
-      @XmlAttribute(name = "sequenceRef")
-      protected String sequenceRef;
-
-      @XmlAttribute(name = "xPos")
-      protected Float xPos;
-
-      @XmlAttribute(name = "yPos")
-      protected Float yPos;
-
-      @XmlAttribute(name = "zPos")
-      protected Float zPos;
-
-      /**
-       * Gets the value of the sequenceRef property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getSequenceRef()
-      {
-        return sequenceRef;
-      }
-
-      /**
-       * Sets the value of the sequenceRef property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setSequenceRef(String value)
-      {
-        this.sequenceRef = value;
-      }
-
-      /**
-       * Gets the value of the xPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getXPos()
-      {
-        return xPos;
-      }
-
-      /**
-       * Sets the value of the xPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setXPos(Float value)
-      {
-        this.xPos = value;
-      }
-
-      /**
-       * Gets the value of the yPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getYPos()
-      {
-        return yPos;
-      }
-
-      /**
-       * Sets the value of the yPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setYPos(Float value)
-      {
-        this.yPos = value;
-      }
-
-      /**
-       * Gets the value of the zPos property.
-       * 
-       * @return possible object is {@link Float }
-       * 
-       */
-      public Float getZPos()
-      {
-        return zPos;
-      }
-
-      /**
-       * Sets the value of the zPos property.
-       * 
-       * @param value
-       *          allowed object is {@link Float }
-       * 
-       */
-      public void setZPos(Float value)
-      {
-        this.zPos = value;
-      }
+        /**
+         * Sets the value of the seqPointMax property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link JalviewModel.PcaViewer.SeqPointMax }
+         *     
+         */
+        public void setSeqPointMax(JalviewModel.PcaViewer.SeqPointMax value) {
+            this.seqPointMax = value;
+        }
 
-    }
+        /**
+         * Gets the value of the pcaData property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link PcaDataType }
+         *     
+         */
+        public PcaDataType getPcaData() {
+            return pcaData;
+        }
 
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(name = "", propOrder = { "title", "newick" })
-  public static class Tree
-  {
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected String title;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected String newick;
-
-    @XmlAttribute(name = "fontName")
-    protected String fontName;
-
-    @XmlAttribute(name = "fontSize")
-    protected Integer fontSize;
-
-    @XmlAttribute(name = "fontStyle")
-    protected Integer fontStyle;
-
-    @XmlAttribute(name = "threshold")
-    protected Float threshold;
-
-    @XmlAttribute(name = "showBootstrap")
-    protected Boolean showBootstrap;
-
-    @XmlAttribute(name = "showDistances")
-    protected Boolean showDistances;
-
-    @XmlAttribute(name = "markUnlinked")
-    protected Boolean markUnlinked;
-
-    @XmlAttribute(name = "fitToWindow")
-    protected Boolean fitToWindow;
-
-    @XmlAttribute(name = "currentTree")
-    protected Boolean currentTree;
-
-    @XmlAttribute(name = "id")
-    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
-    @XmlID
-    @XmlSchemaType(name = "ID")
-    protected String id;
-
-    @XmlAttribute(name = "linkToAllViews")
-    protected Boolean linkToAllViews;
-
-    @XmlAttribute(name = "width")
-    protected Integer width;
-
-    @XmlAttribute(name = "height")
-    protected Integer height;
-
-    @XmlAttribute(name = "xpos")
-    protected Integer xpos;
-
-    @XmlAttribute(name = "ypos")
-    protected Integer ypos;
+        /**
+         * Sets the value of the pcaData property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link PcaDataType }
+         *     
+         */
+        public void setPcaData(PcaDataType value) {
+            this.pcaData = value;
+        }
 
-    /**
-     * Gets the value of the title property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getTitle()
-    {
-      return title;
-    }
+        /**
+         * Gets the value of the title property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getTitle() {
+            return title;
+        }
 
-    /**
-     * Sets the value of the title property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setTitle(String value)
-    {
-      this.title = value;
-    }
+        /**
+         * Sets the value of the title property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setTitle(String value) {
+            this.title = value;
+        }
 
-    /**
-     * Gets the value of the newick property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getNewick()
-    {
-      return newick;
-    }
+        /**
+         * Gets the value of the scoreModelName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getScoreModelName() {
+            return scoreModelName;
+        }
 
-    /**
-     * Sets the value of the newick property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setNewick(String value)
-    {
-      this.newick = value;
-    }
+        /**
+         * Sets the value of the scoreModelName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setScoreModelName(String value) {
+            this.scoreModelName = value;
+        }
 
-    /**
-     * Gets the value of the fontName property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getFontName()
-    {
-      return fontName;
-    }
+        /**
+         * Gets the value of the xDim property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXDim() {
+            return xDim;
+        }
 
-    /**
-     * Sets the value of the fontName property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setFontName(String value)
-    {
-      this.fontName = value;
-    }
+        /**
+         * Sets the value of the xDim property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXDim(Integer value) {
+            this.xDim = value;
+        }
 
-    /**
-     * Gets the value of the fontSize property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getFontSize()
-    {
-      return fontSize;
-    }
+        /**
+         * Gets the value of the yDim property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYDim() {
+            return yDim;
+        }
 
-    /**
-     * Sets the value of the fontSize property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setFontSize(Integer value)
-    {
-      this.fontSize = value;
-    }
+        /**
+         * Sets the value of the yDim property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYDim(Integer value) {
+            this.yDim = value;
+        }
 
-    /**
-     * Gets the value of the fontStyle property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getFontStyle()
-    {
-      return fontStyle;
-    }
+        /**
+         * Gets the value of the zDim property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getZDim() {
+            return zDim;
+        }
 
-    /**
-     * Sets the value of the fontStyle property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setFontStyle(Integer value)
-    {
-      this.fontStyle = value;
-    }
+        /**
+         * Sets the value of the zDim property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setZDim(Integer value) {
+            this.zDim = value;
+        }
 
-    /**
-     * Gets the value of the threshold property.
-     * 
-     * @return possible object is {@link Float }
-     * 
-     */
-    public Float getThreshold()
-    {
-      return threshold;
-    }
+        /**
+         * Gets the value of the bgColour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getBgColour() {
+            return bgColour;
+        }
 
-    /**
-     * Sets the value of the threshold property.
-     * 
-     * @param value
-     *          allowed object is {@link Float }
-     * 
-     */
-    public void setThreshold(Float value)
-    {
-      this.threshold = value;
-    }
+        /**
+         * Sets the value of the bgColour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setBgColour(Integer value) {
+            this.bgColour = value;
+        }
 
-    /**
-     * Gets the value of the showBootstrap property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowBootstrap()
-    {
-      return showBootstrap;
-    }
+        /**
+         * Gets the value of the scaleFactor property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Float }
+         *     
+         */
+        public Float getScaleFactor() {
+            return scaleFactor;
+        }
 
-    /**
-     * Sets the value of the showBootstrap property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowBootstrap(Boolean value)
-    {
-      this.showBootstrap = value;
-    }
+        /**
+         * Sets the value of the scaleFactor property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Float }
+         *     
+         */
+        public void setScaleFactor(Float value) {
+            this.scaleFactor = value;
+        }
 
-    /**
-     * Gets the value of the showDistances property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowDistances()
-    {
-      return showDistances;
-    }
+        /**
+         * Gets the value of the showLabels property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowLabels() {
+            return showLabels;
+        }
 
-    /**
-     * Sets the value of the showDistances property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowDistances(Boolean value)
-    {
-      this.showDistances = value;
-    }
+        /**
+         * Sets the value of the showLabels property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowLabels(Boolean value) {
+            this.showLabels = value;
+        }
 
-    /**
-     * Gets the value of the markUnlinked property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isMarkUnlinked()
-    {
-      return markUnlinked;
-    }
+        /**
+         * Gets the value of the linkToAllViews property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isLinkToAllViews() {
+            return linkToAllViews;
+        }
 
-    /**
-     * Sets the value of the markUnlinked property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setMarkUnlinked(Boolean value)
-    {
-      this.markUnlinked = value;
-    }
+        /**
+         * Sets the value of the linkToAllViews property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setLinkToAllViews(Boolean value) {
+            this.linkToAllViews = value;
+        }
 
-    /**
-     * Gets the value of the fitToWindow property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isFitToWindow()
-    {
-      return fitToWindow;
-    }
+        /**
+         * Gets the value of the width property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getWidth() {
+            return width;
+        }
 
-    /**
-     * Sets the value of the fitToWindow property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setFitToWindow(Boolean value)
-    {
-      this.fitToWindow = value;
-    }
+        /**
+         * Sets the value of the width property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setWidth(Integer value) {
+            this.width = value;
+        }
 
-    /**
-     * Gets the value of the currentTree property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isCurrentTree()
-    {
-      return currentTree;
-    }
+        /**
+         * Gets the value of the height property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getHeight() {
+            return height;
+        }
 
-    /**
-     * Sets the value of the currentTree property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setCurrentTree(Boolean value)
-    {
-      this.currentTree = value;
-    }
+        /**
+         * Sets the value of the height property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setHeight(Integer value) {
+            this.height = value;
+        }
 
-    /**
-     * Gets the value of the id property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getId()
-    {
-      return id;
-    }
+        /**
+         * Gets the value of the xpos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXpos() {
+            return xpos;
+        }
 
-    /**
-     * Sets the value of the id property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setId(String value)
-    {
-      this.id = value;
-    }
+        /**
+         * Sets the value of the xpos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXpos(Integer value) {
+            this.xpos = value;
+        }
 
-    /**
-     * Gets the value of the linkToAllViews property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isLinkToAllViews()
-    {
-      if (linkToAllViews == null)
-      {
-        return false;
-      }
-      else
-      {
-        return linkToAllViews;
-      }
-    }
+        /**
+         * Gets the value of the ypos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYpos() {
+            return ypos;
+        }
 
-    /**
-     * Sets the value of the linkToAllViews property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setLinkToAllViews(Boolean value)
-    {
-      this.linkToAllViews = value;
-    }
+        /**
+         * Sets the value of the ypos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYpos(Integer value) {
+            this.ypos = value;
+        }
 
-    /**
-     * Gets the value of the width property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getWidth()
-    {
-      return width;
-    }
+        /**
+         * Gets the value of the includeGaps property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isIncludeGaps() {
+            return includeGaps;
+        }
 
-    /**
-     * Sets the value of the width property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setWidth(Integer value)
-    {
-      this.width = value;
-    }
+        /**
+         * Sets the value of the includeGaps property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIncludeGaps(Boolean value) {
+            this.includeGaps = value;
+        }
 
-    /**
-     * Gets the value of the height property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getHeight()
-    {
-      return height;
-    }
+        /**
+         * Gets the value of the matchGaps property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isMatchGaps() {
+            return matchGaps;
+        }
 
-    /**
-     * Sets the value of the height property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setHeight(Integer value)
-    {
-      this.height = value;
-    }
+        /**
+         * Sets the value of the matchGaps property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setMatchGaps(Boolean value) {
+            this.matchGaps = value;
+        }
 
-    /**
-     * Gets the value of the xpos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getXpos()
-    {
-      return xpos;
-    }
+        /**
+         * Gets the value of the includeGappedColumns property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isIncludeGappedColumns() {
+            return includeGappedColumns;
+        }
 
-    /**
-     * Sets the value of the xpos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setXpos(Integer value)
-    {
-      this.xpos = value;
-    }
+        /**
+         * Sets the value of the includeGappedColumns property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIncludeGappedColumns(Boolean value) {
+            this.includeGappedColumns = value;
+        }
 
-    /**
-     * Gets the value of the ypos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getYpos()
-    {
-      return ypos;
-    }
+        /**
+         * Gets the value of the denominateByShortestLength property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isDenominateByShortestLength() {
+            return denominateByShortestLength;
+        }
 
-    /**
-     * Sets the value of the ypos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setYpos(Integer value)
-    {
-      this.ypos = value;
-    }
+        /**
+         * Sets the value of the denominateByShortestLength property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setDenominateByShortestLength(Boolean value) {
+            this.denominateByShortestLength = value;
+        }
 
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(name = "", propOrder = { "userColourScheme" })
-  public static class UserColours
-  {
-
-    @XmlElement(
-      name = "UserColourScheme",
-      namespace = "www.jalview.org",
-      required = true)
-    protected JalviewUserColours userColourScheme;
-
-    @XmlAttribute(name = "id")
-    protected String id;
 
-    /**
-     * Gets the value of the userColourScheme property.
-     * 
-     * @return possible object is {@link JalviewUserColours }
-     * 
-     */
-    public JalviewUserColours getUserColourScheme()
-    {
-      return userColourScheme;
-    }
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class Axis {
+
+            @XmlAttribute(name = "xPos")
+            protected Float xPos;
+            @XmlAttribute(name = "yPos")
+            protected Float yPos;
+            @XmlAttribute(name = "zPos")
+            protected Float zPos;
+
+            /**
+             * Gets the value of the xPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getXPos() {
+                return xPos;
+            }
+
+            /**
+             * Sets the value of the xPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setXPos(Float value) {
+                this.xPos = value;
+            }
+
+            /**
+             * Gets the value of the yPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getYPos() {
+                return yPos;
+            }
+
+            /**
+             * Sets the value of the yPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setYPos(Float value) {
+                this.yPos = value;
+            }
+
+            /**
+             * Gets the value of the zPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getZPos() {
+                return zPos;
+            }
+
+            /**
+             * Sets the value of the zPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setZPos(Float value) {
+                this.zPos = value;
+            }
 
-    /**
-     * Sets the value of the userColourScheme property.
-     * 
-     * @param value
-     *          allowed object is {@link JalviewUserColours }
-     * 
-     */
-    public void setUserColourScheme(JalviewUserColours value)
-    {
-      this.userColourScheme = value;
-    }
+        }
 
-    /**
-     * Gets the value of the id property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getId()
-    {
-      return id;
-    }
 
-    /**
-     * Sets the value of the id property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setId(String value)
-    {
-      this.id = value;
-    }
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class SeqPointMax {
+
+            @XmlAttribute(name = "xPos")
+            protected Float xPos;
+            @XmlAttribute(name = "yPos")
+            protected Float yPos;
+            @XmlAttribute(name = "zPos")
+            protected Float zPos;
+
+            /**
+             * Gets the value of the xPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getXPos() {
+                return xPos;
+            }
+
+            /**
+             * Sets the value of the xPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setXPos(Float value) {
+                this.xPos = value;
+            }
+
+            /**
+             * Gets the value of the yPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getYPos() {
+                return yPos;
+            }
+
+            /**
+             * Sets the value of the yPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setYPos(Float value) {
+                this.yPos = value;
+            }
+
+            /**
+             * Gets the value of the zPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getZPos() {
+                return zPos;
+            }
+
+            /**
+             * Sets the value of the zPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setZPos(Float value) {
+                this.zPos = value;
+            }
+
+        }
 
-  }
-
-  /**
-   * &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;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;
-   * 
-   * 
-   */
-  @XmlAccessorType(XmlAccessType.FIELD)
-  @XmlType(
-    name = "",
-    propOrder =
-    { "annotationColours", "hiddenColumns", "calcIdParam" })
-  public static class Viewport
-  {
-
-    @XmlElement(name = "AnnotationColours", namespace = "www.jalview.org")
-    protected AnnotationColourScheme annotationColours;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.Viewport.HiddenColumns> hiddenColumns;
-
-    @XmlElement(namespace = "www.jalview.org")
-    protected List<JalviewModel.Viewport.CalcIdParam> calcIdParam;
-
-    @XmlAttribute(name = "conservationSelected")
-    protected Boolean conservationSelected;
-
-    @XmlAttribute(name = "pidSelected")
-    protected Boolean pidSelected;
-
-    @XmlAttribute(name = "bgColour")
-    protected String bgColour;
-
-    @XmlAttribute(name = "consThreshold")
-    protected Integer consThreshold;
-
-    @XmlAttribute(name = "pidThreshold")
-    protected Integer pidThreshold;
-
-    @XmlAttribute(name = "title")
-    protected String title;
-
-    @XmlAttribute(name = "showFullId")
-    protected Boolean showFullId;
-
-    @XmlAttribute(name = "rightAlignIds")
-    protected Boolean rightAlignIds;
-
-    @XmlAttribute(name = "showText")
-    protected Boolean showText;
-
-    @XmlAttribute(name = "showColourText")
-    protected Boolean showColourText;
-
-    @XmlAttribute(name = "showUnconserved")
-    protected Boolean showUnconserved;
-
-    @XmlAttribute(name = "showBoxes")
-    protected Boolean showBoxes;
-
-    @XmlAttribute(name = "wrapAlignment")
-    protected Boolean wrapAlignment;
-
-    @XmlAttribute(name = "renderGaps")
-    protected Boolean renderGaps;
 
-    @XmlAttribute(name = "showSequenceFeatures")
-    protected Boolean showSequenceFeatures;
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class SeqPointMin {
+
+            @XmlAttribute(name = "xPos")
+            protected Float xPos;
+            @XmlAttribute(name = "yPos")
+            protected Float yPos;
+            @XmlAttribute(name = "zPos")
+            protected Float zPos;
+
+            /**
+             * Gets the value of the xPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getXPos() {
+                return xPos;
+            }
+
+            /**
+             * Sets the value of the xPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setXPos(Float value) {
+                this.xPos = value;
+            }
+
+            /**
+             * Gets the value of the yPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getYPos() {
+                return yPos;
+            }
+
+            /**
+             * Sets the value of the yPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setYPos(Float value) {
+                this.yPos = value;
+            }
+
+            /**
+             * Gets the value of the zPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getZPos() {
+                return zPos;
+            }
+
+            /**
+             * Sets the value of the zPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setZPos(Float value) {
+                this.zPos = value;
+            }
 
-    @XmlAttribute(name = "showNPfeatureTooltip")
-    protected Boolean showNPfeatureTooltip;
+        }
 
-    @XmlAttribute(name = "showDbRefTooltip")
-    protected Boolean showDbRefTooltip;
 
-    @XmlAttribute(name = "followHighlight")
-    protected Boolean followHighlight;
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class SequencePoint {
+
+            @XmlAttribute(name = "sequenceRef")
+            protected String sequenceRef;
+            @XmlAttribute(name = "xPos")
+            protected Float xPos;
+            @XmlAttribute(name = "yPos")
+            protected Float yPos;
+            @XmlAttribute(name = "zPos")
+            protected Float zPos;
+
+            /**
+             * Gets the value of the sequenceRef property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getSequenceRef() {
+                return sequenceRef;
+            }
+
+            /**
+             * Sets the value of the sequenceRef property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setSequenceRef(String value) {
+                this.sequenceRef = value;
+            }
+
+            /**
+             * Gets the value of the xPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getXPos() {
+                return xPos;
+            }
+
+            /**
+             * Sets the value of the xPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setXPos(Float value) {
+                this.xPos = value;
+            }
+
+            /**
+             * Gets the value of the yPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getYPos() {
+                return yPos;
+            }
+
+            /**
+             * Sets the value of the yPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setYPos(Float value) {
+                this.yPos = value;
+            }
+
+            /**
+             * Gets the value of the zPos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getZPos() {
+                return zPos;
+            }
+
+            /**
+             * Sets the value of the zPos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setZPos(Float value) {
+                this.zPos = value;
+            }
 
-    @XmlAttribute(name = "followSelection")
-    protected Boolean followSelection;
+        }
 
-    @XmlAttribute(name = "showAnnotation")
-    protected Boolean showAnnotation;
+    }
 
-    @XmlAttribute(name = "centreColumnLabels")
-    protected Boolean centreColumnLabels;
 
-    @XmlAttribute(name = "showGroupConservation")
-    protected Boolean showGroupConservation;
+    /**
+     * &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;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;
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "title",
+        "newick"
+    })
+    public static class Tree {
 
-    @XmlAttribute(name = "showGroupConsensus")
-    protected Boolean showGroupConsensus;
+        @XmlElement(namespace = "www.jalview.org")
+        protected String title;
+        @XmlElement(namespace = "www.jalview.org")
+        protected String newick;
+        @XmlAttribute(name = "fontName")
+        protected String fontName;
+        @XmlAttribute(name = "fontSize")
+        protected Integer fontSize;
+        @XmlAttribute(name = "fontStyle")
+        protected Integer fontStyle;
+        @XmlAttribute(name = "threshold")
+        protected Float threshold;
+        @XmlAttribute(name = "showBootstrap")
+        protected Boolean showBootstrap;
+        @XmlAttribute(name = "showDistances")
+        protected Boolean showDistances;
+        @XmlAttribute(name = "markUnlinked")
+        protected Boolean markUnlinked;
+        @XmlAttribute(name = "fitToWindow")
+        protected Boolean fitToWindow;
+        @XmlAttribute(name = "currentTree")
+        protected Boolean currentTree;
+        @XmlAttribute(name = "id")
+        @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+        @XmlID
+        @XmlSchemaType(name = "ID")
+        protected String id;
+        @XmlAttribute(name = "linkToAllViews")
+        protected Boolean linkToAllViews;
+        @XmlAttribute(name = "width")
+        protected Integer width;
+        @XmlAttribute(name = "height")
+        protected Integer height;
+        @XmlAttribute(name = "xpos")
+        protected Integer xpos;
+        @XmlAttribute(name = "ypos")
+        protected Integer ypos;
 
-    @XmlAttribute(name = "showConsensusHistogram")
-    protected Boolean showConsensusHistogram;
+        /**
+         * Gets the value of the title property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getTitle() {
+            return title;
+        }
 
-    @XmlAttribute(name = "showSequenceLogo")
-    protected Boolean showSequenceLogo;
+        /**
+         * Sets the value of the title property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setTitle(String value) {
+            this.title = value;
+        }
 
-    @XmlAttribute(name = "normaliseSequenceLogo")
-    protected Boolean normaliseSequenceLogo;
+        /**
+         * Gets the value of the newick property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getNewick() {
+            return newick;
+        }
 
-    @XmlAttribute(name = "ignoreGapsinConsensus")
-    protected Boolean ignoreGapsinConsensus;
+        /**
+         * Sets the value of the newick property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setNewick(String value) {
+            this.newick = value;
+        }
 
-    @XmlAttribute(name = "startRes")
-    protected Integer startRes;
+        /**
+         * Gets the value of the fontName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getFontName() {
+            return fontName;
+        }
 
-    @XmlAttribute(name = "startSeq")
-    protected Integer startSeq;
+        /**
+         * Sets the value of the fontName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setFontName(String value) {
+            this.fontName = value;
+        }
 
-    @XmlAttribute(name = "fontName")
-    protected String fontName;
+        /**
+         * Gets the value of the fontSize property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontSize() {
+            return fontSize;
+        }
 
-    @XmlAttribute(name = "fontSize")
-    protected Integer fontSize;
+        /**
+         * Sets the value of the fontSize property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontSize(Integer value) {
+            this.fontSize = value;
+        }
 
-    @XmlAttribute(name = "fontStyle")
-    protected Integer fontStyle;
+        /**
+         * Gets the value of the fontStyle property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontStyle() {
+            return fontStyle;
+        }
 
-    @XmlAttribute(name = "scaleProteinAsCdna")
-    protected Boolean scaleProteinAsCdna;
+        /**
+         * Sets the value of the fontStyle property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontStyle(Integer value) {
+            this.fontStyle = value;
+        }
 
-    @XmlAttribute(name = "viewName")
-    protected String viewName;
+        /**
+         * Gets the value of the threshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Float }
+         *     
+         */
+        public Float getThreshold() {
+            return threshold;
+        }
 
-    @XmlAttribute(name = "sequenceSetId")
-    protected String sequenceSetId;
+        /**
+         * Sets the value of the threshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Float }
+         *     
+         */
+        public void setThreshold(Float value) {
+            this.threshold = value;
+        }
 
-    @XmlAttribute(name = "gatheredViews")
-    protected Boolean gatheredViews;
+        /**
+         * Gets the value of the showBootstrap property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowBootstrap() {
+            return showBootstrap;
+        }
 
-    @XmlAttribute(name = "textCol1")
-    protected Integer textCol1;
+        /**
+         * Sets the value of the showBootstrap property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowBootstrap(Boolean value) {
+            this.showBootstrap = value;
+        }
 
-    @XmlAttribute(name = "textCol2")
-    protected Integer textCol2;
+        /**
+         * Gets the value of the showDistances property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowDistances() {
+            return showDistances;
+        }
 
-    @XmlAttribute(name = "textColThreshold")
-    protected Integer textColThreshold;
+        /**
+         * Sets the value of the showDistances property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowDistances(Boolean value) {
+            this.showDistances = value;
+        }
 
-    @XmlAttribute(name = "id")
-    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
-    @XmlID
-    @XmlSchemaType(name = "ID")
-    protected String id;
+        /**
+         * Gets the value of the markUnlinked property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isMarkUnlinked() {
+            return markUnlinked;
+        }
 
-    @XmlAttribute(name = "complementId")
-    protected String complementId;
+        /**
+         * Sets the value of the markUnlinked property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setMarkUnlinked(Boolean value) {
+            this.markUnlinked = value;
+        }
 
-    @XmlAttribute(name = "showComplementFeatures")
-    protected Boolean showComplementFeatures;
+        /**
+         * Gets the value of the fitToWindow property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isFitToWindow() {
+            return fitToWindow;
+        }
 
-    @XmlAttribute(name = "showComplementFeaturesOnTop")
-    protected Boolean showComplementFeaturesOnTop;
+        /**
+         * Sets the value of the fitToWindow property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFitToWindow(Boolean value) {
+            this.fitToWindow = value;
+        }
 
-    @XmlAttribute(name = "width")
-    protected Integer width;
+        /**
+         * Gets the value of the currentTree property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isCurrentTree() {
+            return currentTree;
+        }
 
-    @XmlAttribute(name = "height")
-    protected Integer height;
+        /**
+         * Sets the value of the currentTree property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setCurrentTree(Boolean value) {
+            this.currentTree = value;
+        }
 
-    @XmlAttribute(name = "xpos")
-    protected Integer xpos;
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
 
-    @XmlAttribute(name = "ypos")
-    protected Integer ypos;
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
 
-    /**
-     * Gets the value of the annotationColours property.
-     * 
-     * @return possible object is {@link AnnotationColourScheme }
-     * 
-     */
-    public AnnotationColourScheme getAnnotationColours()
-    {
-      return annotationColours;
-    }
+        /**
+         * Gets the value of the linkToAllViews property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isLinkToAllViews() {
+            if (linkToAllViews == null) {
+                return false;
+            } else {
+                return linkToAllViews;
+            }
+        }
 
-    /**
-     * Sets the value of the annotationColours property.
-     * 
-     * @param value
-     *          allowed object is {@link AnnotationColourScheme }
-     * 
-     */
-    public void setAnnotationColours(AnnotationColourScheme value)
-    {
-      this.annotationColours = value;
-    }
+        /**
+         * Sets the value of the linkToAllViews property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setLinkToAllViews(Boolean value) {
+            this.linkToAllViews = value;
+        }
 
-    /**
-     * Gets the value of the hiddenColumns property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenColumns property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getHiddenColumns().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.Viewport.HiddenColumns }
-     * 
-     * 
-     */
-    public List<JalviewModel.Viewport.HiddenColumns> getHiddenColumns()
-    {
-      if (hiddenColumns == null)
-      {
-        hiddenColumns = new ArrayList<JalviewModel.Viewport.HiddenColumns>();
-      }
-      return this.hiddenColumns;
-    }
+        /**
+         * Gets the value of the width property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getWidth() {
+            return width;
+        }
 
-    /**
-     * Gets the value of the calcIdParam property.
-     * 
-     * &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
-     * &lt;CODE&gt;set&lt;/CODE&gt; method for the calcIdParam property.
-     * 
-     * &lt;p&gt; For example, to add a new item, do as follows: &lt;pre&gt;
-     * getCalcIdParam().add(newItem); &lt;/pre&gt;
-     * 
-     * 
-     * &lt;p&gt; Objects of the following type(s) are allowed in the list
-     * {@link JalviewModel.Viewport.CalcIdParam }
-     * 
-     * 
-     */
-    public List<JalviewModel.Viewport.CalcIdParam> getCalcIdParam()
-    {
-      if (calcIdParam == null)
-      {
-        calcIdParam = new ArrayList<JalviewModel.Viewport.CalcIdParam>();
-      }
-      return this.calcIdParam;
-    }
+        /**
+         * Sets the value of the width property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setWidth(Integer value) {
+            this.width = value;
+        }
 
-    /**
-     * Gets the value of the conservationSelected property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isConservationSelected()
-    {
-      return conservationSelected;
-    }
+        /**
+         * Gets the value of the height property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getHeight() {
+            return height;
+        }
 
-    /**
-     * Sets the value of the conservationSelected property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setConservationSelected(Boolean value)
-    {
-      this.conservationSelected = value;
-    }
+        /**
+         * Sets the value of the height property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setHeight(Integer value) {
+            this.height = value;
+        }
 
-    /**
-     * Gets the value of the pidSelected property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isPidSelected()
-    {
-      return pidSelected;
-    }
+        /**
+         * Gets the value of the xpos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXpos() {
+            return xpos;
+        }
 
-    /**
-     * Sets the value of the pidSelected property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setPidSelected(Boolean value)
-    {
-      this.pidSelected = value;
-    }
+        /**
+         * Sets the value of the xpos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXpos(Integer value) {
+            this.xpos = value;
+        }
 
-    /**
-     * Gets the value of the bgColour property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getBgColour()
-    {
-      return bgColour;
-    }
+        /**
+         * Gets the value of the ypos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYpos() {
+            return ypos;
+        }
 
-    /**
-     * Sets the value of the bgColour property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setBgColour(String value)
-    {
-      this.bgColour = value;
-    }
+        /**
+         * Sets the value of the ypos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYpos(Integer value) {
+            this.ypos = value;
+        }
 
-    /**
-     * Gets the value of the consThreshold property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getConsThreshold()
-    {
-      return consThreshold;
     }
 
-    /**
-     * Sets the value of the consThreshold property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setConsThreshold(Integer value)
-    {
-      this.consThreshold = value;
-    }
 
     /**
-     * Gets the value of the pidThreshold property.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * @return possible object is {@link Integer }
+     * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
      * 
-     */
-    public Integer getPidThreshold()
-    {
-      return pidThreshold;
-    }
-
-    /**
-     * Sets the value of the pidThreshold property.
+     * &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;
      * 
-     * @param value
-     *          allowed object is {@link Integer }
      * 
      */
-    public void setPidThreshold(Integer value)
-    {
-      this.pidThreshold = value;
-    }
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "userColourScheme"
+    })
+    public static class UserColours {
 
-    /**
-     * Gets the value of the title property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getTitle()
-    {
-      return title;
-    }
+        @XmlElement(name = "UserColourScheme", namespace = "www.jalview.org", required = true)
+        protected JalviewUserColours userColourScheme;
+        @XmlAttribute(name = "id")
+        protected String id;
 
-    /**
-     * Sets the value of the title property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setTitle(String value)
-    {
-      this.title = value;
-    }
+        /**
+         * Gets the value of the userColourScheme property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link JalviewUserColours }
+         *     
+         */
+        public JalviewUserColours getUserColourScheme() {
+            return userColourScheme;
+        }
 
-    /**
-     * Gets the value of the showFullId property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowFullId()
-    {
-      return showFullId;
-    }
+        /**
+         * Sets the value of the userColourScheme property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link JalviewUserColours }
+         *     
+         */
+        public void setUserColourScheme(JalviewUserColours value) {
+            this.userColourScheme = value;
+        }
 
-    /**
-     * Sets the value of the showFullId property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowFullId(Boolean value)
-    {
-      this.showFullId = value;
-    }
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
 
-    /**
-     * Gets the value of the rightAlignIds property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isRightAlignIds()
-    {
-      return rightAlignIds;
-    }
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
 
-    /**
-     * Sets the value of the rightAlignIds property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setRightAlignIds(Boolean value)
-    {
-      this.rightAlignIds = value;
     }
 
-    /**
-     * Gets the value of the showText property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowText()
-    {
-      return showText;
-    }
 
     /**
-     * Sets the value of the showText property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     */
-    public void setShowText(Boolean value)
-    {
-      this.showText = value;
-    }
-
-    /**
-     * Gets the value of the showColourText property.
+     * &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;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;
      * 
-     * @return possible object is {@link Boolean }
      * 
      */
-    public Boolean isShowColourText()
-    {
-      return showColourText;
-    }
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "annotationColours",
+        "hiddenColumns",
+        "calcIdParam"
+    })
+    public static class Viewport {
+
+        @XmlElement(name = "AnnotationColours", namespace = "www.jalview.org")
+        protected AnnotationColourScheme annotationColours;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.Viewport.HiddenColumns> hiddenColumns;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModel.Viewport.CalcIdParam> calcIdParam;
+        @XmlAttribute(name = "conservationSelected")
+        protected Boolean conservationSelected;
+        @XmlAttribute(name = "pidSelected")
+        protected Boolean pidSelected;
+        @XmlAttribute(name = "bgColour")
+        protected String bgColour;
+        @XmlAttribute(name = "consThreshold")
+        protected Integer consThreshold;
+        @XmlAttribute(name = "pidThreshold")
+        protected Integer pidThreshold;
+        @XmlAttribute(name = "title")
+        protected String title;
+        @XmlAttribute(name = "showFullId")
+        protected Boolean showFullId;
+        @XmlAttribute(name = "rightAlignIds")
+        protected Boolean rightAlignIds;
+        @XmlAttribute(name = "showText")
+        protected Boolean showText;
+        @XmlAttribute(name = "showColourText")
+        protected Boolean showColourText;
+        @XmlAttribute(name = "showUnconserved")
+        protected Boolean showUnconserved;
+        @XmlAttribute(name = "showBoxes")
+        protected Boolean showBoxes;
+        @XmlAttribute(name = "wrapAlignment")
+        protected Boolean wrapAlignment;
+        @XmlAttribute(name = "renderGaps")
+        protected Boolean renderGaps;
+        @XmlAttribute(name = "showSequenceFeatures")
+        protected Boolean showSequenceFeatures;
+        @XmlAttribute(name = "showNPfeatureTooltip")
+        protected Boolean showNPfeatureTooltip;
+        @XmlAttribute(name = "showDbRefTooltip")
+        protected Boolean showDbRefTooltip;
+        @XmlAttribute(name = "followHighlight")
+        protected Boolean followHighlight;
+        @XmlAttribute(name = "followSelection")
+        protected Boolean followSelection;
+        @XmlAttribute(name = "showAnnotation")
+        protected Boolean showAnnotation;
+        @XmlAttribute(name = "centreColumnLabels")
+        protected Boolean centreColumnLabels;
+        @XmlAttribute(name = "showGroupConservation")
+        protected Boolean showGroupConservation;
+        @XmlAttribute(name = "showGroupConsensus")
+        protected Boolean showGroupConsensus;
+        @XmlAttribute(name = "showConsensusHistogram")
+        protected Boolean showConsensusHistogram;
+        @XmlAttribute(name = "showSequenceLogo")
+        protected Boolean showSequenceLogo;
+        @XmlAttribute(name = "normaliseSequenceLogo")
+        protected Boolean normaliseSequenceLogo;
+        @XmlAttribute(name = "ignoreGapsinConsensus")
+        protected Boolean ignoreGapsinConsensus;
+        @XmlAttribute(name = "startRes")
+        protected Integer startRes;
+        @XmlAttribute(name = "startSeq")
+        protected Integer startSeq;
+        @XmlAttribute(name = "fontName")
+        protected String fontName;
+        @XmlAttribute(name = "fontSize")
+        protected Integer fontSize;
+        @XmlAttribute(name = "fontStyle")
+        protected Integer fontStyle;
+        @XmlAttribute(name = "scaleProteinAsCdna")
+        protected Boolean scaleProteinAsCdna;
+        @XmlAttribute(name = "viewName")
+        protected String viewName;
+        @XmlAttribute(name = "sequenceSetId")
+        protected String sequenceSetId;
+        @XmlAttribute(name = "gatheredViews")
+        protected Boolean gatheredViews;
+        @XmlAttribute(name = "textCol1")
+        protected Integer textCol1;
+        @XmlAttribute(name = "textCol2")
+        protected Integer textCol2;
+        @XmlAttribute(name = "textColThreshold")
+        protected Integer textColThreshold;
+        @XmlAttribute(name = "id")
+        @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+        @XmlID
+        @XmlSchemaType(name = "ID")
+        protected String id;
+        @XmlAttribute(name = "complementId")
+        protected String complementId;
+        @XmlAttribute(name = "showComplementFeatures")
+        protected Boolean showComplementFeatures;
+        @XmlAttribute(name = "showComplementFeaturesOnTop")
+        protected Boolean showComplementFeaturesOnTop;
+        @XmlAttribute(name = "width")
+        protected Integer width;
+        @XmlAttribute(name = "height")
+        protected Integer height;
+        @XmlAttribute(name = "xpos")
+        protected Integer xpos;
+        @XmlAttribute(name = "ypos")
+        protected Integer ypos;
 
-    /**
-     * Sets the value of the showColourText property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowColourText(Boolean value)
-    {
-      this.showColourText = value;
-    }
+        /**
+         * Gets the value of the annotationColours property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public AnnotationColourScheme getAnnotationColours() {
+            return annotationColours;
+        }
 
-    /**
-     * Gets the value of the showUnconserved property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowUnconserved()
-    {
-      if (showUnconserved == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showUnconserved;
-      }
-    }
+        /**
+         * Sets the value of the annotationColours property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public void setAnnotationColours(AnnotationColourScheme value) {
+            this.annotationColours = value;
+        }
 
-    /**
-     * Sets the value of the showUnconserved property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowUnconserved(Boolean value)
-    {
-      this.showUnconserved = value;
-    }
+        /**
+         * Gets the value of the hiddenColumns property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenColumns property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getHiddenColumns().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.Viewport.HiddenColumns }
+         * 
+         * 
+         */
+        public List<JalviewModel.Viewport.HiddenColumns> getHiddenColumns() {
+            if (hiddenColumns == null) {
+                hiddenColumns = new ArrayList<JalviewModel.Viewport.HiddenColumns>();
+            }
+            return this.hiddenColumns;
+        }
 
-    /**
-     * Gets the value of the showBoxes property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowBoxes()
-    {
-      return showBoxes;
-    }
+        /**
+         * Gets the value of the calcIdParam property.
+         * 
+         * &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 &lt;CODE&gt;set&lt;/CODE&gt; method for the calcIdParam property.
+         * 
+         * &lt;p&gt;
+         * For example, to add a new item, do as follows:
+         * &lt;pre&gt;
+         *    getCalcIdParam().add(newItem);
+         * &lt;/pre&gt;
+         * 
+         * 
+         * &lt;p&gt;
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModel.Viewport.CalcIdParam }
+         * 
+         * 
+         */
+        public List<JalviewModel.Viewport.CalcIdParam> getCalcIdParam() {
+            if (calcIdParam == null) {
+                calcIdParam = new ArrayList<JalviewModel.Viewport.CalcIdParam>();
+            }
+            return this.calcIdParam;
+        }
+
+        /**
+         * Gets the value of the conservationSelected property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isConservationSelected() {
+            return conservationSelected;
+        }
+
+        /**
+         * Sets the value of the conservationSelected property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setConservationSelected(Boolean value) {
+            this.conservationSelected = value;
+        }
+
+        /**
+         * Gets the value of the pidSelected property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isPidSelected() {
+            return pidSelected;
+        }
+
+        /**
+         * Sets the value of the pidSelected property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setPidSelected(Boolean value) {
+            this.pidSelected = value;
+        }
+
+        /**
+         * Gets the value of the bgColour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getBgColour() {
+            return bgColour;
+        }
+
+        /**
+         * Sets the value of the bgColour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setBgColour(String value) {
+            this.bgColour = value;
+        }
+
+        /**
+         * Gets the value of the consThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getConsThreshold() {
+            return consThreshold;
+        }
+
+        /**
+         * Sets the value of the consThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setConsThreshold(Integer value) {
+            this.consThreshold = value;
+        }
+
+        /**
+         * Gets the value of the pidThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getPidThreshold() {
+            return pidThreshold;
+        }
+
+        /**
+         * Sets the value of the pidThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setPidThreshold(Integer value) {
+            this.pidThreshold = value;
+        }
+
+        /**
+         * Gets the value of the title property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getTitle() {
+            return title;
+        }
+
+        /**
+         * Sets the value of the title property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setTitle(String value) {
+            this.title = value;
+        }
+
+        /**
+         * Gets the value of the showFullId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowFullId() {
+            return showFullId;
+        }
+
+        /**
+         * Sets the value of the showFullId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowFullId(Boolean value) {
+            this.showFullId = value;
+        }
+
+        /**
+         * Gets the value of the rightAlignIds property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isRightAlignIds() {
+            return rightAlignIds;
+        }
+
+        /**
+         * Sets the value of the rightAlignIds property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setRightAlignIds(Boolean value) {
+            this.rightAlignIds = value;
+        }
+
+        /**
+         * Gets the value of the showText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowText() {
+            return showText;
+        }
+
+        /**
+         * Sets the value of the showText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowText(Boolean value) {
+            this.showText = value;
+        }
+
+        /**
+         * Gets the value of the showColourText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowColourText() {
+            return showColourText;
+        }
+
+        /**
+         * Sets the value of the showColourText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowColourText(Boolean value) {
+            this.showColourText = value;
+        }
+
+        /**
+         * Gets the value of the showUnconserved property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowUnconserved() {
+            if (showUnconserved == null) {
+                return false;
+            } else {
+                return showUnconserved;
+            }
+        }
 
-    /**
-     * Sets the value of the showBoxes property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowBoxes(Boolean value)
-    {
-      this.showBoxes = value;
-    }
+        /**
+         * Sets the value of the showUnconserved property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowUnconserved(Boolean value) {
+            this.showUnconserved = value;
+        }
 
-    /**
-     * Gets the value of the wrapAlignment property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isWrapAlignment()
-    {
-      return wrapAlignment;
-    }
+        /**
+         * Gets the value of the showBoxes property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowBoxes() {
+            return showBoxes;
+        }
 
-    /**
-     * Sets the value of the wrapAlignment property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setWrapAlignment(Boolean value)
-    {
-      this.wrapAlignment = value;
-    }
+        /**
+         * Sets the value of the showBoxes property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowBoxes(Boolean value) {
+            this.showBoxes = value;
+        }
 
-    /**
-     * Gets the value of the renderGaps property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isRenderGaps()
-    {
-      return renderGaps;
-    }
+        /**
+         * Gets the value of the wrapAlignment property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isWrapAlignment() {
+            return wrapAlignment;
+        }
 
-    /**
-     * Sets the value of the renderGaps property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setRenderGaps(Boolean value)
-    {
-      this.renderGaps = value;
-    }
+        /**
+         * Sets the value of the wrapAlignment property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setWrapAlignment(Boolean value) {
+            this.wrapAlignment = value;
+        }
 
-    /**
-     * Gets the value of the showSequenceFeatures property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowSequenceFeatures()
-    {
-      return showSequenceFeatures;
-    }
+        /**
+         * Gets the value of the renderGaps property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isRenderGaps() {
+            return renderGaps;
+        }
 
-    /**
-     * Sets the value of the showSequenceFeatures property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowSequenceFeatures(Boolean value)
-    {
-      this.showSequenceFeatures = value;
-    }
+        /**
+         * Sets the value of the renderGaps property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setRenderGaps(Boolean value) {
+            this.renderGaps = value;
+        }
 
-    /**
-     * Gets the value of the showNPfeatureTooltip property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowNPfeatureTooltip()
-    {
-      return showNPfeatureTooltip;
-    }
+        /**
+         * Gets the value of the showSequenceFeatures property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowSequenceFeatures() {
+            return showSequenceFeatures;
+        }
 
-    /**
-     * Sets the value of the showNPfeatureTooltip property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowNPfeatureTooltip(Boolean value)
-    {
-      this.showNPfeatureTooltip = value;
-    }
+        /**
+         * Sets the value of the showSequenceFeatures property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceFeatures(Boolean value) {
+            this.showSequenceFeatures = value;
+        }
 
-    /**
-     * Gets the value of the showDbRefTooltip property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowDbRefTooltip()
-    {
-      return showDbRefTooltip;
-    }
+        /**
+         * Gets the value of the showNPfeatureTooltip property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowNPfeatureTooltip() {
+            return showNPfeatureTooltip;
+        }
 
-    /**
-     * Sets the value of the showDbRefTooltip property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowDbRefTooltip(Boolean value)
-    {
-      this.showDbRefTooltip = value;
-    }
+        /**
+         * Sets the value of the showNPfeatureTooltip property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowNPfeatureTooltip(Boolean value) {
+            this.showNPfeatureTooltip = value;
+        }
 
-    /**
-     * Gets the value of the followHighlight property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isFollowHighlight()
-    {
-      if (followHighlight == null)
-      {
-        return true;
-      }
-      else
-      {
-        return followHighlight;
-      }
-    }
+        /**
+         * Gets the value of the showDbRefTooltip property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowDbRefTooltip() {
+            return showDbRefTooltip;
+        }
 
-    /**
-     * Sets the value of the followHighlight property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setFollowHighlight(Boolean value)
-    {
-      this.followHighlight = value;
-    }
+        /**
+         * Sets the value of the showDbRefTooltip property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowDbRefTooltip(Boolean value) {
+            this.showDbRefTooltip = value;
+        }
 
-    /**
-     * Gets the value of the followSelection property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isFollowSelection()
-    {
-      if (followSelection == null)
-      {
-        return true;
-      }
-      else
-      {
-        return followSelection;
-      }
-    }
+        /**
+         * Gets the value of the followHighlight property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isFollowHighlight() {
+            if (followHighlight == null) {
+                return true;
+            } else {
+                return followHighlight;
+            }
+        }
 
-    /**
-     * Sets the value of the followSelection property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setFollowSelection(Boolean value)
-    {
-      this.followSelection = value;
-    }
+        /**
+         * Sets the value of the followHighlight property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFollowHighlight(Boolean value) {
+            this.followHighlight = value;
+        }
 
-    /**
-     * Gets the value of the showAnnotation property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isShowAnnotation()
-    {
-      return showAnnotation;
-    }
+        /**
+         * Gets the value of the followSelection property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isFollowSelection() {
+            if (followSelection == null) {
+                return true;
+            } else {
+                return followSelection;
+            }
+        }
 
-    /**
-     * Sets the value of the showAnnotation property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowAnnotation(Boolean value)
-    {
-      this.showAnnotation = value;
-    }
+        /**
+         * Sets the value of the followSelection property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFollowSelection(Boolean value) {
+            this.followSelection = value;
+        }
 
-    /**
-     * Gets the value of the centreColumnLabels property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isCentreColumnLabels()
-    {
-      if (centreColumnLabels == null)
-      {
-        return false;
-      }
-      else
-      {
-        return centreColumnLabels;
-      }
-    }
+        /**
+         * Gets the value of the showAnnotation property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowAnnotation() {
+            return showAnnotation;
+        }
 
-    /**
-     * Sets the value of the centreColumnLabels property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setCentreColumnLabels(Boolean value)
-    {
-      this.centreColumnLabels = value;
-    }
+        /**
+         * Sets the value of the showAnnotation property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowAnnotation(Boolean value) {
+            this.showAnnotation = value;
+        }
 
-    /**
-     * Gets the value of the showGroupConservation property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowGroupConservation()
-    {
-      if (showGroupConservation == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showGroupConservation;
-      }
-    }
+        /**
+         * Gets the value of the centreColumnLabels property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isCentreColumnLabels() {
+            if (centreColumnLabels == null) {
+                return false;
+            } else {
+                return centreColumnLabels;
+            }
+        }
 
-    /**
-     * Sets the value of the showGroupConservation property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowGroupConservation(Boolean value)
-    {
-      this.showGroupConservation = value;
-    }
+        /**
+         * Sets the value of the centreColumnLabels property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setCentreColumnLabels(Boolean value) {
+            this.centreColumnLabels = value;
+        }
 
-    /**
-     * Gets the value of the showGroupConsensus property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowGroupConsensus()
-    {
-      if (showGroupConsensus == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showGroupConsensus;
-      }
-    }
+        /**
+         * Gets the value of the showGroupConservation property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowGroupConservation() {
+            if (showGroupConservation == null) {
+                return false;
+            } else {
+                return showGroupConservation;
+            }
+        }
 
-    /**
-     * Sets the value of the showGroupConsensus property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowGroupConsensus(Boolean value)
-    {
-      this.showGroupConsensus = value;
-    }
+        /**
+         * Sets the value of the showGroupConservation property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowGroupConservation(Boolean value) {
+            this.showGroupConservation = value;
+        }
 
-    /**
-     * Gets the value of the showConsensusHistogram property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowConsensusHistogram()
-    {
-      if (showConsensusHistogram == null)
-      {
-        return true;
-      }
-      else
-      {
-        return showConsensusHistogram;
-      }
-    }
+        /**
+         * Gets the value of the showGroupConsensus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowGroupConsensus() {
+            if (showGroupConsensus == null) {
+                return false;
+            } else {
+                return showGroupConsensus;
+            }
+        }
 
-    /**
-     * Sets the value of the showConsensusHistogram property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowConsensusHistogram(Boolean value)
-    {
-      this.showConsensusHistogram = value;
-    }
+        /**
+         * Sets the value of the showGroupConsensus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowGroupConsensus(Boolean value) {
+            this.showGroupConsensus = value;
+        }
+
+        /**
+         * Gets the value of the showConsensusHistogram property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowConsensusHistogram() {
+            if (showConsensusHistogram == null) {
+                return true;
+            } else {
+                return showConsensusHistogram;
+            }
+        }
 
-    /**
-     * Gets the value of the showSequenceLogo property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowSequenceLogo()
-    {
-      if (showSequenceLogo == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showSequenceLogo;
-      }
-    }
+        /**
+         * Sets the value of the showConsensusHistogram property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowConsensusHistogram(Boolean value) {
+            this.showConsensusHistogram = value;
+        }
 
-    /**
-     * Sets the value of the showSequenceLogo property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowSequenceLogo(Boolean value)
-    {
-      this.showSequenceLogo = value;
-    }
+        /**
+         * Gets the value of the showSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowSequenceLogo() {
+            if (showSequenceLogo == null) {
+                return false;
+            } else {
+                return showSequenceLogo;
+            }
+        }
 
-    /**
-     * Gets the value of the normaliseSequenceLogo property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isNormaliseSequenceLogo()
-    {
-      if (normaliseSequenceLogo == null)
-      {
-        return false;
-      }
-      else
-      {
-        return normaliseSequenceLogo;
-      }
-    }
+        /**
+         * Sets the value of the showSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceLogo(Boolean value) {
+            this.showSequenceLogo = value;
+        }
 
-    /**
-     * Sets the value of the normaliseSequenceLogo property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setNormaliseSequenceLogo(Boolean value)
-    {
-      this.normaliseSequenceLogo = value;
-    }
+        /**
+         * Gets the value of the normaliseSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isNormaliseSequenceLogo() {
+            if (normaliseSequenceLogo == null) {
+                return false;
+            } else {
+                return normaliseSequenceLogo;
+            }
+        }
 
-    /**
-     * Gets the value of the ignoreGapsinConsensus property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isIgnoreGapsinConsensus()
-    {
-      if (ignoreGapsinConsensus == null)
-      {
-        return true;
-      }
-      else
-      {
-        return ignoreGapsinConsensus;
-      }
-    }
+        /**
+         * Sets the value of the normaliseSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setNormaliseSequenceLogo(Boolean value) {
+            this.normaliseSequenceLogo = value;
+        }
 
-    /**
-     * Sets the value of the ignoreGapsinConsensus property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setIgnoreGapsinConsensus(Boolean value)
-    {
-      this.ignoreGapsinConsensus = value;
-    }
+        /**
+         * Gets the value of the ignoreGapsinConsensus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isIgnoreGapsinConsensus() {
+            if (ignoreGapsinConsensus == null) {
+                return true;
+            } else {
+                return ignoreGapsinConsensus;
+            }
+        }
 
-    /**
-     * Gets the value of the startRes property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getStartRes()
-    {
-      return startRes;
-    }
+        /**
+         * Sets the value of the ignoreGapsinConsensus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIgnoreGapsinConsensus(Boolean value) {
+            this.ignoreGapsinConsensus = value;
+        }
 
-    /**
-     * Sets the value of the startRes property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setStartRes(Integer value)
-    {
-      this.startRes = value;
-    }
+        /**
+         * Gets the value of the startRes property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getStartRes() {
+            return startRes;
+        }
 
-    /**
-     * Gets the value of the startSeq property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getStartSeq()
-    {
-      return startSeq;
-    }
+        /**
+         * Sets the value of the startRes property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setStartRes(Integer value) {
+            this.startRes = value;
+        }
 
-    /**
-     * Sets the value of the startSeq property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setStartSeq(Integer value)
-    {
-      this.startSeq = value;
-    }
+        /**
+         * Gets the value of the startSeq property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getStartSeq() {
+            return startSeq;
+        }
 
-    /**
-     * Gets the value of the fontName property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getFontName()
-    {
-      return fontName;
-    }
+        /**
+         * Sets the value of the startSeq property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setStartSeq(Integer value) {
+            this.startSeq = value;
+        }
 
-    /**
-     * Sets the value of the fontName property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setFontName(String value)
-    {
-      this.fontName = value;
-    }
+        /**
+         * Gets the value of the fontName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getFontName() {
+            return fontName;
+        }
 
-    /**
-     * Gets the value of the fontSize property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getFontSize()
-    {
-      return fontSize;
-    }
+        /**
+         * Sets the value of the fontName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setFontName(String value) {
+            this.fontName = value;
+        }
 
-    /**
-     * Sets the value of the fontSize property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setFontSize(Integer value)
-    {
-      this.fontSize = value;
-    }
+        /**
+         * Gets the value of the fontSize property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontSize() {
+            return fontSize;
+        }
 
-    /**
-     * Gets the value of the fontStyle property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getFontStyle()
-    {
-      return fontStyle;
-    }
+        /**
+         * Sets the value of the fontSize property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontSize(Integer value) {
+            this.fontSize = value;
+        }
 
-    /**
-     * Sets the value of the fontStyle property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setFontStyle(Integer value)
-    {
-      this.fontStyle = value;
-    }
+        /**
+         * Gets the value of the fontStyle property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontStyle() {
+            return fontStyle;
+        }
 
-    /**
-     * Gets the value of the scaleProteinAsCdna property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isScaleProteinAsCdna()
-    {
-      if (scaleProteinAsCdna == null)
-      {
-        return true;
-      }
-      else
-      {
-        return scaleProteinAsCdna;
-      }
-    }
+        /**
+         * Sets the value of the fontStyle property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontStyle(Integer value) {
+            this.fontStyle = value;
+        }
 
-    /**
-     * Sets the value of the scaleProteinAsCdna property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setScaleProteinAsCdna(Boolean value)
-    {
-      this.scaleProteinAsCdna = value;
-    }
+        /**
+         * Gets the value of the scaleProteinAsCdna property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isScaleProteinAsCdna() {
+            if (scaleProteinAsCdna == null) {
+                return true;
+            } else {
+                return scaleProteinAsCdna;
+            }
+        }
 
-    /**
-     * Gets the value of the viewName property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getViewName()
-    {
-      return viewName;
-    }
+        /**
+         * Sets the value of the scaleProteinAsCdna property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setScaleProteinAsCdna(Boolean value) {
+            this.scaleProteinAsCdna = value;
+        }
 
-    /**
-     * Sets the value of the viewName property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setViewName(String value)
-    {
-      this.viewName = value;
-    }
+        /**
+         * Gets the value of the viewName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getViewName() {
+            return viewName;
+        }
 
-    /**
-     * Gets the value of the sequenceSetId property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getSequenceSetId()
-    {
-      return sequenceSetId;
-    }
+        /**
+         * Sets the value of the viewName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setViewName(String value) {
+            this.viewName = value;
+        }
 
-    /**
-     * Sets the value of the sequenceSetId property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setSequenceSetId(String value)
-    {
-      this.sequenceSetId = value;
-    }
+        /**
+         * Gets the value of the sequenceSetId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getSequenceSetId() {
+            return sequenceSetId;
+        }
 
-    /**
-     * Gets the value of the gatheredViews property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public Boolean isGatheredViews()
-    {
-      return gatheredViews;
-    }
+        /**
+         * Sets the value of the sequenceSetId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setSequenceSetId(String value) {
+            this.sequenceSetId = value;
+        }
 
-    /**
-     * Sets the value of the gatheredViews property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setGatheredViews(Boolean value)
-    {
-      this.gatheredViews = value;
-    }
+        /**
+         * Gets the value of the gatheredViews property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isGatheredViews() {
+            return gatheredViews;
+        }
 
-    /**
-     * Gets the value of the textCol1 property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getTextCol1()
-    {
-      return textCol1;
-    }
+        /**
+         * Sets the value of the gatheredViews property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setGatheredViews(Boolean value) {
+            this.gatheredViews = value;
+        }
+
+        /**
+         * Gets the value of the textCol1 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol1() {
+            return textCol1;
+        }
 
-    /**
-     * Sets the value of the textCol1 property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setTextCol1(Integer value)
-    {
-      this.textCol1 = value;
-    }
+        /**
+         * Sets the value of the textCol1 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol1(Integer value) {
+            this.textCol1 = value;
+        }
 
-    /**
-     * Gets the value of the textCol2 property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getTextCol2()
-    {
-      return textCol2;
-    }
+        /**
+         * Gets the value of the textCol2 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol2() {
+            return textCol2;
+        }
 
-    /**
-     * Sets the value of the textCol2 property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setTextCol2(Integer value)
-    {
-      this.textCol2 = value;
-    }
+        /**
+         * Sets the value of the textCol2 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol2(Integer value) {
+            this.textCol2 = value;
+        }
 
-    /**
-     * Gets the value of the textColThreshold property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getTextColThreshold()
-    {
-      return textColThreshold;
-    }
+        /**
+         * Gets the value of the textColThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextColThreshold() {
+            return textColThreshold;
+        }
 
-    /**
-     * Sets the value of the textColThreshold property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setTextColThreshold(Integer value)
-    {
-      this.textColThreshold = value;
-    }
+        /**
+         * Sets the value of the textColThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextColThreshold(Integer value) {
+            this.textColThreshold = value;
+        }
 
-    /**
-     * Gets the value of the id property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getId()
-    {
-      return id;
-    }
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
 
-    /**
-     * Sets the value of the id property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setId(String value)
-    {
-      this.id = value;
-    }
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
 
-    /**
-     * Gets the value of the complementId property.
-     * 
-     * @return possible object is {@link String }
-     * 
-     */
-    public String getComplementId()
-    {
-      return complementId;
-    }
+        /**
+         * Gets the value of the complementId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getComplementId() {
+            return complementId;
+        }
 
-    /**
-     * Sets the value of the complementId property.
-     * 
-     * @param value
-     *          allowed object is {@link String }
-     * 
-     */
-    public void setComplementId(String value)
-    {
-      this.complementId = value;
-    }
+        /**
+         * Sets the value of the complementId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setComplementId(String value) {
+            this.complementId = value;
+        }
 
-    /**
-     * Gets the value of the showComplementFeatures property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowComplementFeatures()
-    {
-      if (showComplementFeatures == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showComplementFeatures;
-      }
-    }
+        /**
+         * Gets the value of the showComplementFeatures property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowComplementFeatures() {
+            if (showComplementFeatures == null) {
+                return false;
+            } else {
+                return showComplementFeatures;
+            }
+        }
 
-    /**
-     * Sets the value of the showComplementFeatures property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowComplementFeatures(Boolean value)
-    {
-      this.showComplementFeatures = value;
-    }
+        /**
+         * Sets the value of the showComplementFeatures property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowComplementFeatures(Boolean value) {
+            this.showComplementFeatures = value;
+        }
 
-    /**
-     * Gets the value of the showComplementFeaturesOnTop property.
-     * 
-     * @return possible object is {@link Boolean }
-     * 
-     */
-    public boolean isShowComplementFeaturesOnTop()
-    {
-      if (showComplementFeaturesOnTop == null)
-      {
-        return false;
-      }
-      else
-      {
-        return showComplementFeaturesOnTop;
-      }
-    }
+        /**
+         * Gets the value of the showComplementFeaturesOnTop property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowComplementFeaturesOnTop() {
+            if (showComplementFeaturesOnTop == null) {
+                return false;
+            } else {
+                return showComplementFeaturesOnTop;
+            }
+        }
 
-    /**
-     * Sets the value of the showComplementFeaturesOnTop property.
-     * 
-     * @param value
-     *          allowed object is {@link Boolean }
-     * 
-     */
-    public void setShowComplementFeaturesOnTop(Boolean value)
-    {
-      this.showComplementFeaturesOnTop = value;
-    }
+        /**
+         * Sets the value of the showComplementFeaturesOnTop property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowComplementFeaturesOnTop(Boolean value) {
+            this.showComplementFeaturesOnTop = value;
+        }
 
-    /**
-     * Gets the value of the width property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getWidth()
-    {
-      return width;
-    }
+        /**
+         * Gets the value of the width property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getWidth() {
+            return width;
+        }
 
-    /**
-     * Sets the value of the width property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setWidth(Integer value)
-    {
-      this.width = value;
-    }
+        /**
+         * Sets the value of the width property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setWidth(Integer value) {
+            this.width = value;
+        }
 
-    /**
-     * Gets the value of the height property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getHeight()
-    {
-      return height;
-    }
+        /**
+         * Gets the value of the height property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getHeight() {
+            return height;
+        }
 
-    /**
-     * Sets the value of the height property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setHeight(Integer value)
-    {
-      this.height = value;
-    }
+        /**
+         * Sets the value of the height property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setHeight(Integer value) {
+            this.height = value;
+        }
 
-    /**
-     * Gets the value of the xpos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getXpos()
-    {
-      return xpos;
-    }
+        /**
+         * Gets the value of the xpos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXpos() {
+            return xpos;
+        }
 
-    /**
-     * Sets the value of the xpos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setXpos(Integer value)
-    {
-      this.xpos = value;
-    }
+        /**
+         * Sets the value of the xpos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXpos(Integer value) {
+            this.xpos = value;
+        }
 
-    /**
-     * Gets the value of the ypos property.
-     * 
-     * @return possible object is {@link Integer }
-     * 
-     */
-    public Integer getYpos()
-    {
-      return ypos;
-    }
+        /**
+         * Gets the value of the ypos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYpos() {
+            return ypos;
+        }
 
-    /**
-     * Sets the value of the ypos property.
-     * 
-     * @param value
-     *          allowed object is {@link Integer }
-     * 
-     */
-    public void setYpos(Integer value)
-    {
-      this.ypos = value;
-    }
+        /**
+         * Sets the value of the ypos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYpos(Integer value) {
+            this.ypos = value;
+        }
 
-    /**
-     * &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;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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class CalcIdParam extends WebServiceParameterSet
-    {
-
-      @XmlAttribute(name = "calcId", required = true)
-      protected String calcId;
-
-      @XmlAttribute(name = "needsUpdate")
-      protected Boolean needsUpdate;
-
-      @XmlAttribute(name = "autoUpdate", required = true)
-      protected boolean autoUpdate;
-
-      /**
-       * Gets the value of the calcId property.
-       * 
-       * @return possible object is {@link String }
-       * 
-       */
-      public String getCalcId()
-      {
-        return calcId;
-      }
-
-      /**
-       * Sets the value of the calcId property.
-       * 
-       * @param value
-       *          allowed object is {@link String }
-       * 
-       */
-      public void setCalcId(String value)
-      {
-        this.calcId = value;
-      }
-
-      /**
-       * Gets the value of the needsUpdate property.
-       * 
-       * @return possible object is {@link Boolean }
-       * 
-       */
-      public boolean isNeedsUpdate()
-      {
-        if (needsUpdate == null)
+
+        /**
+         * &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;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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class CalcIdParam
+            extends WebServiceParameterSet
         {
-          return false;
+
+            @XmlAttribute(name = "calcId", required = true)
+            protected String calcId;
+            @XmlAttribute(name = "needsUpdate")
+            protected Boolean needsUpdate;
+            @XmlAttribute(name = "autoUpdate", required = true)
+            protected boolean autoUpdate;
+
+            /**
+             * Gets the value of the calcId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getCalcId() {
+                return calcId;
+            }
+
+            /**
+             * Sets the value of the calcId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setCalcId(String value) {
+                this.calcId = value;
+            }
+
+            /**
+             * Gets the value of the needsUpdate property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public boolean isNeedsUpdate() {
+                if (needsUpdate == null) {
+                    return false;
+                } else {
+                    return needsUpdate;
+                }
+            }
+
+            /**
+             * Sets the value of the needsUpdate property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setNeedsUpdate(Boolean value) {
+                this.needsUpdate = value;
+            }
+
+            /**
+             * Gets the value of the autoUpdate property.
+             * 
+             */
+            public boolean isAutoUpdate() {
+                return autoUpdate;
+            }
+
+            /**
+             * Sets the value of the autoUpdate property.
+             * 
+             */
+            public void setAutoUpdate(boolean value) {
+                this.autoUpdate = value;
+            }
+
         }
-        else
-        {
-          return needsUpdate;
-        }
-      }
-
-      /**
-       * Sets the value of the needsUpdate property.
-       * 
-       * @param value
-       *          allowed object is {@link Boolean }
-       * 
-       */
-      public void setNeedsUpdate(Boolean value)
-      {
-        this.needsUpdate = value;
-      }
-
-      /**
-       * Gets the value of the autoUpdate property.
-       * 
-       */
-      public boolean isAutoUpdate()
-      {
-        return autoUpdate;
-      }
-
-      /**
-       * Sets the value of the autoUpdate property.
-       * 
-       */
-      public void setAutoUpdate(boolean value)
-      {
-        this.autoUpdate = value;
-      }
 
-    }
 
-    /**
-     * &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="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;
-     * 
-     * 
-     */
-    @XmlAccessorType(XmlAccessType.FIELD)
-    @XmlType(name = "")
-    public static class HiddenColumns
-    {
-
-      @XmlAttribute(name = "start")
-      protected Integer start;
-
-      @XmlAttribute(name = "end")
-      protected Integer end;
-
-      /**
-       * Gets the value of the start property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getStart()
-      {
-        return start;
-      }
-
-      /**
-       * Sets the value of the start property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setStart(Integer value)
-      {
-        this.start = value;
-      }
-
-      /**
-       * Gets the value of the end property.
-       * 
-       * @return possible object is {@link Integer }
-       * 
-       */
-      public Integer getEnd()
-      {
-        return end;
-      }
-
-      /**
-       * Sets the value of the end property.
-       * 
-       * @param value
-       *          allowed object is {@link Integer }
-       * 
-       */
-      public void setEnd(Integer value)
-      {
-        this.end = value;
-      }
+        /**
+         * &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="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;
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class HiddenColumns {
+
+            @XmlAttribute(name = "start")
+            protected Integer start;
+            @XmlAttribute(name = "end")
+            protected Integer end;
+
+            /**
+             * Gets the value of the start property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getStart() {
+                return start;
+            }
+
+            /**
+             * Sets the value of the start property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setStart(Integer value) {
+                this.start = value;
+            }
+
+            /**
+             * Gets the value of the end property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getEnd() {
+                return end;
+            }
+
+            /**
+             * Sets the value of the end property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setEnd(Integer value) {
+                this.end = value;
+            }
 
-    }
+        }
 
-  }
+    }
 
 }
diff --git a/src/javajs/async/Assets.java b/src/javajs/async/Assets.java
new file mode 100644 (file)
index 0000000..05614ce
--- /dev/null
@@ -0,0 +1,594 @@
+package javajs.async;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import swingjs.api.JSUtilI;
+
+/**
+ * The Assets class allows assets such as images and property files to be
+ * combined into zip files rather than delivered individually. The Assets
+ * instance is a singleton served by a set of static methods. In particular, the
+ * three add(...) methods are used to create asset references, which include an
+ * arbitrary name, a path to a zip file asset, and one or more class paths that
+ * are covered by this zip file asset.
+ * 
+ * For example:
+ * 
+ * <code>
+       static {
+               try {
+                       Assets.add(new Assets.Asset("osp", "osp-assets.zip", "org/opensourcephysics/resources"));
+                       Assets.add(new Assets.Asset("tracker", "tracker-assets.zip",
+                                       "org/opensourcephysics/cabrillo/tracker/resources"));
+                       Assets.add(new Assets.Asset("physlets", "physlet-assets.zip", new String[] { "opticsimages", "images" }));
+                       // add the Info.assets last so that it can override these defaults
+                       if (OSPRuntime.isJS) {
+                               Assets.add(OSPRuntime.jsutil.getAppletInfo("assets"));
+                       }
+               } catch (Exception e) {
+                       OSPLog.warning("Error reading assets path. ");
+               }
+
+       }
+ * </code>
+ * 
+ * It is not clear that Java is well-served by this zip-file loading, but
+ * certainly JavaScript is. What could be 100 downloads is just one, and SwingJS
+ * (but not Java) can cache individual ZipEntry instances in order to unzip them
+ * independently only when needed. This is potentially a huge savings.
+ * 
+ * Several static methods can be used to retrieve assets. Principal among those
+ * are:
+ * 
+ * <code>
+ * getAssetBytes(String fullPath)
+ * getAssetString(String fullPath)
+ * getAssetStream(String fullPath)
+ * </code>
+ * 
+ * If an asset is not found in a zip file, then it will be loaded from its fullPath. 
+ * 
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+
+public class Assets {
+
+       public static boolean isJS = /** @j2sNative true || */
+                       false;
+
+       public static JSUtilI jsutil;
+
+       static {
+               try {
+                       if (isJS) {
+                               jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance());
+                       }
+
+               } catch (Exception e) {
+                       System.err.println("Assets could not create swinjs.JSUtil instance");
+               }
+       }
+
+       private Map<String, Map<String, ZipEntry>> htZipContents = new HashMap<>();
+
+       private static boolean doCacheZipContents = true;
+
+       private static Assets instance = new Assets();
+
+       private Assets() {
+       }
+
+       private Map<String, Asset> assetsByPath = new HashMap<>();
+
+       private String[] sortedList = new String[0];
+
+       /**
+        * If this object has been cached by SwingJS, add its bytes to the URL, URI, or
+        * File
+        * 
+        * @param URLorURIorFile
+        * @return
+        */
+       public static byte[] addJSCachedBytes(Object URLorURIorFile) {
+               return (isJS ? jsutil.addJSCachedBytes(URLorURIorFile) : null);
+       }
+
+       public static class Asset {
+               String name;
+               URI uri;
+               String classPath;
+               String zipPath;
+               String[] classPaths;
+
+               public Asset(String name, String zipPath, String[] classPaths) {
+                       this.name = name;
+                       this.zipPath = zipPath;
+                       this.classPaths = classPaths;
+               }
+
+               public Asset(String name, String zipPath, String classPath) {
+                       this.name = name;
+                       this.zipPath = zipPath;
+                       uri = getAbsoluteURI(zipPath); // no spaces expected here.
+                       this.classPath = classPath.endsWith("/") ? classPath : classPath + "/";
+               }
+
+               public URL getURL(String fullPath) throws MalformedURLException {
+                       return (fullPath.indexOf(classPath) < 0 ? null
+                                       : new URL("jar", null, uri + "!/" + fullPath));//.replaceAll(" ", "%20")));
+               }
+
+               @Override
+               public String toString() {
+                       return "{" + "\"name\":" + "\"" + name + "\"," + "\"zipPath\":" + "\"" + zipPath + "\"," + "\"classPath\":"
+                                       + "\"" + classPath + "\"" + "}";
+               }
+
+       }
+
+       public static Assets getInstance() {
+               return instance;
+       }
+
+       /**
+        * The difference here is that URL will not insert the %20 for space that URI will.
+        * 
+        * @param path
+        * @return
+        */
+       @SuppressWarnings("deprecation")
+       public static URL getAbsoluteURL(String path) {
+               URL url = null;
+               try {
+                       url = (path.indexOf(":/") < 0 ? new File(new File(path).getAbsolutePath()).toURL() : new URL(path));
+                       if (path.indexOf("!/")>=0)
+                               url = new URL("jar", null, url.toString());
+               } catch (MalformedURLException e) {
+                       e.printStackTrace();
+               }
+               return url;
+       }
+
+       public static URI getAbsoluteURI(String path) {
+               URI uri = null;
+               try {
+                       uri = (path.indexOf(":/") < 0 ? new File(new File(path).getAbsolutePath()).toURI() : new URI(path));
+               } catch (URISyntaxException e) {
+                       e.printStackTrace();
+               }
+               return uri;
+       }
+
+       /**
+        * Allows passing a Java Asset or array of Assets or a JavaScript Object or
+        * Object array that contains name, zipPath, and classPath keys; in JavaScript,
+        * the keys can have multiple .
+        * 
+        * @param o
+        */
+       public static void add(Object o) {
+               if (o == null)
+                       return;
+               try {
+                       if (o instanceof Object[]) {
+                               Object[] a = (Object[]) o;
+                               for (int i = 0; i < a.length; i++)
+                                       add(a[i]);
+                               return;
+                       }
+                       // In JavaScript this may not actually be an Asset, only a proxy for that.
+                       // Just testing for keys. Only one of classPath and classPaths is allowed.
+                       Asset a = (Asset) o;
+                       if (a.name == null || a.zipPath == null || a.classPath == null && a.classPaths == null
+                                       || a.classPath != null && a.classPaths != null) {
+                               throw new NullPointerException("Assets could not parse " + o);
+                       }
+                       if (a.classPaths == null) {
+                               // not possible in Java, but JavaScript may be passing an array of class paths
+                               add(a.name, a.zipPath, a.classPath);
+                       } else {
+                               add(a.name, a.zipPath, a.classPaths);
+                       }
+               } catch (Throwable t) {
+                       throw new IllegalArgumentException(t.getMessage());
+               }
+       }
+
+       public static void add(String name, String zipFile, String path) {
+               add(name, zipFile, new String[] { path });
+       }
+
+       private static HashSet<String> loadedAssets = new HashSet<>();
+       
+       public static boolean hasLoaded(String name) {
+               return loadedAssets.contains(name);
+       }
+       
+       public static void reset() {
+               getInstance().htZipContents.clear();
+               getInstance().assetsByPath.clear();
+               getInstance().sortedList = new String[0];
+       }
+
+       public static void add(String name, String zipFile, String[] paths) {
+               getInstance()._add(name, zipFile, paths);
+       }
+
+       private void _add(String name, String zipFile, String[] paths) {
+               if (hasLoaded(name)) {
+                       System.err.println("Assets warning: Asset " + name + " already exists");
+               }
+               loadedAssets.add(name);
+               for (int i = paths.length; --i >= 0;) {
+                       assetsByPath.put(paths[i], new Asset(name, zipFile, paths[i]));
+               }
+               resort();
+       }
+       
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static byte[] getAssetBytes(String assetPath) {
+               return getAssetBytes(assetPath, false);
+       }
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static String getAssetString(String assetPath) {
+               return getAssetString(assetPath, false);
+       }
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static InputStream getAssetStream(String assetPath) {
+               return getAssetStream(assetPath, false);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static byte[] getAssetBytesFromZip(String assetPath) {
+               return getAssetBytes(assetPath, true);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static String getAssetStringFromZip(String assetPath) {
+               return getAssetString(assetPath, true);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static InputStream getAssetStreamFromZip(String assetPath) {
+               return getAssetStream(assetPath, true);
+       }
+
+
+       /**
+        * Get the contents of a path from a zip file asset as byte[], optionally loading
+        * the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static byte[] getAssetBytes(String path, boolean zipOnly) {
+               byte[] bytes = null;
+               try {
+                       URL url = getInstance()._getURLFromPath(path, true);
+                       if (url == null && !zipOnly) {
+                               url = getAbsoluteURL(path);
+                               //url = Assets.class.getResource(path);
+                       }
+                       if (url == null)
+                               return null;
+                       if (isJS) {
+                               bytes = jsutil.getURLBytes(url);
+                               if (bytes == null) {
+                                       url.openStream();
+                                       bytes = jsutil.getURLBytes(url);
+                               }
+                       } else {
+                               bytes = getLimitedStreamBytes(url.openStream(), -1, null);
+                       }
+               } catch (Throwable t) {
+                       t.printStackTrace();
+               }
+               return bytes;
+       }
+
+       /**
+        * Get the contents of a path from a zip file asset as a String, optionally
+        * loading the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static String getAssetString(String path, boolean zipOnly) {
+               byte[] bytes = getAssetBytes(path, zipOnly);
+               return (bytes == null ? null : new String(bytes));
+       }
+
+       /**
+        * Get the contents of a path from a zip file asset as an InputStream, optionally
+        * loading the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static InputStream getAssetStream(String path, boolean zipOnly) {
+               try {
+                       URL url = getInstance()._getURLFromPath(path, true);
+                       if (url == null && !zipOnly) {
+                               url = Assets.class.getClassLoader().getResource(path);
+                       }
+                       if (url != null)
+                               return url.openStream();
+               } catch (Throwable t) {
+               }
+               return null;
+       }
+       /**
+        * Determine the path to an asset. If not found in a zip file asset, return the
+        * absolute path to this resource.
+        * 
+        * @param fullPath
+        * @return
+        */
+       public static URL getURLFromPath(String fullPath) {
+               return getInstance()._getURLFromPath(fullPath, false);
+       }
+
+       /**
+        * Determine the path to an asset. If not found in a zip file asset, optionally
+        * return null or the absolute path to this resource.
+        * 
+        * @param fullPath
+        * @param zipOnly
+        * @return the URL to this asset, or null if not found.
+        */
+       public static URL getURLFromPath(String fullPath, boolean zipOnly) {
+               return getInstance()._getURLFromPath(fullPath, zipOnly);
+       }
+
+       private URL _getURLFromPath(String fullPath, boolean zipOnly) {
+               URL url = null;
+               try {
+                       if (fullPath.startsWith("/"))
+                               fullPath = fullPath.substring(1);
+                       for (int i = sortedList.length; --i >= 0;) {
+                               if (fullPath.startsWith(sortedList[i])) {
+                                       url = assetsByPath.get(sortedList[i]).getURL(fullPath);
+                                       ZipEntry ze = findZipEntry(url);
+                                       if (ze == null)
+                                               break;
+                                       if (isJS) {
+                                               jsutil.setURLBytes(url, jsutil.getZipBytes(ze));
+                                       }
+                                       return url;
+                               }
+                       }
+                       if (!zipOnly)
+                               return getAbsoluteURL(fullPath);
+               } catch (MalformedURLException e) {
+               }
+               return null;
+       }
+
+       public static ZipEntry findZipEntry(URL url) {
+               String[] parts = getJarURLParts(url.toString());
+               if (parts == null || parts[0] == null || parts[1].length() == 0)
+                       return null;
+               return findZipEntry(parts[0], parts[1]);
+       }
+
+       public static ZipEntry findZipEntry(String zipFile, String fileName) {
+               return getZipContents(zipFile).get(fileName);
+       }
+
+       /**
+        * Gets the contents of a zip file.
+        * 
+        * @param zipPath the path to the zip file
+        * @return a set of file names in alphabetical order
+        */
+       public static Map<String, ZipEntry> getZipContents(String zipPath) {
+               return getInstance()._getZipContents(zipPath);
+       }
+
+       private Map<String, ZipEntry> _getZipContents(String zipPath) {
+               URL url = getURLWithCachedBytes(zipPath); // BH carry over bytes if we have them already
+               Map<String, ZipEntry> fileNames = htZipContents.get(url.toString());
+               if (fileNames != null)
+                       return fileNames;
+               try {
+                       // Scan URL zip stream for files.
+                       return readZipContents(url.openStream(), url);
+               } catch (Exception ex) {
+                       ex.printStackTrace();
+                       return null;
+               }
+       }
+
+       /**
+        * Deconstruct a jar URL into two parts, before and after "!/".
+        * 
+        * @param source
+        * @return
+        */
+       public static String[] getJarURLParts(String source) {
+               int n = source.indexOf("!/");
+               if (n < 0)
+                       return null;
+               String jarfile = source.substring(0, n).replace("jar:", "");
+               while (jarfile.startsWith("//"))
+                       jarfile = jarfile.substring(1);
+               return new String[] { jarfile, (n == source.length() - 2 ? null : source.substring(n + 2)) };
+       }
+
+       /**
+        * Get the contents of any URL as a byte array. This method does not do any asset check. It just gets the url data as a byte array.
+        * 
+        * @param url
+        * @return byte[]
+        * 
+        * @author hansonr
+        */
+       public static byte[] getURLContents(URL url) {
+               if (url == null)
+                       return null;
+               try {
+                       if (isJS) {
+                               // Java 9! return new String(url.openStream().readAllBytes());
+                               return jsutil.readAllBytes(url.openStream());
+                       }
+                       return getLimitedStreamBytes(url.openStream(), -1, null);
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+               return null;
+       }
+
+       /**
+        * 
+        * Convert a file path to a URL, retrieving any cached file data, as from DnD.
+        * Do not do any actual data transfer. This is a swingjs.JSUtil service.
+        * 
+        * @param path
+        * @return
+        */
+       private static URL getURLWithCachedBytes(String path) {
+               URL url = getAbsoluteURL(path);
+               if (url != null)
+                       addJSCachedBytes(url);
+               return url;
+       }
+
+       private Map<String, ZipEntry> readZipContents(InputStream is, URL url) throws IOException {
+               HashMap<String, ZipEntry> fileNames = new HashMap<String, ZipEntry>();
+               if (doCacheZipContents)
+                       htZipContents.put(url.toString(), fileNames);
+               ZipInputStream input = new ZipInputStream(is);
+               ZipEntry zipEntry = null;
+               int n = 0;
+               while ((zipEntry = input.getNextEntry()) != null) {
+                       if (zipEntry.isDirectory() || zipEntry.getSize() == 0)
+                               continue;
+                       n++;
+                       String fileName = zipEntry.getName();
+                       fileNames.put(fileName, zipEntry); // Java has no use for the ZipEntry, but JavaScript can read it.
+               }
+               input.close();
+               System.out.println("Assets: " + n + " zip entries found in " + url); //$NON-NLS-1$
+               return fileNames;
+       }
+
+       private void resort() {
+               sortedList = new String[assetsByPath.size()];
+               int i = 0;
+               for (String path : assetsByPath.keySet()) {
+                       sortedList[i++] = path;
+               }
+               Arrays.sort(sortedList);
+       }
+
+
+       /**
+        * Only needed for Java
+        * 
+        * @param is
+        * @param n
+        * @param out
+        * @return
+        * @throws IOException
+        */
+       private static byte[] getLimitedStreamBytes(InputStream is, long n, OutputStream out) throws IOException {
+
+               // Note: You cannot use InputStream.available() to reliably read
+               // zip data from the web.
+
+               boolean toOut = (out != null);
+               int buflen = (n > 0 && n < 1024 ? (int) n : 1024);
+               byte[] buf = new byte[buflen];
+               byte[] bytes = (out == null ? new byte[n < 0 ? 4096 : (int) n] : null);
+               int len = 0;
+               int totalLen = 0;
+               if (n < 0)
+                       n = Integer.MAX_VALUE;
+               while (totalLen < n && (len = is.read(buf, 0, buflen)) > 0) {
+                       totalLen += len;
+                       if (toOut) {
+                               out.write(buf, 0, len);
+                       } else {
+                               if (totalLen > bytes.length)
+                                       bytes = Arrays.copyOf(bytes, totalLen * 2);
+                               System.arraycopy(buf, 0, bytes, totalLen - len, len);
+                               if (n != Integer.MAX_VALUE && totalLen + buflen > bytes.length)
+                                       buflen = bytes.length - totalLen;
+                       }
+               }
+               if (toOut)
+                       return null;
+               if (totalLen == bytes.length)
+                       return bytes;
+               buf = new byte[totalLen];
+               System.arraycopy(bytes, 0, buf, 0, totalLen);
+               return buf;
+       }
+
+       /**
+        * Return all assets in the form that is appropriate for the Info.assets value in SwingJS.
+        * 
+        */
+       @Override
+       public String toString() {
+               String s = "[";
+               for (int i = 0; i < sortedList.length; i++) {
+                       Asset a = assetsByPath.get(sortedList[i]);
+                       s += (i == 0 ? "" : ",") + a;
+               }
+               return s + "]";
+       }
+
+}
diff --git a/src/javajs/async/Async.java b/src/javajs/async/Async.java
new file mode 100644 (file)
index 0000000..f6f31e5
--- /dev/null
@@ -0,0 +1,189 @@
+package javajs.async;
+
+/**
+ * A package to manage asynchronous aspects of SwingJS
+ * 
+ * The javajs.async package simplifies the production of methods that can be
+ * used equally well in Java and in JavaScript for handling "pseudo-modal"
+ * blocking in JavaScript, meaning the user is locked out of other interactions,
+ * as in Java, but the code is not actually blocking the thread.
+ * 
+ * Included in this package are
+ * 
+ * Async
+ * 
+ * Provides few simple generic static methods.
+ * 
+ * Async.isJS() -- true if we are running this in JavaScript
+ * 
+ * Async.javaSleep() -- bypassing Thread.sleep() for JavaScript; allowing it for
+ * Java
+ * 
+ * 
+ * AsyncDialog
+ * 
+ * Provides several very useful methods that act as a replacement for direct
+ * JOptionPane or JDialog calls, including both a synchronous callback
+ * (manifested in Java) and an asynchronous callback (JavaScript), both
+ * resulting in the same effect, except for the fact that the JavaScript code
+ * has returned immediately from the call with an "ignore me" reference, while
+ * Java is waiting at that call to return a value from JOptionPane (which is
+ * saved by AsyncDialog and not delivered as a return value).
+ * 
+ * AsyncDialog does not extend JOptionPane, but it mirrors that classes public
+ * methods. There are a LOT of public methods in JOPtionPane. I suppose we
+ * should implement them all. In practice, AsyncDialog calls standard
+ * JOptionPane static classes for the dialogs.
+ * 
+ * Initially, the following methods are implemented:
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * ActionListener a)
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * int optionType, ActionListener a)
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * int optionType, int messageType, ActionListener a)
+ * 
+ * public void showInputDialog(Component frame, Object message, ActionListener
+ * a)
+ * 
+ * public void showInputDialog(Component frame, Object message, String title,
+ * int messageType, Icon icon, Object[] selectionValues, Object
+ * initialSelectionValue, ActionListener a)
+ * 
+ * public void showMessageDialog(Component frame, Object message, ActionListener
+ * a)
+ * 
+ * public void showOptionDialog(Component frame, Object message, String title,
+ * int optionType, int messageType, Icon icon, Object[] options, Object
+ * initialValue, ActionListener a)
+ * 
+ * 
+ * All nonstatic methods, requiring new AsyncDialog(), also require an
+ * ActionListener. This listener will get a call to actionPerformed(ActionEvent)
+ * where:
+ * 
+ * event.getSource() is a reference to the originating AsyncDialog (super
+ * JOptionPane) for all information that a standard JOptionPane can provide,
+ * along with the two methods int getOption() and Object getChoice().
+ * 
+ * event.getID() is a reference to the standard JOptionPane int return code.
+ * 
+ * event.getActionCommand() also holds a value, but it may or may not be of
+ * value.
+ * 
+ * 
+ * A few especially useful methods are static, allowing just one or two expected
+ * callbacks of interest:
+ * 
+ * AsyncDialog.showOKAsync(Component parent, Object message, String title,
+ * Runnable ok)
+ * 
+ * AsyncDialog.showYesAsync (Component parent, Object message, String title,
+ * Runnable yes)
+ * 
+ * AsyncDialog.showYesNoAsync (Component parent, Object message, String title,
+ * Runnable yes, Runnable no)
+ * 
+ * These methods provide a fast way to adjust JOptionPane calls to be
+ * asynchronous.
+ * 
+ * 
+ * 
+ * AsyncFileChooser extends javax.swing.JFileChooser
+ * 
+ * Accepted constructors include:
+ * 
+ * public AsyncFileChooser()
+ * 
+ * public AsyncFileChooser(File file)
+ * 
+ * public AsyncFileChooser(File file, FileSystemView view)
+ * 
+ * (Note, however, that FileSystemView has no equivalent in JavaScript.)
+ * 
+ * It's three public methods include:
+ * 
+ * public void showDialog(Component frame, String btnLabel, Runnable ok,
+ * Runnable cancel)
+ * 
+ * public void showOpenDialog(Component frame, Runnable ok, Runnable cancel)
+ * 
+ * public void showSaveDialog(Component frame, Runnable ok, Runnable cancel)
+ * 
+ * 
+ * ActionListener is not needed here, as the instance of new AsyncFileChooser()
+ * already has direct access to all the JFileChooser public methods such as
+ * getSelectedFile() and getSelectedFiles().
+ * 
+ * As a subclass of JFileChooser, it accepts all three public showXXXX methods
+ * of JFileChooser, namely:
+ * 
+ * public void showDialog(Component frame, String btnLabel)
+ * 
+ * public void showOpenDialog(Component frame)
+ * 
+ * public void showSaveDialog(Component frame)
+ * 
+ * 
+ * None of these are recommended. AsyncFileChooser will indicate errors if the
+ * first of these two are called. (showSaveDialog is fine, as it is modal even
+ * in JavaScript. However it is not recommended that showSaveDialog(Component
+ * frame) be used, as in the future browsers may implement some sort of file
+ * saver in HTML5.
+ * 
+ * 
+ * 
+ * AsyncColorChooser
+ * 
+ * 
+ * AsyncColorChooser accesses JColorChooser asynchronously, using a private
+ * SwingJS setting that tells JColorChooser to report back to it with property
+ * changes. It is constructed using new AsyncColorChooser() and implements just
+ * two methods:
+ * 
+ * public void showDialog(Component component, String title, Color initialColor,
+ * ActionListener listener)
+ * 
+ * public Color getSelectedColor()
+ * 
+ * 
+ * The listener will get an actionPerformed(ActionEvent) callback with
+ * event.getID() equal to the color value or 0 if canceled. The
+ * getSelectedColor() method may also be called from this callback to retrieve
+ * the associated java.awt.Color object, using
+ * 
+ * ((AsyncColorChooser)e.getSource()).getSelectedColor()
+ * 
+ * As in Java, a null value for the selected color indicates that the
+ * JColorChooser was closed.
+ * 
+ * Bob Hanson 2019.11.07
+ * 
+ * 
+ * @author Bob Hanson hansonr_at_stolaf.edu
+ *
+ */
+public class Async {
+
+       public static boolean isJS() {
+               return  (/** @j2sNative 1 ? true : */false);
+       }
+
+       /**
+        * No sleep in JavaScript
+        * @param ms
+        */
+       public static void javaSleep(int ms) {
+               if (!isJS()) {
+                       try {
+                               Thread.sleep(ms);
+                       } catch (InterruptedException e) {
+                       }
+               }
+       
+       }
+
+}
diff --git a/src/javajs/async/AsyncColorChooser.java b/src/javajs/async/AsyncColorChooser.java
new file mode 100644 (file)
index 0000000..2474833
--- /dev/null
@@ -0,0 +1,67 @@
+package javajs.async;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JColorChooser;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A simple Asynchronous file chooser for JavaScript; synchronous with Java.
+ * 
+ * Allows two modes -- using an ActionListener (setAction(ActionListener) or constructor(ActionListener))
+ * 
+ * @author Bob Hanson
+ */
+
+public class AsyncColorChooser implements PropertyChangeListener {
+
+       private ActionListener listener;
+       private Color selectedColor;
+
+       public void showDialog(Component component, String title, Color initialColor, ActionListener listener) {
+               setListener(listener);
+               process(JColorChooser.showDialog(component, title, initialColor));
+               unsetListener();
+       }
+
+       public Color getSelectedColor() {
+               return selectedColor;
+       }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               // JavaScript only
+               Color c = (Color) evt.getNewValue();
+               
+               switch (evt.getPropertyName()) {
+               case "SelectedColor":
+                       process(c);
+                       break;
+               }
+       }
+
+       private void setListener(ActionListener a) {
+               listener = a;
+               /** @j2sNative Clazz.load("javax.swing.JColorChooser");javax.swing.JColorChooser.listener = this */
+       }
+
+       private void unsetListener() {
+               /** @j2sNative javax.swing.JColorChooser.listener = null */
+       }
+
+       
+       
+       private void process(Color c) {
+               if (c instanceof UIResource)
+                       return;
+               selectedColor = c;
+               listener.actionPerformed(new ActionEvent(this, c == null ? 0 : c.getRGB(), c == null ? null : c.toString()));
+       }
+       
+}
diff --git a/src/javajs/async/AsyncDialog.java b/src/javajs/async/AsyncDialog.java
new file mode 100644 (file)
index 0000000..5752f48
--- /dev/null
@@ -0,0 +1,282 @@
+package javajs.async;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A class to manage asynchronous input, option, and confirmation dialogs.
+ * 
+ * @author Bob Hanson hansonr_at_stolaf.edu
+ *
+ */
+public class AsyncDialog implements PropertyChangeListener {
+
+// see discussion in net.sf.j2s.core/doc/Differences.txt
+//
+// Confirmation dialog example. Note moving the parent component into the constructor.
+// 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;
+//                     }
+//             }
+//
+// revised: 
+//
+//             private void promptQuitAsync() {
+//                     new AsyncDialog().showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION, 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;
+//             }
+//     }}));
+//             }
+
+       
+       public AsyncDialog() {
+       }
+       
+       private ActionListener actionListener;
+       private Object choice;
+       private Object[] options;
+       private Object value;
+       private boolean wantsInput;
+
+       // These options can be supplemented as desired.
+
+       
+       /**
+        * Synchronous call; OK in JavaScript as long as we are using a JavaScript prompt() call
+        * 
+        * @param frame
+        * @param msg
+        * @return
+        */
+       @Deprecated
+       public static String showInputDialog(Component frame, String msg) {
+               return JOptionPane.showInputDialog(frame, msg);
+       }
+
+       public void showInputDialog(Component frame, Object message, ActionListener a) {
+               setListener(a);
+               wantsInput = true;
+               process(JOptionPane.showInputDialog(frame, message));
+               unsetListener();
+       }
+
+       public void showInputDialog(Component frame, Object message, String title, int messageType, Icon icon,
+                       Object[] selectionValues, Object initialSelectionValue, ActionListener a) {
+               setListener(a);
+               wantsInput = true;
+               process(JOptionPane.showInputDialog(frame, message, title, messageType, icon, selectionValues,
+                               initialSelectionValue));
+               unsetListener();
+       }
+
+       public void showMessageDialog(Component frame, Object message, ActionListener a) {
+               setListener(a);
+               JOptionPane.showMessageDialog(frame, message);
+               unsetListener();
+               if (/** @j2sNative false || */true)
+                       process("" + message);
+       }
+
+       public void showOptionDialog(Component frame, Object message, String title, int optionType, int messageType,
+                       Icon icon, Object[] options, Object initialValue, ActionListener a) {
+               actionListener = a;
+               this.options = options;
+               setListener(a);
+               process(JOptionPane.showOptionDialog(frame, message, title, optionType, messageType, icon, options,
+                               initialValue));
+               unsetListener();
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, ActionListener a) {
+               showConfirmDialog(frame, message, title, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, a);
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, int optionType, ActionListener a) {
+               showConfirmDialog(frame, message, title, optionType, JOptionPane.QUESTION_MESSAGE, a);
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, int optionType, int messageType,
+                       ActionListener a) {
+               setListener(a);
+               process(JOptionPane.showConfirmDialog(frame, message, title, optionType, messageType));
+               unsetListener();
+       }
+
+       /**
+        * retrieve selection from the ActionEvent, for which "this" is getSource()
+        * 
+        * @return
+        */
+       public Object getChoice() {
+               return choice;
+       }
+
+       public int getOption() {
+               if (!(choice instanceof Integer)) {
+                       throw new java.lang.IllegalArgumentException("AsyncDialog.getOption called for non-Integer choice");
+               }
+               return ((Integer) choice).intValue();
+       }
+
+       /**
+        * A dialog option that allows for YES, NO, and CLOSE options via
+        * ActionListener. ActionEvent.getID() contains the reply.
+        * 
+        * @param parent   The parent component for the dialog
+        * @param message  The text of the message to display
+        * @param title    Optional title defaults to "Question"
+        * @param listener Handle options based on an ActionEvent
+        */
+       public static void showYesNoAsync(Component parent, Object message, String title, ActionListener listener) {
+               new AsyncDialog().showConfirmDialog(parent, message, (title == null ? "Question" : title),
+                               JOptionPane.YES_NO_OPTION, listener);
+       }
+
+       /**
+        * A dialog option that involves just a YES follower.
+        * @param parent
+        * @param message
+        * @param title TODO
+        * @param yes
+        */
+       public static void showYesAsync(Component parent, Object message, String title, Runnable yes) {
+               AsyncDialog.showYesNoAsync(parent, message, title, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (e.getID() == JOptionPane.YES_OPTION) {
+                                       yes.run();
+                               }
+                       }
+                       
+               });
+       }
+
+       /**
+        * A dialog option that involves just an OK follower.
+        * @param parent
+        * @param message
+        * @param title
+        * @param ok
+        */
+       public static void showOKAsync(Component parent, Object message, String title, Runnable ok) {
+               new AsyncDialog().showConfirmDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (e.getID() == JOptionPane.OK_OPTION) {
+                                       ok.run();
+                               }
+                       }
+                       
+               });
+       }
+
+
+       private void setListener(ActionListener a) {
+               actionListener = a;
+               @SuppressWarnings("unused")
+               Class c = JOptionPane.class; // loads the class
+               /** @j2sNative c.$clazz$.listener = this */
+       }
+
+       private void unsetListener() {
+               /** @j2sNative javax.swing.JOptionPane.listener = null */
+       }
+
+       /**
+        * Switch from property change to action.
+        * 
+        */
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               value = evt.getNewValue();
+               switch (evt.getPropertyName()) {
+               case "inputValue":                      
+                       process(value);
+                       break;
+               case "value":
+                       if (value != null && options == null && !(value instanceof Integer)) {
+                               process(getOptionIndex(((JOptionPane) evt.getSource()).getOptions(), value));
+                               return;
+                       }
+                       if (options != null) {
+                               int i = getOptionIndex(options, value);
+                               value = Integer.valueOf(i >= 0 ? i : JOptionPane.CLOSED_OPTION);
+                       } 
+                       process(value);
+                       break;
+               }
+       }
+
+       private int getOptionIndex(Object[] options, Object val) {
+               if (options != null)
+                       for (int i = 0; i < options.length; i++) {
+                               if (options[i] == val)
+                                       return i;
+                       }
+               return -1;
+       }
+
+       public Object getValue() {
+               if (wantsInput || options == null)
+                       return value;
+               int val = ((Integer) value).intValue();
+               return (val < 0 ? null : options[val]);
+       }
+       
+       private boolean processed;
+
+       /**
+        * Return for confirm dialog.
+        * 
+        * @param ret may be JavaScript NaN, testable as ret != ret or ret != - -ret
+        */
+       private void process(int ret) {
+               if (ret != -(-ret) || processed)
+                       return;
+               processed = true;
+               choice = ret;
+               actionListener.actionPerformed(new ActionEvent(this, ret, "SelectedOption"));
+       }
+
+       private void process(Object ret) {
+               if (ret instanceof UIResource || processed)
+                       return;
+               processed = true;
+               choice = ret;
+               actionListener.actionPerformed(new ActionEvent(this, 
+                               ret == null ? JOptionPane.CANCEL_OPTION : JOptionPane.OK_OPTION, 
+                                               (ret == null ? null : ret.toString())));
+       }
+
+
+}
diff --git a/src/javajs/async/AsyncFileChooser.java b/src/javajs/async/AsyncFileChooser.java
new file mode 100644 (file)
index 0000000..849fe83
--- /dev/null
@@ -0,0 +1,217 @@
+package javajs.async;
+
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.function.Function;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.filechooser.FileSystemView;
+
+/**
+ * A simple Asynchronous file chooser for JavaScript and Java.
+ * 
+ * Requires an OK runnable; JavaScript can return notification of cancel for
+ * file reading only, not saving.
+ * 
+ * @author Bob Hanson
+ */
+
+public class AsyncFileChooser extends JFileChooser implements PropertyChangeListener {
+
+       private int optionSelected;
+       private Runnable ok, cancel; // sorry, no CANCEL in JavaScript for file open
+       private boolean isAsyncSave = true;
+       private static boolean notified;
+
+       public AsyncFileChooser() {
+               super();
+       }
+
+       public AsyncFileChooser(File file) {
+               super(file);
+       }
+
+       public AsyncFileChooser(File file, FileSystemView view) {
+               super(file, view);
+       }
+
+       @Deprecated
+       @Override
+       public int showDialog(Component frame, String btnText) {
+               // This one can come from JFileChooser - default is OPEN
+               return super.showDialog(frame, btnText);
+       }
+
+       private int err() {
+               try {
+                       throw new java.lang.IllegalAccessException("Warning! AsyncFileChooser interface bypassed!");
+               } catch (IllegalAccessException e) {
+                       e.printStackTrace();
+               }
+               return JFileChooser.ERROR_OPTION;
+       }
+
+       @Deprecated
+       @Override
+       public int showOpenDialog(Component frame) {
+               return err();
+       }
+
+       @Override
+       public int showSaveDialog(Component frame) {
+               isAsyncSave  = false;
+               return super.showSaveDialog(frame);
+       }
+
+       /**
+        * 
+        * @param frame
+        * @param btnLabel "open" or "save"
+        * @param ok
+        * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+        */
+       public void showDialog(Component frame, String btnLabel, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               if (getDialogType() != JFileChooser.SAVE_DIALOG && cancel != null)
+                       notifyCancel();
+               process(super.showDialog(frame, btnLabel));
+       }
+
+       /**
+        * 
+        * @param frame
+        * @param ok
+        * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+        */
+       public void showOpenDialog(Component frame, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               if (cancel != null)
+                       notifyCancel();
+               process(super.showOpenDialog(frame));
+       }
+
+       /**
+        * 
+        * This just completes the set. It is not necessary for JavaScript, because JavaScript
+        * will just throw up a simple modal OK/Cancel message anyway.
+        * 
+        * @param frame
+        * @param ok
+        * @param cancel must be null
+        */
+       public void showSaveDialog(Component frame, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               this.cancel = cancel;
+               process(super.showSaveDialog(frame));
+       }
+
+       
+       /**
+        * Locate a file for input or output. Note that JavaScript will not return on cancel for OPEN_DIALOG.
+        * 
+        * @param title       The title for the dialog
+        * @param mode        OPEN_DIALOG or SAVE_DIALOG
+        * @param processFile function to use when complete
+        */
+         public static void getFileAsync(Component parent, String title, int mode, Function<File, Void> processFile) {
+                 // BH no references to this method. So changing its signature for asynchonous use
+                 // And it didn't do as advertised - ran System.exit(0) if canceled
+           // create and display a file dialog
+               AsyncFileChooser fc = new AsyncFileChooser();
+               fc.setDialogTitle(title);
+               Runnable after = new Runnable() {
+
+                       @Override
+                       public void run() {
+                               processFile.apply(fc.getSelectedFile());
+                       }
+       
+               };
+               if (mode == JFileChooser.OPEN_DIALOG) {
+                       fc.showOpenDialog(parent, after, after);  
+               } else {
+                       fc.showSaveDialog(parent, after, after);  
+               }
+                               
+         }
+           
+               /**
+                * Run yes.run() if a file doesn't exist or if the user allows it, else run no.run()
+                * @param parent
+                * @param filename
+                * @param title
+                * @param yes (approved)
+                * @param no (optional)
+                */
+               public static void checkReplaceFileAsync(Component parent, File outfile, String title, Runnable yes, Runnable no) {
+                       if (outfile.exists()) {
+                               AsyncDialog.showYesNoAsync(parent,
+                                               outfile + " exists. Replace it?", null, new ActionListener() {
+               
+                                                       @Override
+                                                       public void actionPerformed(ActionEvent e) {
+                                                               switch (e.getID()) {
+                                                               case JOptionPane.YES_OPTION:
+                                                                       yes.run();
+                                                                       break;
+                                                               default:
+                                                                       if (no != null)
+                                                                               no.run();
+                                                                       break;
+                                                               }
+                                                       }
+               
+                                               });
+               
+                       } else {
+                               yes.run();
+                       }
+               
+               }
+
+       private void notifyCancel() {
+               if (!notified) {
+                       System.err.println("developer note: JavaScript cannot fire a FileChooser CANCEL action");
+               }
+               notified = true;
+       }
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               switch (evt.getPropertyName()) {
+               case "SelectedFile":
+               case "SelectedFiles":
+                       process(optionSelected = (evt.getNewValue() == null ? CANCEL_OPTION : APPROVE_OPTION));
+                       break;
+               }
+       }
+
+       private void process(int ret) {
+               if (ret != -(-ret))
+                       return; // initial JavaScript return is NaN
+               optionSelected = ret;
+               File f = getSelectedFile();
+               if (f == null) {
+                       if (cancel != null)
+                               cancel.run();
+               } else {
+                       if (ok != null)
+                               ok.run();
+               }
+       }
+
+       public int getSelectedOption() {
+               return optionSelected;
+       }
+
+       public static byte[] getFileBytes(File f) {
+               return /** @j2sNative f.秘bytes || */null;
+       }
+
+}
diff --git a/src/javajs/async/AsyncSwingWorker.java b/src/javajs/async/AsyncSwingWorker.java
new file mode 100644 (file)
index 0000000..703f7f5
--- /dev/null
@@ -0,0 +1,431 @@
+package javajs.async;
+
+import java.awt.Component;
+
+import javax.swing.ProgressMonitor;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import javajs.async.SwingJSUtils.StateHelper;
+import javajs.async.SwingJSUtils.StateMachine;
+
+/**
+ * v. 2020.06.03 
+ * 
+ * Executes synchronous or asynchronous tasks using a SwingWorker in Java or
+ * JavaScript, equivalently.
+ * 
+ * Unlike a standard SwingWorker, AsyncSwingWorker may itself be asynchronous.
+ * For example, it might load a file asynchronously, or carry out a background
+ * process in JavaScript much like one might be done in Java, but with only a
+ * single thread.
+ * 
+ * Whereas a standard SwingWorker would execute done() long before the
+ * asynchronous task completed, this class will wait until progress has been
+ * asynchronously set greater or equal to its max value or the task is canceled
+ * before executing that method.
+ * 
+ * Three methods must be supplied by the subclass:
+ * 
+ * void initAsync()
+ * 
+ * int doInBackgroundAsync(int progress)
+ * 
+ * void doneAsync()
+ * 
+ * Both initAsync() and doneAsync() are technically optional - they may be
+ * empty. doInBackgroundAsync(), however, is the key method where, like
+ * SwingWorker's doInBackground, the main work is done. The supplied progress
+ * parameter reminds the subclass of where it is at, and the return value allows
+ * the subclass to update the progress field in both the SwingWorker and the
+ * ProgressMonitor.
+ * 
+ * If it is desired to run the AsyncSwingWorker synchronously, call the
+ * executeSynchronously() method rather than execute(). Never call
+ * SwingWorker.run().
+ * 
+ * Note that doInBackgroundAsync runs on the Java AWT event queue. This means
+ * that, unlike a true SwingWorker, it will run in event-queue sequence, after
+ * anything that that method itself adds to the queue. This is what SwingWorker itself
+ * does with its done() signal. 
+ * 
+ * If doInBackgroundAsync has tasks that are time intensive, the thing to do is to
+ * 
+ * (a) pause this worker by setting the value of progress for the NEXT step:
+ * 
+ *    setProgressAsync(n);
+ *    
+ * (b) pause the timer so that when doInBackgroundAsync returns, the timer is not fired:
+ * 
+ *    setPaused(true);
+ * 
+ * (c) start your process as new Thread, which bypasses the AWT EventQueue:
+ * 
+ *    new Thread(Runnable).start();
+ *    
+ * (d) have your thread, when it is done, return control to this worker:
+ * 
+ *    setPaused(false);
+ *    
+ * This final call restarts the worker with the currently specified progress value.
+ * 
+ * @author hansonr
+ *
+ */
+public abstract class AsyncSwingWorker extends SwingWorker<Void, Void> implements StateMachine {
+
+
+       // PropertyChangeEvent getPropertyName()
+       
+       private static final String PROPERTY_STATE = "state";
+       private static final String PROPERTY_PAUSE = "pause";
+       
+       // PropertyChangeEvent getNewValue()
+       
+       public static final String STARTED_ASYNC = "STARTED_ASYNC";
+       public static final String STARTED_SYNC = "STARTED_SYNC";
+       
+       public static final String DONE_ASYNC = "DONE_ASYNC";
+       public static final String CANCELED_ASYNC = "CANCELED_ASYNC";
+       
+       public static final String PAUSED = "PAUSED";
+       public static final String RESUMED = "RESUMED";
+
+       protected int progressAsync;
+
+       /**
+        * Override to provide initial tasks.
+        */
+       abstract public void initAsync();
+
+       /**
+        * Given the last progress, do some portion of the task that the SwingWorker
+        * would do in the background, and return the new progress. returning max or
+        * above will complete the task.
+        * 
+        * @param progress
+        * @return new progress
+        */
+       abstract public int doInBackgroundAsync(int progress);
+
+       /**
+        * Do something when the task is finished or canceled.
+        * 
+        */
+       abstract public void doneAsync();
+
+       protected ProgressMonitor progressMonitor;
+
+       protected int delayMillis;
+       protected String note;
+       protected int min;
+       protected int max;
+       protected int progressPercent;
+
+       protected boolean isAsync;
+       private Exception exception;
+
+       /**
+        * Construct an asynchronous SwingWorker task that optionally will display a
+        * ProgressMonitor. Progress also can be monitored by adding a
+        * PropertyChangeListener to the AsyncSwingWorker and looking for the "progress"
+        * event, just the same as for a standard SwingWorker.
+        * 
+        * @param owner       optional owner for the ProgressMonitor, typically a JFrame
+        *                    or JDialog.
+        * 
+        * @param title       A non-null title indicates we want to use a
+        *                    ProgressMonitor with that title line.
+        * 
+        * @param delayMillis A positive number indicating the delay we want before
+        *                    executions, during which progress will be reported.
+        * 
+        * @param min         The first progress value. No range limit.
+        * 
+        * @param max         The last progress value. No range limit; may be greater
+        *                    than min.
+        * 
+        */
+       public AsyncSwingWorker(Component owner, String title, int delayMillis, int min, int max) {
+               if (title != null && delayMillis > 0) {
+                       progressMonitor = new ProgressMonitor(owner, title, "", Math.min(min, max), Math.max(min, max));
+                       progressMonitor.setProgress(Math.min(min, max)); // displays monitor
+               }
+               this.delayMillis = Math.max(0, delayMillis);
+               this.isAsync = (delayMillis > 0);
+
+               this.min = min;
+               this.max = max;
+       }
+
+       public void executeAsync() {
+               firePropertyChange(PROPERTY_STATE, null, STARTED_ASYNC);
+               super.execute();
+       }
+
+       public void executeSynchronously() {
+               firePropertyChange(PROPERTY_STATE, null, STARTED_SYNC);
+               isAsync = false;
+               delayMillis = 0;
+               try {
+                       doInBackground();
+               } catch (Exception e) {
+                       exception = e;
+                       e.printStackTrace();
+                       cancelAsync();
+               }
+       }
+
+       public Exception getException() {
+               return exception;
+       }
+
+       public int getMinimum() {
+               return min;
+       }
+
+       public void setMinimum(int min) {
+               this.min = min;
+               if (progressMonitor != null) {
+                       progressMonitor.setMinimum(min);
+               }
+       }
+
+       public int getMaximum() {
+               return max;
+       }
+
+       public void setMaximum(int max) {
+               if (progressMonitor != null) {
+                       progressMonitor.setMaximum(max);
+               }
+               this.max = max;
+       }
+
+       public int getProgressPercent() {
+               return progressPercent;
+       }
+
+       public void setNote(String note) {
+               this.note = note;
+               if (progressMonitor != null) {
+                       progressMonitor.setNote(note);
+               }
+       }
+
+       /**
+        * Cancel the asynchronous process.
+        * 
+        */
+       public void cancelAsync() {
+               helper.interrupt();
+       }
+
+       /**
+        * Check to see if the asynchronous process has been canceled.
+        *
+        * @return true if StateHelper is not alive anymore
+        * 
+        */
+       public boolean isCanceledAsync() {
+               return !helper.isAlive();
+       }
+
+       /**
+        * Check to see if the asynchronous process is completely done.
+        * 
+        * @return true only if the StateMachine is at STATE_DONE
+        * 
+        */
+       public boolean isDoneAsync() {
+               return helper.getState() == STATE_DONE;
+       }
+
+       /**
+        * Override to set a more informed note for the ProcessMonitor.
+        * 
+        * @param progress
+        * @return
+        */
+       public String getNote(int progress) {
+               return String.format("Completed %d%%.\n", progress);
+       }
+
+       /**
+        * Retrieve the last note delivered by the ProcessMonitor.
+        * 
+        * @return
+        */
+       public String getNote() {
+               return note;
+       }
+
+       public int getProgressAsync() {
+               return progressAsync;
+       }
+
+       /**
+        * Set the [min,max] progress safely.
+        * 
+        * SwingWorker only allows progress between 0 and 100. This method safely
+        * translates [min,max] to [0,100].
+        * 
+        * @param n
+        */
+       public void setProgressAsync(int n) {
+               n = (max > min ? Math.max(min, Math.min(n, max)) : Math.max(max, Math.min(n, min)));
+               progressAsync = n;
+               n = (n - min) * 100 / (max - min);
+               n = (n < 0 ? 0 : n > 100 ? 100 : n);
+               progressPercent = n;
+       }
+
+       ///// the StateMachine /////
+
+       private final static int STATE_INIT = 0;
+       private final static int STATE_LOOP = 1;
+       private final static int STATE_WAIT = 2;
+       private final static int STATE_DONE = 99;
+       
+       private StateHelper helper;
+
+       protected StateHelper getHelper() {
+               return helper;
+       }
+
+       private boolean isPaused;
+
+       protected void setPaused(boolean tf) {
+               isPaused = tf;
+               firePropertyChange(PROPERTY_PAUSE, null, (tf ? PAUSED : RESUMED));
+               if (!tf)
+                       stateLoop();
+       }
+
+       protected boolean isPaused() {
+               return isPaused;
+       }
+
+       /**
+        * The StateMachine's main loop.
+        * 
+        * Note that a return from this method will exit doInBackground, trigger the
+        * isDone() state on the underying worker, and scheduling its done() for
+        * execution on the AWTEventQueue.
+        *
+        * Since this happens essentially immediately, it is unlikely that
+        * SwingWorker.isCancelled() will ever be true. Thus, the SwingWorker task
+        * itself won't be cancelable in Java or in JavaScript, since its
+        * doInBackground() method is officially complete, and isDone() is true well
+        * before we are "really" done. FutureTask will not set isCancelled() true once
+        * the task has run.
+        * 
+        * We are using an asynchronous task specifically because we want to have the
+        * opportunity for the ProgressMonitor to report in JavaScript. We will have to
+        * cancel our task and report progress explicitly using our own methods.
+        * 
+        */
+       @Override
+       public boolean stateLoop() {
+               while (helper.isAlive() && !isPaused) {
+                       switch (helper.getState()) {
+                       case STATE_INIT:
+                               setProgressAsync(min);
+                               initAsync();
+                               helper.setState(STATE_WAIT);
+                               continue;
+                       case STATE_LOOP:
+                               if (checkCanceled()) {
+                                       helper.setState(STATE_DONE);
+                                       firePropertyChange(PROPERTY_STATE, null, CANCELED_ASYNC);
+                               } else {
+                                       int ret = doInBackgroundAsync(progressAsync);                                   
+                                       if (!helper.isAlive() || isPaused) {
+                                               continue;
+                                       }
+                                       progressAsync = ret;
+                                       setProgressAsync(progressAsync);
+                                       setNote(getNote(progressAsync));
+                                       setProgress(progressPercent);
+                                       if (progressMonitor != null) {
+                                               progressMonitor.setProgress(max > min ? progressAsync : max + min - progressAsync);
+                                       }
+                                       helper.setState(progressAsync == max ? STATE_DONE : STATE_WAIT);
+                               }
+                               continue;
+                       case STATE_WAIT:
+                               // meaning "sleep" and then "loop"
+                               helper.setState(STATE_LOOP);
+                               helper.sleep(delayMillis);
+                               return true;
+                       default:
+                       case STATE_DONE:
+                               stopProgressMonitor();
+                               // Put the doneAsync() method on the AWTEventQueue
+                               // just as for SwingWorker.done().
+                               if (isAsync) {
+                                       SwingUtilities.invokeLater(doneRunnable);
+                               } else {
+                                       doneRunnable.run();
+                               }
+
+                               return false;
+                       }
+               }
+               if (!helper.isAlive()) {
+                       stopProgressMonitor();
+               }
+               return false;
+       }
+
+       private void stopProgressMonitor() {
+               if (progressMonitor != null) {
+                       progressMonitor.close();
+                       progressMonitor = null;
+               }
+       }
+
+       private Runnable doneRunnable = new Runnable() {
+               @Override
+               public void run() {
+                       doneAsync();
+                       firePropertyChange(PROPERTY_STATE, null, DONE_ASYNC);
+               }
+
+       };
+
+       private boolean checkCanceled() {
+               if (isMonitorCanceled() || isCancelled()) {
+                       helper.interrupt();
+                       return true;
+               }
+               return false;
+       }
+
+       //// final SwingWorker methods not to be used by subclasses ////
+
+       private boolean isMonitorCanceled() {
+               return (progressMonitor != null && progressMonitor.isCanceled());
+       }
+
+       /**
+        * see SwingWorker, made final here.
+        * 
+        */
+       @Override
+       final protected Void doInBackground() throws Exception {
+               helper = new StateHelper(this);
+               setProgressAsync(min);
+               helper.next(STATE_INIT);
+               return null;
+       }
+
+       /**
+        * see SwingWorker, made final here. Nothing to do.
+        * 
+        */
+       @Override
+       final public void done() {
+       }
+
+}
diff --git a/src/javajs/async/SwingJSUtils.java b/src/javajs/async/SwingJSUtils.java
new file mode 100644 (file)
index 0000000..82a0265
--- /dev/null
@@ -0,0 +1,651 @@
+package javajs.async;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.stream.Collectors;
+
+import javax.imageio.ImageIO;
+import javax.swing.Timer;
+
+/**
+ * A set of generally useful SwingJS-related methods. Includes:
+ * 
+ * alternatives to using getCodeBase() for loading resources, due to issues in
+ * Eclipse setting that incorrectly (but no problem in JavaScript)
+ * 
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+public class SwingJSUtils {
+       /**
+        * Set the dimension for the applet prior to j2sApplet's call to 
+        * run the applet. Must be used to create a static field:
+        * 
+        * <code>
+        *   private static Dimension dim = 
+        * </code>
+        * 
+        * 
+        * Then, if it is desired also to have Java also set this, add
+        * 
+        *  if (dim != null) setSize(dim);  
+        *  
+        *  to the applet's init() method.
+        * 
+        * @param w
+        * @param h
+        * @return the Dimension
+        * 
+        * @author hansonr
+        */
+       public static Dimension setDim(int w, int h) {
+               String baseURI = (/** @j2sNative document.body.baseURI || */
+               null);
+               boolean isTest = (baseURI == null || baseURI.indexOf("_applet.html") >= 0);
+               if (!isTest)
+                       return null;
+               /**
+                * @j2sNative
+                * 
+                *                      J2S.thisApplet.__Info.width = w; J2S.thisApplet.__Info.height = h;
+                */
+               return new Dimension(w, h);
+       }
+
+       /**
+        * Reliably load a resource of a specific type from the code directory
+        * 
+        * adaptable - here we are returning an image or a string
+        * 
+        * @param cl       the classname of the object to return (Image.class,
+        *                 String.class) null for InputStream
+        * @param filename
+        * @return
+        * 
+        * @author hansonr
+        */
+       public static Object getResource(Class<?> baseClass, String filename, Class<?> cl) {
+               System.out.println("mpUtils.SwingJSUtils.getResource " + baseClass.getCanonicalName() + " " + filename);
+               InputStream is = baseClass.getResourceAsStream(filename);
+               if (cl == Image.class) {
+                       try {
+                               return ImageIO.read(is);
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                       }
+               } else if (cl == String.class) {
+                       return new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
+               }
+               return is;
+       }
+
+       /**
+        * Pre-fetch images during the static entry of the class. This should provide
+        * plenty of clock ticks, since the file transfer is synchronous, and all we are
+        * waiting for is the DOM image object to initialize.
+        * 
+        * @param cl
+        * @param images
+        * @param root
+        * @param nImages
+        * @param ext
+        */
+       public static void loadImagesStatic(Class<?> cl, Image[] images, String root, String ext, int nImages) {
+               for (int i = nImages; --i >= 0;) {
+
+                       // Bild laden und beim MediaTracker registrieren
+                       // MediaTracker ladekontrolle = new MediaTracker(this);
+
+                       // BH SwingJS -- adding generally useful method for loading data
+                       // avoiding the use of getCodeBase(), which for some reason does not work in
+                       // Eclipse.
+
+                       images[i] = (Image) getResource(cl, root + i + "." + ext, Image.class);
+//                     /**
+//                      * @j2sNative
+//                      * $("body").append(images[i]._imgNode);
+//                      * 
+//                      */
+//                       ladekontrolle.addImage(scharf[i],i);
+                       // Warten , bis Bild ganz geladen ist
+
+//                       try {ladekontrolle.waitForID(i);}
+//                       catch (InterruptedException e)
+//                          {}
+               }
+       }
+
+       /**
+        * Fill an array with images based on a String[] listing
+        * @param cl reference class
+        * @param root  optional root path, ending in "/"
+        * @param names source file names
+        * @param images  array to fill
+        */
+       public static void loadImagesStatic(Class<?> cl, String root, String[] names, Image[] images) {
+               for (int i = names.length; --i >= 0;) {
+                       images[i] = (Image) getResource(cl, root + names[i], Image.class);
+               }
+       }
+
+       /**
+        * Eclipse-friendly image getting
+        * 
+        * @param c
+        * @param fileName
+        * @return
+        */
+       public static Image getImage(Component c, String fileName) {
+               return getImage(c.getClass(), fileName);
+       }
+
+       /**
+        * Eclipse-friendly image getting
+        * 
+        * @param c
+        * @param fileName
+        * @return
+        */
+       public static Image getImage(Class<?> c, String fileName) {
+               return (Image) getResource(c, fileName, Image.class);
+       }
+
+       /**
+        * Clear the component graphic. BH added this for JavaScript because changing
+        * the browser zoom can change the size of the canvas for unknown reasons.
+        * 
+        * @param c
+        */
+       public static void clearComponent(Component c) {
+               Graphics gc = c.getGraphics();
+               gc.clearRect(0, 0, c.getWidth(), c.getHeight());
+               gc.dispose();
+       }
+
+       
+       /**
+        * A simple interface to the machine loop, generally of the form 
+        * <code>
+        *   public boolean stateLoop() {
+        *   while (stateHepler.isAlive()) {
+        *     switch (stateHelper.getState()) {
+        *     case STATE_XXX:
+        *        ...
+        *        return stateHelper.delayState(100,STATE_YYY);
+        *     case STATE_YYY:
+        *        ...
+        *        stateHelper.setState(STATE_ZZZ);
+        *        continue;
+        *     case STATE_ZZZ:
+        *        ...
+        *        return stateHelper.delayAction(100, MY_ID, "myCommand", myListener, STATE_XXX);        *   
+        *     case STATE_DONE:
+        *        ...
+        *        stateHelper.interrupt();
+        *        return false;
+        *     }
+        *     return true;
+        *   }
+        *   return false;
+        *   }
+        * </code>
+        * @author hansonr
+        *
+        */
+       public interface StateMachine {
+
+               public boolean stateLoop();
+
+       }
+       /**
+        * StateHelper is a class that facilitates moving from an asychronous multithreaded model to a state-oriented model of programming
+        * for SwingJS animations and other asynchronous business.
+        *  
+        * @author hansonr
+        *
+        */
+       public static class StateHelper {
+               
+               public static final int UNCHANGED = Integer.MIN_VALUE;
+
+               private StateMachine machine;
+               private int state;
+               private int level;
+               
+               private boolean interrupted;
+               
+
+               public StateHelper(StateMachine machine) {
+                       this.machine = machine;
+               }
+
+               public void interrupt() {
+                       interrupted = true;
+               }
+               
+               public boolean isInterrupted() {
+                       return interrupted;
+               }
+               
+               public boolean isAlive() {
+                       return !interrupted;
+               }
+               
+               public void restart() {
+                       interrupted = false;
+               }
+               
+               public void setState(int state) {
+                       this.state = this.stateNext = state;
+               }
+
+               public int getState() {
+                       return state;
+               }
+
+               public void setLevel(int level) {
+                       this.level = this.levelNext = level;
+               }
+
+               public int getLevel() {
+                       return level;
+               }
+               
+               public void setNextState(int next) {
+                       stateNext = next; 
+               }
+               
+               public int getNextState() {
+                       return stateNext;
+               }
+
+               public int getNextLevel() {
+                       return levelNext;
+               }
+
+               public void setNextLevel(int next) {
+                       levelNext = next; 
+               }
+
+               /** 
+                * 
+                * NOTE: this method must remain private; it is accessed via p$1
+                * 
+                * @return
+                */
+               private boolean nextState() {
+                       return next(stateNext, levelNext);
+               }
+               /**
+                * Set the state and run machine.stateLoop().
+                * 
+                * @param state something meaningful to the machine
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean next(int state) {
+                       return next(state, 0);
+               }
+               
+               /**
+                * Set the state and level, and then run machine.stateLoop(). Driven directly or via delayedState or delayedAction
+                * 
+                * @param state something meaningful to the machine
+                * @param level something meaningful to the machine
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean next(int state, int level) {
+                       return nextStatePriv(this, state, level);
+               }
+
+               private static boolean nextStatePriv(Object oThis, int state, int level) {
+                       StateHelper me = (StateHelper) oThis;
+                       if (me.interrupted)
+                               return false;
+                       if (level != UNCHANGED)
+                               me.level = level;
+                       if (state != UNCHANGED)
+                               me.state = state;
+                       return me.machine.stateLoop();
+               }
+
+               /**
+                * After the given number of milliseseconds, set the new state and run the machines stateLoop with unchanged level
+                * 
+                * @param ms the number of milliseconds to delay; 0 to execute synchronously             * 
+                * @param stateNext  the next state to run
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedState(int ms, int stateNext) {
+                       return delayedState(ms, stateNext, level);
+               }
+
+               private Timer stateTimer;
+
+               private int stateNext;
+               private int levelNext;
+               
+               /**
+                * After the given number of milliseseconds, set the new state and level, and
+                * run the machines stateLoop
+                * 
+                * @param ms        the number of milliseconds to delay; 0 to execute
+                *                  synchronously *
+                * @param stateNext the next state
+                * @param levelNext the next level
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               
+               public boolean delayedState(int ms, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false;
+                       if (ms == 0)
+                               return next(stateNext, levelNext);
+                       if (stateNext != UNCHANGED)
+                               this.stateNext = stateNext;
+                       if (levelNext != UNCHANGED)
+                               this.levelNext = levelNext;
+                       
+                       /**
+                        * @j2sNative
+                        * var me = this;
+                        * setTimeout(function(){
+                        *  p$1.nextState.apply(me, []);
+                        * },ms);
+                        */
+                       {
+                               // Java only
+
+                               if (stateTimer == null) {
+                                       stateTimer = new Timer(ms, new ActionListener() {
+                                               @Override
+                                               public void actionPerformed(ActionEvent e) {
+                                                       nextState();
+                                               }
+
+                                       });
+                                       stateTimer.setRepeats(false);
+                                       stateTimer.start();
+                               } else {
+                                       stateTimer.restart();
+                               }
+                       }
+                       return true;
+               }
+
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener) {
+                       return delayedAction(ms, id, command, listener, UNCHANGED, UNCHANGED);
+               }
+
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * 
+                * @param state    the next state to go to after this listener is called; UNCHANGED to let the listener take care of this.
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener, int state) {
+                       return delayedAction(ms, id, command, listener, state, UNCHANGED);
+               }               
+               
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds. Setting BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE)
+                * allows the listener to handle continuing the loop.
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * @param stateNext  state to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
+                * @param levelNext  level to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false; 
+                       ActionEvent event = new ActionEvent(this, id, command);
+                       if (ms == 0) {
+                               listener.actionPerformed(event);
+                               return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStatePriv(this, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
+                       }
+                       
+                       StateHelper me = this;
+                       
+                       Timer timer = new Timer(ms, id == ActionEvent.ACTION_PERFORMED ? listener : new ActionListener() {
+                               @Override
+                               public void actionPerformed(ActionEvent e) {
+                                       if (!interrupted)
+                                               listener.actionPerformed(event);
+                                       if (!interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
+                                               nextStatePriv(me, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext);
+                               }
+                               
+                       });
+                       timer.setRepeats(false);
+                       timer.start();
+                       return true;
+               }
+
+               public static void delayedRun(int ms, Runnable runnable) {
+                       new StateHelper(null).delayedRun(ms, runnable, UNCHANGED, UNCHANGED);
+               }
+
+               
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds. Setting
+                * BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE) allows the
+                * listener to handle continuing the loop.
+                * 
+                * @param ms        delay milliseconds. if 0, then this action will be called
+                *                  synchronously
+                * @param id        id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                  necessarily
+                * @param command   key for ActionEvent.getCommand()
+                * @param listener  ActionListener to be called.
+                * @param stateNext state to run after the event is processed by the listener,
+                *                  or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
+                *                  this.
+                * @param levelNext level to run after the event is processed by the listener,
+                *                  or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
+                *                  this.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedRun(int ms, Runnable runnable, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false;
+                       if (ms == 0) {
+                               return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStateIfUnchanged(this, runnable,
+                                               stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
+                       }
+                       StateHelper me = this;
+                       /**
+                        * @j2sNative
+                        * 
+                        * setTimeout(function() {
+                        * 
+                        *    me.nextStateIfUnchanged$O$O$I$I.apply(me, [me, runnable, stateNext, levelNext]);
+                        * 
+                        * },ms);
+                        */
+                       {
+                               Timer timer = new Timer(ms, new ActionListener() {
+                                       @Override
+                                       public void actionPerformed(ActionEvent e) {
+                                               nextStateIfUnchanged(me, runnable, stateNext, levelNext);
+                                       }
+
+                               });
+                               timer.setRepeats(false);
+                               timer.start();
+                       }
+                       return true;
+               }
+
+               protected boolean nextStateIfUnchanged(Object oThis, Object runnable, int stateNext, int levelNext) {
+                       StateHelper me = (StateHelper)(oThis);
+                       if (!me.interrupted)
+                               ((Runnable) runnable).run();
+                       if (!me.interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
+                               nextStatePriv(oThis, stateNext == UNCHANGED ? me.state : stateNext,
+                                               levelNext == UNCHANGED ? me.level : levelNext);
+                       return true;
+               }
+
+               /**
+                * sleep and then execute the next state
+                * @param ms
+                */
+               public void sleep(int ms) {
+                       int next = stateNext;
+                       delayedState(ms, next);
+               }
+       }
+       
+       /**
+        * open a "url-like" input stream
+        * @param base
+        * @param fileName
+        * @return
+        */
+       public static BufferedInputStream openStream(Class<?> base, String fileName) {
+               String s = (String) getResource(base, fileName, String.class);
+        return new BufferedInputStream(new ByteArrayInputStream(s.getBytes()));
+       }
+
+
+       public static class Performance {
+
+               public final static int TIME_RESET = 0;
+
+               public final static int TIME_MARK = 1;
+
+               public static final int TIME_SET = 2;
+
+               public static final int TIME_GET = 3;
+
+               public static long time, mark, set, duration;
+
+               /**
+                * typical usage:
+                * 
+                * Performance.timeCheck(null, Platform.TIME_MARK);
+                * 
+                * ...
+                * 
+                * Performance.timeCheck("some message", Platform.TIME_MARK);
+                * 
+                * reset...[set/mark]n...get  (total time) (time spent between set and mark)
+                * 
+                * set...get   (total time) (time spent between set and get)
+                * 
+                * long t0 = now(0); ........ ; dt = now(t0); (time since t0)e
+                * 
+                * @param msg
+                * @param mode
+                */
+               public static void timeCheck(String msg, int mode) {
+                       msg = timeCheckStr(msg, mode);
+                       if (msg != null)
+                               System.err.println(msg);
+               }
+
+               public static long now(long t) {
+                       return System.currentTimeMillis() - t;
+               }
+               
+               public static String timeCheckStr(String msg, int mode) {
+                       long t = System.currentTimeMillis();
+                       switch (mode) {
+                       case TIME_RESET:
+                               time = mark = t;
+                               duration = set = 0;
+                               if (msg != null) {
+                                       return ("Platform: timer reset\t\t\t" + msg);
+                               }
+                               break;
+                       case TIME_SET:
+                               if (time == 0)
+                                       time = t;
+                               set = t;
+                               break;
+                       case TIME_MARK:
+                               if (set > 0) {
+                                       // total time between set/mark points
+                                       duration += (t - set);
+                               } else {
+                                       if (time == 0) {
+                                               time = mark = t;
+                                       }
+                                       if (msg != null) {
+                                               long m0 = mark;
+                                               mark = t;
+                                               return ("Platform: timer mark\t" + ((t - time) / 1000f) + "\t" + ((t - m0) / 1000f) + "\t"
+                                                               + msg);
+                                       }
+                                       mark = t;
+                               }
+                               break;
+                       case TIME_GET:
+                               if (msg != null) {
+                                       if (mark < set)
+                                               duration = t - set;
+                                       return ("Platform: timer get\t" + ((t - time) / 1000f) + "\t" + ((duration) / 1000f) + "\t" + msg);
+                               }
+                               set = 0;
+                               break;
+                       }
+                       return null;
+               }
+
+       }
+
+}
\ No newline at end of file
index 4b5c5ea..5a827b7 100644 (file)
@@ -32,7 +32,6 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
diff --git a/src/swingjs/api/Interface.java b/src/swingjs/api/Interface.java
new file mode 100644 (file)
index 0000000..6616780
--- /dev/null
@@ -0,0 +1,78 @@
+/* $RCSfile$
+ * $Author$
+ * $Date$
+ * $Revision$
+ *
+ * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
+ * for use in SwingJS via transpilation into JavaScript using Java2Script.
+ *
+ * Copyright (C) 2006  The Jmol Development Team
+ *
+ * Contact: jmol-developers@lists.sf.net
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+package swingjs.api;
+
+public class Interface {
+       
+       private static String instances=""; 
+
+       public static Object getInstanceWithParams(String name, Class<?>[] classes, Object... params) {
+               try {
+                       Class<?> cl = Class.forName(name);
+                       return  cl.getConstructor(classes).newInstance(params);
+               } catch (Exception e) {
+                       return null;
+               }
+       }
+  public static Object getInstance(String name, boolean isQuiet) {
+       Object x = null;
+       /**
+        * @j2sNative
+        * 
+        * Clazz._isQuietLoad = isQuiet;
+        */
+       {}
+    try {
+       if (!isQuiet && instances.indexOf(name + ";") <= 0) {
+               System.out.println("swingjs.api.Interface creating instance of " + name);
+               instances += name + ";";
+       }
+       Class<?> y = Class.forName(name); 
+      if (y != null)
+       x = y.newInstance();
+    } catch (Throwable e) {
+      System.out.println("Swingjs.api.Interface Error creating instance for " + name + ": \n" + e);
+      /**
+       * @j2sNative
+       * 
+       * if (e.stack)System.out.println(e.stack);
+       */
+      {}
+    } finally {
+       /**
+        * @j2sNative
+        * 
+        * Clazz._isQuietLoad = false;
+        */
+       {}      
+    }
+    return x;          
+  }
+
+}
diff --git a/src/swingjs/api/JSFileHandler.java b/src/swingjs/api/JSFileHandler.java
new file mode 100644 (file)
index 0000000..fde82e8
--- /dev/null
@@ -0,0 +1,7 @@
+package swingjs.api;
+
+public interface JSFileHandler {
+
+       void handleFileLoaded(Object data, String fileName);
+
+}
index a20ab55..d08b5b2 100644 (file)
@@ -2,185 +2,354 @@ package swingjs.api;
 
 import java.awt.Component;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
+import java.util.function.Function;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.swing.JComponent;
 
 import swingjs.api.js.HTML5Applet;
 
 public interface JSUtilI
 {
 
-  /**
-   * Indicate to SwingJS that the given file type is binary.
-   * 
-   * @param ext
-   */
-  void addBinaryFileType(String ext);
-
-  /**
-   * Indicate to SwingJS that we can load files using AJAX from the given
-   * domain, such as "www.stolaf.edu", because we know that CORS access has been
-   * provided.
-   * 
-   * @param domain
-   */
-  void addDirectDatabaseCall(String domain);
-
-  /**
-   * Cache or uncache data under the given path name.
-   * 
-   * @param path
-   * @param data
-   *          null to remove from the cache
-   */
-  void cachePathData(String path, Object data);
-
-  /**
-   * Get the HTML5 object corresponding to the specified Component, or the
-   * current thread if null.
-   * 
-   * @param c
-   *          the associated component, or null for the current thread
-   * @return HTML5 applet object
-   */
-  HTML5Applet getAppletForComponent(Component c);
-
-  /**
-   * Get an attribute applet.foo for the applet found using getApplet(null).
-   * 
-   * @param key
-   * @return
-   */
-  Object getAppletAttribute(String key);
-
-  /**
-   * Get the code base (swingjs/j2s, probably) for the applet found using
-   * getApplet(null).
-   * 
-   * @return
-   */
-  URL getCodeBase();
-
-  /**
-   * Get the document base (wherever the page is) for the applet found using
-   * getApplet(null).
-   * 
-   * @return
-   */
-
-  URL getDocumentBase();
-
-  /**
-   * Get an attribute from the div on the page that is associated with this
-   * frame, i.e. with id frame.getName() + "-div".
-   * 
-   * @param frame
-   * @param type
-   *          "node" or "dim"
-   * @return
-   */
-  Object getEmbeddedAttribute(Component frame, String type);
-
-  /**
-   * Get a file synchronously.
-   * 
-   * @param path
-   * @param asString
-   *          true for String; false for byte[]
-   * @return byte[] or String
-   */
-  Object getFile(String path, boolean asString);
-
-  /**
-   * Get the ç§˜bytes field associated with a file, but only if the File object
-   * itself has them attached, not downloading them.
-   * 
-   * @param f
-   * @return
-   */
-  byte[] getBytes(File f);
-
-  /**
-   * Retrieve a HashMap consisting of whatever the application wants, but
-   * guaranteed to be unique to this app context, that is, for the applet found
-   * using getApplet(null).
-   * 
-   * @param contextKey
-   * @return
-   */
-  HashMap<?, ?> getJSContext(Object contextKey);
-
-  /**
-   * Load a resource -- probably a core file -- if and only if a particular
-   * class has not been instantialized. We use a String here because if we used
-   * a .class object, that reference itself would simply load the class, and we
-   * want the core package to include that as well.
-   * 
-   * @param resourcePath
-   * @param className
-   */
-  void loadResourceIfClassUnknown(String resource, String className);
-
-  /**
-   * Read all applet.__Info properties for the applet found using
-   * getApplet(null) that start with the given prefix, such as "jalview_". A
-   * null prefix retrieves all properties. Note that non-string properties will
-   * be stringified.
-   * 
-   * @param prefix
-   *          an application prefix, or null for all properties
-   * @param p
-   *          properties to be appended to
-   */
-  void readInfoProperties(String prefix, Properties p);
-
-  /**
-   * Set an attribute for the applet found using getApplet(null). That is,
-   * applet[key] = val.
-   * 
-   * @param key
-   * @param val
-   */
-  void setAppletAttribute(String key, Object val);
-
-  /**
-   * Set an attribute of applet's Info map for the applet found using
-   * getApplet(null). That is, applet.__Info[key] = val.
-   * 
-   * @param infoKey
-   * @param val
-   */
-  void setAppletInfo(String infoKey, Object val);
-
-  /**
-   * Set the given File object's ç§˜bytes field from an InputStream or a byte[]
-   * array. If the file is a JSTempFile, then also cache those bytes.
-   * 
-   * @param f
-   * @param isOrBytes
-   *          BufferedInputStream, ByteArrayInputStream, FileInputStream, or
-   *          byte[]
-   * @return
-   */
-  boolean setFileBytes(File f, Object isOrBytes);
-
-  /**
-   * Same as setFileBytes, but also caches the data if it is a JSTempFile.
-   * 
-   * @param is
-   * @param outFile
-   * @return
-   */
-  boolean streamToFile(InputStream is, File outFile);
-
-  /**
-   * Switch the flag in SwingJS to use or not use the JavaScript Map object in
-   * Hashtable, HashMap, and HashSet. Default is enabled.
-   * 
-   */
-
-  void setJavaScriptMapObjectEnabled(boolean enabled);
+       /**
+        * The HTML5 canvas delivers [r g b a r g b a ...] which is not a Java option.
+        * The closest Java option is TYPE_4BYTE_ABGR, but that is not quite what we
+        * need. SwingJS decodes TYPE_4BYTE_HTML5 as TYPE_4BYTE_RGBA"
+        * 
+        * ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+        * 
+        * int[] nBits = { 8, 8, 8, 8 };
+        * 
+        * int[] bOffs = { 0, 1, 2, 3 };
+        * 
+        * colorModel = new ComponentColorModel(cs, nBits, true, false,
+        * Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
+        * 
+        * raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height,
+        * width * 4, 4, bOffs, null);
+        * 
+        * Note, however, that this buffer type should only be used for direct buffer access
+        * using
+        * 
+        * 
+        * 
+        */
+       public static final int TYPE_4BYTE_HTML5 = -6;
+       
+       /**
+        * The HTML5 VIDEO element wrapped in a BufferedImage. 
+        * 
+        * To be extended to allow video capture?
+        */
+       public static final int TYPE_HTML5_VIDEO = Integer.MIN_VALUE;
+
+       /**
+        * Indicate to SwingJS that the given file type is binary.
+        * 
+        * @param ext
+        */
+       void addBinaryFileType(String ext);
+
+       /**
+        * Indicate to SwingJS that we can load files using AJAX from the given domain,
+        * such as "www.stolaf.edu", because we know that CORS access has been provided.
+        * 
+        * @param domain
+        */
+       void addDirectDatabaseCall(String domain);
+
+       /**
+        * Cache or uncache data under the given path name.
+        * 
+        * @param path
+        * @param data null to remove from the cache
+        */
+       void cachePathData(String path, Object data);
+
+       /**
+        * Get the HTML5 object corresponding to the specified Component, or the current thread if null.
+        * 
+        * @param c  the associated component, or null for the current thread
+        * @return HTML5 applet object
+        */
+       HTML5Applet getAppletForComponent(Component c);
+
+       /**
+        * Get an attribute applet.foo for the applet found using getApplet(null).
+        * 
+        * @param key
+        * @return
+        */
+       Object getAppletAttribute(String key);
+
+
+       /**
+        * Get the applet's __Info map or an attribute of that map for the applet found using
+        * getApplet(null). That is, applet.__Info or applet.__Info[InfoKey].
+        * 
+        * @param infoKey if null, return the full __Info map
+        */
+       Object getAppletInfo(String infoKey);
+
+       /**
+        * Get the code base (swingjs/j2s, probably) for the applet found using
+        * getApplet(null).
+        * 
+        * @return
+        */
+       URL getCodeBase();
+
+       /**
+        * Get the document base (wherever the page is) for the applet found using
+        * getApplet(null).
+        * 
+        * @return
+        */
+
+       URL getDocumentBase();
+
+       /**
+        * Get an attribute from the div on the page that is associated with this frame,
+        * i.e. with id frame.getName() + "-div".
+        * 
+        * @param frame
+        * @param type  "node" or "dim"
+        * @return
+        */
+       Object getEmbeddedAttribute(Component frame, String type);
+
+       /**
+        * Get a file synchronously.
+        * 
+        * @param path
+        * @param asString true for String; false for byte[]
+        * @return byte[] or String
+        */
+       Object getFile(String path, boolean asString);
+
+       /**
+        * Get the ç§˜bytes field associated with a file, but only if the File object itself has
+        * them attached, not downloading them.
+        * 
+        * @param f
+        * @return
+        */
+       byte[] getBytes(File f);
+
+       /**
+        * Retrieve a HashMap consisting of whatever the application wants, but
+        * guaranteed to be unique to this app context, that is, for the applet found using
+        * getApplet(null).
+        * 
+        * @param contextKey
+        * @return
+        */
+       HashMap<?, ?> getJSContext(Object contextKey);
+
+       /**
+        * Load a resource -- probably a core file -- if and only if a particular class
+        * has not been instantialized. We use a String here because if we used a .class
+        * object, that reference itself would simply load the class, and we want the
+        * core package to include that as well.
+        * 
+        * @param resourcePath
+        * @param className
+        */
+       void loadResourceIfClassUnknown(String resource, String className);
+
+       /**
+        * Read all applet.__Info properties  for the applet found using
+        * getApplet(null) that start with the given prefix, such as "jalview_".
+        * A null prefix retrieves all properties. Note that non-string properties will be
+        * stringified.
+        * 
+        * @param prefix an application prefix, or null for all properties
+        * @param p      properties to be appended to
+        */
+       void readInfoProperties(String prefix, Properties p);
+
+       /**
+        * Set an attribute for the applet found using
+        * getApplet(null). That is, applet[key] = val.
+        * 
+        * @param key
+        * @param val
+        */
+       void setAppletAttribute(String key, Object val);
+
+       /**
+        * Set an attribute of applet's Info map for the applet found using
+        * getApplet(null). That is, applet.__Info[key] = val.
+        * 
+        * @param infoKey
+        * @param val
+        */
+       void setAppletInfo(String infoKey, Object val);
+
+       /**
+        * Set the given File object's ç§˜bytes field from an InputStream or a byte[] array.
+        * If the file is a JSTempFile, then also cache those bytes.
+        * 
+        * @param f
+        * @param isOrBytes BufferedInputStream, ByteArrayInputStream, FileInputStream, or byte[]
+        * @return
+        */
+       boolean setFileBytes(File f, Object isOrBytes);
+
+       /**
+        * Set the given URL object's _streamData field from an InputStream or a byte[] array.
+        * 
+        * @param f
+        * @param isOrBytes BufferedInputStream, ByteArrayInputStream, FileInputStream, or byte[]
+        * @return
+        */
+       boolean setURLBytes(URL url, Object isOrBytes);
+
+       /**
+        * Same as setFileBytes.
+        * 
+        * @param is
+        * @param outFile
+        * @return
+        */
+       boolean streamToFile(InputStream is, File outFile);
+
+         /**
+          * Switch the flag in SwingJS to use or not use the JavaScript Map object in
+          * Hashtable, HashMap, and HashSet. Default is enabled.
+          *       * 
+          */
+       void setJavaScriptMapObjectEnabled(boolean enabled);
+
+
+       /**
+        * Open a URL in a browser tab.
+        * 
+        * @param url
+        * @param target null or specific tab, such as "_blank"
+        */
+       void displayURL(String url, String target);
+
+       /**
+        * Retrieve cached bytes for a path (with unnormalized name)
+        * from J2S._javaFileCache.
+        * 
+        * @param path
+        * 
+        * @return byte[] or null
+        */
+       byte[] getCachedBytes(String path);
+       
+       /**
+        * Attach cached bytes to a file-like object, including URL,
+        * or anything having a ç§˜bytes field (File, URI, Path)
+        * from J2S._javaFileCache. That is, allow two such objects
+        * to share the same underlying byte[ ] array.
+        * 
+        * 
+        * @param URLorURIorFile
+        * @return byte[] or null
+        */
+       byte[] addJSCachedBytes(Object URLorURIorFile);
+
+       /**
+        * Seek an open ZipInputStream to the supplied ZipEntry, if possible.
+        * 
+        * @param zis the ZipInputStream
+        * @param ze  the ZipEntry
+        * @return the length of this entry, or -1 if, for whatever reason, this was not possible
+        */
+       long seekZipEntry(ZipInputStream zis, ZipEntry ze);
+
+       /**
+        * Retrieve the byte array associated with a ZipEntry.
+        * 
+        * @param ze
+        * @return
+        */
+       byte[] getZipBytes(ZipEntry ze);
+
+       /**
+        * Java 9 method to read all (remaining) bytes from an InputStream. In SwingJS,
+        * this may just create a new reference to an underlying Int8Array without
+        * copying it.
+        * 
+        * @param zis
+        * @return
+        * @throws IOException 
+        */
+       byte[] readAllBytes(InputStream zis) throws IOException;
+
+       /**
+        * Java 9 method to transfer all (remaining) bytes from an InputStream to an OutputStream.
+        * 
+        * @param is
+        * @param out
+        * @return
+        * @throws IOException
+        */
+       long transferTo(InputStream is, OutputStream out) throws IOException;
+
+       /**
+        * Retrieve any bytes already attached to this URL.
+        * 
+        * @param url
+        * @return
+        */
+       byte[] getURLBytes(URL url);
+
+       /**
+        * Set a message in the lower-left-hand corner SwingJS status block.
+        * 
+        * @param msg
+        * @param doFadeOut
+        */
+       void showStatus(String msg, boolean doFadeOut);
+
+       /**
+        * Asynchronously retrieve the byte[] for a URL.
+        * 
+        * @param url
+        * @param whenDone
+        */
+       void getURLBytesAsync(URL url, Function<byte[], Void> whenDone);
+
+       /**
+        * Experimental method to completely disable a Swing Component's user interface.
+        * 
+        * @param jc
+        * @param enabled
+        */
+       void setUIEnabled(JComponent jc, boolean enabled);
+
+
+       /**
+        * Play an audio
+        * @param buffer
+        * @param format a javax.sound.sampled.AudioFormat
+        * @throws Exception 
+        */
+       void playAudio(byte[] buffer, Object format) throws Exception;
+
+       /**
+        * For either an applet or an application, get the ORIGINAL __Info as a Map that
+        * has a full set up lower-case keys along with whatever non-all-lower-case keys
+        * provided at start-up.
+        * 
+        * @return
+        */
+       Map<String, Object> getAppletInfoAsMap();
+
+       
+  void setAppClass(Object j);
 
 }
diff --git a/src/swingjs/api/js/DOMNode.java b/src/swingjs/api/js/DOMNode.java
new file mode 100644 (file)
index 0000000..4de6ee0
--- /dev/null
@@ -0,0 +1,309 @@
+package swingjs.api.js;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+/**
+ * A mix of direct DOM calls on DOM nodes and convenience methods to do that.
+ * 
+ * NOTE: DO NOT OVERLOAD THESE METHODS, as this package will not be qualified.
+ * 
+ * @author hansonr
+ *
+ */
+public interface DOMNode {
+
+       public static JQuery jQuery = /** @j2sNative jQuery.$ || (jQuery.$ = jQuery) || */null;
+
+       // "abstract" in the sense that these are the exact calls to JavaScript
+       
+
+       public void addEventListener(String event, Object listener);
+       public void removeEventListener(String event);
+       public void removeEventListener(String event, Object listener);
+
+
+
+       public String[] getAttributeNames();
+
+       public String getAttribute(String name);
+
+       public void setAttribute(String attr, String val);
+
+       public void appendChild(DOMNode node);
+       
+       public void prepend(DOMNode node);
+       
+       public void insertBefore(DOMNode node, DOMNode refNode);
+       
+       public DOMNode removeChild(DOMNode node);
+
+       public void focus();
+       public boolean hasFocus();
+       public void blur();
+
+       public DOMNode removeAttribute(String attr);
+       
+       public void setSelectionRange(int start, int end, String direction);
+
+       public Rectangle getBoundingClientRect();
+       
+       // static convenience methods
+
+       public static DOMNode createElement(String key, String id) {
+               DOMNode node = null;
+               /**
+                * @j2sNative
+                *                                      node = document.createElement(key);
+                *                                      id && (node.id = id);
+                */
+               return node;
+       }
+
+       public static DOMNode getElement(String id) {
+               return (/**  @j2sNative  document.getElementById(id) ||*/ null);
+       }
+
+       public static DOMNode createTextNode(String text) {
+               return (/** @j2sNative document.createTextNode(text) || */ null); 
+       }
+
+       public static DOMNode getParent(DOMNode node) {
+               return (/**  @j2sNative  node.parentNode ||*/ null);
+       }
+       
+       public static DOMNode getPreviousSibling(DOMNode node) {
+               return (/**  @j2sNative  node.previousSibling ||*/ null);
+       }
+       
+       public static DOMNode firstChild(DOMNode node) {
+               return  (/**  @j2sNative node.firstChild ||*/ null);
+       }
+
+       public static DOMNode lastChild(DOMNode node) {
+               return  (/**  @j2sNative node.lastChild ||*/ null);
+       }
+
+       public static DOMNode setZ(DOMNode node, int z) {
+               return setStyles(node, "z-index", "" + z);
+       }
+
+       public static Object getAttr(Object node, String attr) {
+               /**
+                * @j2sNative
+                * 
+                * if (!node)
+                *   return null;
+                * var a = node[attr];
+                * return (typeof a == "undefined" ? null : a); 
+                */
+               {
+               return null;
+               }
+       }
+
+       public static int getAttrInt(DOMNode node, String attr) {
+               return  (/**  @j2sNative node && node[attr] ||*/ 0);
+       }
+
+       public static String getStyle(DOMNode node, String style) {
+               return  (/**  @j2sNative node && node.style[style] ||*/ null);
+       }
+
+       public static void getCSSRectangle(DOMNode node, Rectangle r) {
+               /**
+                * @j2sNative
+                * 
+                *       r.x = parseInt(node.style.left.split("p")[0]);
+                *       r.y = parseInt(node.style.top.split("p")[0]);
+                *       r.width = parseInt(node.style.width.split("p")[0]);
+                *       r.height = parseInt(node.style.height.split("p")[0]);
+                * 
+                */
+       }
+
+       public static DOMNode setAttr(DOMNode node, String attr, Object val) {
+               /**
+                * @j2sNative
+                * 
+                *                      attr && (node[attr] = (val == "秘TRUE" ? true : val == "秘FALSE" ? false : val));
+                * 
+                */
+               return node;
+       }
+
+
+       public static void setAttrInt(DOMNode node, String attr, int val) {
+               /**
+                * @j2sNative
+                * 
+                *                      node[attr] = val;
+                * 
+                */
+       }
+
+
+       /**
+        * allows for null key to be skipped (used in audio)
+        * 
+        * @param node
+        * @param attr
+        * @return
+        */
+       public static DOMNode setAttrs(DOMNode node, Object... attr) {
+               /**
+                * @j2sNative
+                * 
+                *            for (var i = 0; i < attr.length;) { 
+                *              C$.setAttr(node, attr[i++],attr[i++]);
+                *            }
+                */
+               return node;
+       }
+
+       public static DOMNode setStyles(DOMNode node, String... attr) {
+               /**
+                * @j2sNative
+                * 
+                *            if (node) for (var i = 0; i < attr.length;) {
+                *             node.style[attr[i++]] = attr[i++];
+                *             }
+                * 
+                */
+               return node;
+       }
+
+       public static DOMNode setSize(DOMNode node, int width, int height) {
+               return setStyles(node, "width", width + "px", "height", height + "px");
+       }
+
+       public static DOMNode setPositionAbsolute(DOMNode node) {
+               return DOMNode.setStyles(node, "position", "absolute");
+       }
+
+       public static void setVisible(DOMNode node, boolean visible) {
+               setStyles(node, "display", visible ? "block" : "none");
+       }
+
+       public static DOMNode setTopLeftAbsolute(DOMNode node, int top, int left) {
+               DOMNode.setStyles(node, "top", top + "px");
+               DOMNode.setStyles(node, "left", left + "px");
+               return DOMNode.setStyles(node, "position", "absolute");
+       }
+
+       public static void addHorizontalGap(DOMNode domNode, int gap) {
+               DOMNode label = DOMNode.setStyles(DOMNode.createElement("label", null), 
+                               "letter-spacing", gap + "px", "font-size", "0pt");
+               label.appendChild(DOMNode.createTextNode("."));
+               domNode.appendChild(label);
+       }
+
+       public static void appendChildSafely(DOMNode parent, DOMNode node) {
+               /**
+                * @j2sNative
+                * if (!parent || node.parentElement == parent)
+                *   return;
+                */
+               parent.appendChild(node);
+       }
+       
+       // static jQuery calls
+       
+       /**
+        * jQuery height()
+        * 
+        * @param node
+        * @return height
+        */
+       public static int getHeight(DOMNode node) {
+               return jQuery.$(node).height();
+       }
+
+       /**
+        * jQuery width()
+        * 
+        * @param node
+        * @return width
+        */
+       public static int getWidth(DOMNode node) {
+               return jQuery.$(node).width();
+       }
+
+       /**
+        * jQuery remove()
+        * 
+        * Remove this node and return its parent. Automatically removing all events
+        * attached to it.
+        * 
+        * @param node
+        * @return parent or null
+        */
+       public static void dispose(DOMNode node) {
+               if (node != null)               
+                       jQuery.$(node).remove();
+       }
+
+       /**
+        * Just remove the node, keeping its events and data 
+        * @param node
+        */
+       public static void remove(DOMNode node) {
+               
+               // NOTE: IE does not have node.remove()
+               
+               DOMNode p = getParent(node);
+               if (p != null)
+                       p.removeChild(node);
+       }
+
+       /**
+        * just detaches all the nodes; doesn't remove their listeners
+        * @param node
+        */
+       public static void detachAll(DOMNode node) {
+               /**
+                * @j2sNative
+                *  if(node)
+                *    while(node.lastChild)
+                *      node.removeChild(node.lastChild);
+                */
+       }
+       
+       /**
+        * jQuery detach() + append()
+        * 
+        * @param node
+        * @param container
+        * @return parent if container is null, or container if it is not null
+        */
+       public static DOMNode transferTo(DOMNode node, DOMNode container) {
+               if (node == null)
+                       return null;
+               DOMNode p = getParent(node);
+               try {
+                       if (p != null)
+                               jQuery.$(node).detach();
+               } catch (Throwable e) {
+                       // ignore
+               }
+                if (container == null)
+                       return p; 
+                jQuery.$(container).append(node);
+               return container;
+       }
+
+       public static Object getEmbedded(String name, String type) {
+               DOMNode node = DOMNode.getElement(name + "-div");
+               if (node == null)
+                       return null;
+               switch (type) {
+               case "node":
+                       return node;
+               case "dim":
+                       return new Dimension(DOMNode.getWidth(node), DOMNode.getHeight(node));
+               default:
+                       return DOMNode.getAttr(node, type);
+               }
+       }
+
+}
diff --git a/src/swingjs/api/js/HTML5AudioContext.java b/src/swingjs/api/js/HTML5AudioContext.java
new file mode 100644 (file)
index 0000000..fecc66b
--- /dev/null
@@ -0,0 +1,58 @@
+package swingjs.api.js;
+
+public interface HTML5AudioContext {
+
+       // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
+       // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API
+       
+       void close();
+
+       float[] createBuffer(int nChannels, int frameCount, int sampleRate);
+
+       void createBufferSource();
+
+       void createMediaElementSource();
+
+       void createMediaStreamSource();
+
+       void createMediaStreamDestination();
+
+       void createScriptProcessor();
+
+       //void createStereoPanner();
+
+       void createAnalyser();
+
+       void createBiquadFilter();
+
+       void createChannelMerger();
+
+       void createChannelSplitter();
+
+       void createConvolver();
+
+       void createDelay();
+
+       void createDynamicsCompressor();
+
+       void createGain();
+
+       void createIIRFilter();
+
+       void createOscillator();
+
+       void createPanner();
+
+       void createPeriodicWave();
+
+       void createWaveShaper();
+
+       void createAudioWorker();
+
+       void decodeAudioData();
+
+       void resume();
+
+       void suspend();
+
+}
diff --git a/src/swingjs/api/js/HTML5Canvas.java b/src/swingjs/api/js/HTML5Canvas.java
new file mode 100644 (file)
index 0000000..f6d0604
--- /dev/null
@@ -0,0 +1,59 @@
+package swingjs.api.js;
+
+import java.awt.image.BufferedImage;
+
+public interface HTML5Canvas extends DOMNode {
+
+       HTML5CanvasContext2D getContext(String str2d);
+
+       /*
+        * Retrieves the byte[] data buffer from an HTML5 CANVAS element, optionally
+        * first setting its contents to a source IMG, CANVAS, or VIDEO element.
+        * 
+        */
+       static byte[] getDataBufferBytes(HTML5Canvas canvas, DOMNode sourceNode, int w, int h) {
+               if (sourceNode != null) {
+                       DOMNode.setAttrInt(canvas, "width", w);
+                       DOMNode.setAttrInt(canvas, "height", h);
+               }
+               HTML5CanvasContext2D ctx = canvas.getContext("2d");
+               if (sourceNode != null) {
+                       ctx.drawImage(sourceNode, 0, 0, w, h);
+               }
+               // Coerse int[] to byte[]
+               return (byte[]) (Object) ctx.getImageData(0, 0, w, h).data;
+       }
+
+       /**
+        * Install a source image (img, video, or canvas) into a matching BufferedImage 
+        * 
+        * @param sourceNode
+        * @param image
+        */
+       static void setImageNode(DOMNode sourceNode, BufferedImage image) {
+               /**
+                * @j2sNative
+                * 
+                *                      image._setImageNode$O$Z(sourceNode, false);
+                * 
+                */             {
+                       // image._setImageNode(sourceNode, false);
+                }
+       }
+       
+       
+
+       static HTML5Canvas createCanvas(int width, int height, String id) {
+               HTML5Canvas canvas = (HTML5Canvas) DOMNode.createElement("canvas", (id == null ? "img" + Math.random() : id + ""));
+               DOMNode.setStyles(canvas, "width", width + "px", "height", height + "px");
+               /**
+                * @j2sNative
+                * 
+                * canvas.width = width;
+                * canvas.height = height;
+                * 
+                */
+               return canvas;
+       }
+
+}
diff --git a/src/swingjs/api/js/HTML5CanvasContext2D.java b/src/swingjs/api/js/HTML5CanvasContext2D.java
new file mode 100644 (file)
index 0000000..226f270
--- /dev/null
@@ -0,0 +1,164 @@
+package swingjs.api.js;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D.Float;
+
+public abstract class HTML5CanvasContext2D {
+
+       public class ImageData {
+               public int[] data; 
+       }
+
+       public ImageData imageData;
+       
+       public Object[][] _aSaved;
+       
+       public double lineWidth;
+
+       public String font, fillStyle, strokeStyle;
+
+       public float globalAlpha;
+
+       public abstract void drawImage(DOMNode img, double sx,
+                       double sy, double swidth, double sheight, double dx, double dy, double width, double height);
+
+       public abstract ImageData getImageData(int x, int y, int width, int height);
+
+       public abstract void beginPath();
+
+       public abstract void moveTo(double x0, double y0);
+
+       public abstract void lineTo(double x1, double y1);
+
+       public abstract void stroke();
+
+       public abstract void save();
+
+       public abstract void scale(double f, double g);
+
+       public abstract void arc(double centerX, double centerY, double radius, double startAngle, double  endAngle, boolean counterclockwise);
+
+       public abstract void closePath();
+
+       public abstract void restore();
+
+       public abstract void translate(double x, double y);
+       
+       public abstract void rotate(double radians);
+
+       public abstract void fill();
+
+
+       public abstract void fill(String winding);
+
+       public abstract void rect(double x, double y, double width, double height);
+
+       public abstract void fillText(String s, double x, double y);
+
+       public abstract void fillRect(double x, double y, double width, double height);
+
+       public abstract void clearRect(double i, double j, double windowWidth, double windowHeight);
+
+       public abstract void setLineDash(int[] dash);
+
+       public abstract void clip();
+
+       public abstract void quadraticCurveTo(double d, double e, double f, double g);
+
+       public abstract void bezierCurveTo(double d, double e, double f, double g, double h, double i);
+
+       public abstract void drawImage(DOMNode img, double x, double y, double width, double height);
+
+       public abstract void putImageData(Object imageData, double x, double y);
+
+       public abstract void transform(double d, double shx, double e, double shy, double f, double g);
+
+
+       /**
+        * pull one save structure onto the stack array ctx._aSaved
+        * 
+        * @param ctx
+        * @return the length of the stack array after the push
+        */
+       public static int push(HTML5CanvasContext2D ctx, Object[] map) {
+               /**
+                * @j2sNative
+                * 
+                * (ctx._aSaved || (ctx._aSaved = [])).push(map); 
+                * return ctx._aSaved.length;
+                */
+               {
+                       return 0;
+               }
+       }
+
+       /**
+        * pull one save structure off the stack array ctx._aSaved
+        * 
+        * @param ctx
+        * @return
+        */
+       public static Object[] pop(HTML5CanvasContext2D ctx) {
+               /**
+                * @j2sNative
+                * 
+                * return (ctx._aSaved && ctx._aSaved.length > 0 ? ctx._aSaved.pop() : null); 
+                */
+               {
+                       return null;
+               }
+       }
+
+       public static int getSavedLevel(HTML5CanvasContext2D ctx) {
+               /**
+                * @j2sNative
+                * 
+                * return (ctx._aSaved ? ctx._aSaved.length : 0); 
+                */
+               {
+                       return 0;
+               }
+       }
+       
+       public static Object[][] getSavedStack(HTML5CanvasContext2D ctx) {
+          /**
+           * @j2sNative
+           * 
+           * return (ctx._aSaved || []);
+           */
+               {
+                       return null;
+               }
+               
+       }
+
+       public static double[] setMatrix(HTML5CanvasContext2D ctx, AffineTransform transform) {
+               double[] m = /**  @j2sNative ctx._m || */ null;
+               if (transform == null) {
+                       /** @j2sNative ctx._m = null; */
+                       return null;                    
+               }
+               if (m == null) {
+                       /**
+                        * @j2sNative
+                        * ctx._m = m = new Array(6);
+                        */
+                       transform.getMatrix(m);
+               }
+               return m;
+       }
+
+       public static void createLinearGradient(HTML5CanvasContext2D ctx, Float p1, Float p2, String css1, String css2) {
+               /**
+                * @j2sNative
+                * 
+                *   var grd = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
+                *   grd.addColorStop(0,css1);
+                *   grd.addColorStop(1,css2);
+                *   ctx.fillStyle = grd;
+                */
+               }
+
+       abstract public void drawImage(DOMNode domNode, int x, int y);
+
+}
diff --git a/src/swingjs/api/js/HTML5DataTransfer.java b/src/swingjs/api/js/HTML5DataTransfer.java
new file mode 100644 (file)
index 0000000..b6d91ba
--- /dev/null
@@ -0,0 +1,7 @@
+package swingjs.api.js;
+
+public interface HTML5DataTransfer {
+
+       Object getData(String type);
+
+}
diff --git a/src/swingjs/api/js/HTML5Video.java b/src/swingjs/api/js/HTML5Video.java
new file mode 100644 (file)
index 0000000..073f1ed
--- /dev/null
@@ -0,0 +1,472 @@
+package swingjs.api.js;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.net.URL;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.function.Function;
+
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import swingjs.api.JSUtilI;
+
+/**
+ * A full-service interface for HTML5 video element interaction. Allows setting
+ * and getting HTML5 video element properties. ActionListeners can be set to
+ * listen for JavaScript events associated with a video element.
+ * 
+ * Video is added using a JavaScript-only two-parameter constructor for
+ * ImageIcon with "jsvideo" as the description, allowing for video construction
+ * from byte[], File, or URL.
+ * 
+ * After adding the ImageIcon to a JLabel, calling
+ * jlabel.getClientProperty("jsvideo") returns an HTML5 object of type
+ * HTML5Video (the &lt;video&gt; tag), which has the full suite of HTML5 video
+ * element properties, methods, and events.
+ * 
+ * Access to event listeners is via the method addActionListener, below, which
+ * return an ActionEvent that has as its source both the video element source as
+ * well as the original JavaScript event as an Object[] { jsvideo, event }. The
+ * id of this ActionEvent is 12345, and its command is the name of the event,
+ * for example, "canplay" or "canplaythrough".
+ * 
+ * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement for
+ * details.
+ * 
+ * @author hansonr
+ *
+ */
+public interface HTML5Video extends DOMNode {
+
+       public interface Promise {
+
+       }
+
+       final static String[] eventTypes = new String[] { "audioprocess", // The input buffer of a ScriptProcessorNode is
+                                                                                                                                               // ready to be processed.
+                       "canplay", // The browser can play the media, but estimates that not enough data has been
+                                               // loaded to play the media up to its end without having to stop for further
+                                               // buffering of content.
+                       "canplaythrough", // The browser estimates it can play the media up to its end without stopping
+                                                               // for content buffering.
+                       "complete", // The rendering of an OfflineAudioContext is terminated.
+                       "durationchange", // The duration attribute has been updated.
+                       "emptied", // The media has become empty; for example, this event is sent if the media has
+                                               // already been loaded (or partially loaded), and the load() method is called to
+                                               // reload it.
+                       "ended", // Playback has stopped because the end of the media was reached.
+                       "loadeddata", // The first frame of the media has finished loading.
+                       "loadedmetadata", // The metadata has been loaded.
+                       "pause", // Playback has been paused.
+                       "play", // Playback has begun.
+                       "playing", // Playback is ready to start after having been paused or delayed due to lack of
+                                               // data.
+                       "progress", // Fired periodically as the browser loads a resource.
+                       "ratechange", // The playback rate has changed.
+                       "seeked", // A seek operation completed.
+                       "seeking", // A seek operation began.
+                       "stalled", // The user agent is trying to fetch media data, but data is unexpectedly not
+                                               // forthcoming.
+                       "suspend", // Media data loading has been suspended.
+                       "timeupdate", // The time indicated by the currentTimeattribute has been updated.
+                       "volumechange", // The volume has changed.
+                       "waiting", // Playback has stopped because of a temporary lack of data
+       };
+
+       // direct methods
+
+       public void addTextTrack() throws Throwable;
+
+       public Object captureStream() throws Throwable;
+
+       public String canPlayType(String mediaType) throws Throwable;
+
+       public void fastSeek(double time) throws Throwable;
+
+       public void load() throws Throwable;
+
+       public void mozCaptureStream() throws Throwable;
+
+       public void mozCaptureStreamUntilEnded() throws Throwable;
+
+       public void mozGetMetadata() throws Throwable;
+
+       public void pause() throws Throwable;
+
+       public Promise play() throws Throwable;
+
+       public Promise seekToNextFrame() throws Throwable;
+
+       public Promise setMediaKeys(Object mediaKeys) throws Throwable;
+
+       public Promise setSinkId(String id) throws Throwable;
+
+       // convenience methods
+
+       public static double getDuration(HTML5Video v) {
+               return /** @j2sNative v.duration || */
+               0;
+       }
+
+       public static double setCurrentTime(HTML5Video v, double time) {
+               return /** @j2sNative v.currentTime = time|| */
+               0;
+       }
+
+       public static double getCurrentTime(HTML5Video v) {
+               return /** @j2sNative v.currentTime|| */
+               0;
+       }
+
+       public static Dimension getSize(HTML5Video v) {
+               return new Dimension(/** @j2sNative v.videoWidth || */
+                               0, /** @j2sNative v.videoHeight|| */
+                               0);
+       }
+
+       /**
+        * 
+        * Create a BufferedIfmage from the current frame. The image will be of type
+        * swingjs.api.JSUtilI.TYPE_4BYTE_HTML5, matching the data buffer of HTML5
+        * images.
+        * 
+        * @param v
+        * @param imageType  if Integer.MIN_VALUE, swingjs.api.JSUtilI.TYPE_4BYTE_HTML5
+        * @return
+        */
+       public static BufferedImage getImage(HTML5Video v, int imageType) {
+               Dimension d = HTML5Video.getSize(v);
+               BufferedImage image = (BufferedImage) HTML5Video.getProperty(v, "_image");
+               if (image == null || image.getWidth() != d.width || image.getHeight() != d.height) {
+                       image = new BufferedImage(d.width, d.height, imageType == Integer.MIN_VALUE ? JSUtilI.TYPE_4BYTE_HTML5 : imageType);
+                       HTML5Video.setProperty(v, "_image", image);
+               }
+               HTML5Canvas.setImageNode(v, image);
+               return image;
+       }
+
+       // property setting and getting
+
+       /**
+        * Set a property of the the HTML5 video element using jsvideo[key] = value.
+        * Numbers and Booleans will be unboxed.
+        * 
+        * @param jsvideo the HTML5 video element
+        * @param key
+        * @param value
+        */
+       public static void setProperty(HTML5Video jsvideo, String key, Object value) {
+               if (value instanceof Number) {
+                       /** @j2sNative jsvideo[key] = +value; */
+               } else if (value instanceof Boolean) {
+                       /** @j2sNative jsvideo[key] = !!+value */
+               } else {
+                       /** @j2sNative jsvideo[key] = value; */
+               }
+       }
+
+       /**
+        * Get a property using jsvideo[key], boxing number as Double and boolean as
+        * Boolean.
+        * 
+        * @param jsvideo the HTML5 video element
+        * 
+        * @param key
+        * @return value or value boxed as Double or Boolean
+        */
+       @SuppressWarnings("unused")
+       public static Object getProperty(HTML5Video jsvideo, String key) {
+               Object val = (/** @j2sNative 1? jsvideo[key] : */
+               null);
+               if (val == null)
+                       return null;
+               switch (/** @j2sNative typeof val || */
+               "") {
+               case "number":
+                       return Double.valueOf(/** @j2sNative val || */
+                                       0);
+               case "boolean":
+                       return Boolean.valueOf(/** @j2sNative val || */
+                                       false);
+               default:
+                       return val;
+               }
+       }
+
+       // event action
+
+       /**
+        * Add an ActionListener for the designated events. When an event is fired,
+        * 
+        * @param jsvideo  the HTML5 video element
+        * @param listener
+        * @param events   array of events to listen to or null to listen on all video
+        *                 element event types
+        * @return an array of event/listener pairs that can be used for removal.
+        */
+       public static Object[] addActionListener(HTML5Video jsvideo, ActionListener listener, String... events) {
+               if (events == null || events.length == 0)
+                       events = eventTypes;
+               @SuppressWarnings("unused")
+               Function<Object, Void> f = new Function<Object, Void>() {
+
+                       @Override
+                       public Void apply(Object jsevent) {
+                               String name = (/** @j2sNative jsevent.type || */
+                               "?");
+                               System.out.println("HTML5Video " + name);
+                               ActionEvent e = new ActionEvent(new Object[] { jsvideo, jsevent }, 12345, name,
+                                               System.currentTimeMillis(), 0);
+                               listener.actionPerformed(e);
+                               return null;
+                       }
+               };
+               ArrayList<Object> listeners = new ArrayList<>();
+               for (int i = 0; i < events.length; i++) {
+                       Object func = /**
+                                                        * @j2sNative function(event){f.apply$O.apply(f, [event])} ||
+                                                        */
+                                       null;
+                       listeners.add(events[i]);
+                       listeners.add(func);
+                       if (jsvideo != null)
+                               jsvideo.addEventListener(events[i], func);
+
+               }
+               return listeners.toArray(new Object[listeners.size()]);
+       }
+
+       /**
+        * Remove action listener
+        * 
+        * @param jsvideo   the HTML5 video element
+        * @param listeners an array of event/listener pairs created by
+        *                  addActionListener
+        */
+       public static void removeActionListener(HTML5Video jsvideo, Object[] listeners) {
+               if (listeners == null) {
+                       for (int i = 0; i < eventTypes.length; i++) {
+                               jsvideo.removeEventListener(eventTypes[i]);
+                       }
+               }
+               
+               for (int i = 0; i < listeners.length; i += 2) {
+                       String event = (String) listeners[i];
+                       Object listener = listeners[i + 1];
+                       jsvideo.removeEventListener(event, listener);
+               }
+       }
+
+       /**
+        * Create an ImageIcon which, when placed in a JLabel, displays the video.
+        * 
+        * @param source
+        * @return
+        */
+       public static ImageIcon createIcon(Object source) {
+               try {
+                       if (source instanceof URL) {
+                               return new ImageIcon((URL) source, "jsvideo");
+                       } else if (source instanceof byte[]) {
+                               return new ImageIcon((byte[]) source, "jsvideo");
+                       } else if (source instanceof File) {
+                               return new ImageIcon(Files.readAllBytes(((File) source).toPath()));
+                       } else {
+                               return new ImageIcon(Files.readAllBytes(new File(source.toString()).toPath()));
+                       }
+               } catch (Throwable t) {
+                       return null;
+               }
+       }
+
+       /**
+        * Create a label that, when shown, displays the video.
+        * 
+        * @param source
+        * @return
+        */
+       public static JLabel createLabel(Object source) {
+               ImageIcon icon = (source instanceof ImageIcon ? (ImageIcon) source : createIcon(source));
+               return (icon == null ? null : new JLabel(icon));
+       }
+
+       /**
+        * Create a dialog that includes rudimentary controls. Optional maxWidth allows image downscaling by factors of two.
+        * 
+        * @param parent
+        * @param source 
+        * @param maxWidth
+        * @return
+        */
+       public static JDialog createDialog(Frame parent, Object source, int maxWidth, Function<HTML5Video, Void> whenReady) {
+               JDialog dialog = new JDialog(parent);
+               Container p = dialog.getContentPane();
+               p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
+               JLabel label = (source instanceof JLabel ? (JLabel) source : createLabel(source));
+               label.setAlignmentX(0.5f);
+               // not in Java! dialog.putClientProperty("jsvideo", label);
+               p.add(label);
+               label.setVisible(false);
+               p.add(getControls(label));
+               dialog.setModal(false);
+               dialog.pack();
+               dialog.setVisible(true);
+               dialog.setVisible(false);
+               HTML5Video jsvideo = (HTML5Video) label.getClientProperty("jsvideo");
+               HTML5Video.addActionListener(jsvideo, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (label.getClientProperty("jsvideo.size") != null)
+                                       return;
+                               Dimension dim = HTML5Video.getSize(jsvideo);
+                               while (dim.width > maxWidth) {
+                                       dim.width /= 2;
+                                       dim.height /= 2;
+                               }
+                               label.putClientProperty("jsvideo.size", dim);
+                               label.setPreferredSize(dim);
+                               label.setVisible(true);
+//                             label.invalidate();
+                               dialog.pack();
+//                             dialog.setVisible(false);
+                               if (whenReady != null)
+                                       whenReady.apply(jsvideo);
+                       }
+                       
+               }, "canplaythrough");
+               HTML5Video.setCurrentTime(jsvideo,  0);
+               return dialog;
+       }
+
+       static JPanel getControls(JLabel label) {
+
+               JPanel controls = new JPanel();
+               controls.setAlignmentX(0.5f);
+               JButton btn = new JButton("play");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               try {
+                                       ((HTML5Video) label.getClientProperty("jsvideo")).play();
+                               } catch (Throwable e1) {
+                                       e1.printStackTrace();
+                               }
+                       }
+
+               });
+               controls.add(btn);
+
+               btn = new JButton("pause");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               try {
+                                       ((HTML5Video) label.getClientProperty("jsvideo")).pause();
+                               } catch (Throwable e1) {
+                                       e1.printStackTrace();
+                               }
+                       }
+
+               });
+               controls.add(btn);
+               
+               btn = new JButton("reset");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               HTML5Video.setCurrentTime((HTML5Video) label.getClientProperty("jsvideo"), 0);
+                       }
+
+               });
+               controls.add(btn);
+
+               return controls;
+       }
+
+       /**
+        * Advance to the next frame, using seekToNextFrame() if available, or using the time difference supplied.
+        * 
+        * @param jsvideo
+        * @param dt  seconds to advance if seekToNextFrame() is not available
+        * @return true if can use seekToNextFrame()
+        * 
+        */
+       public static boolean nextFrame(HTML5Video jsvideo, double dt) {
+               Boolean canSeek = (Boolean) getProperty(jsvideo,"_canseek");
+               if (canSeek == null) {
+                       setProperty(jsvideo, "_canseek", canSeek = Boolean.valueOf(getProperty(jsvideo, "seekToNextFrame") != null));
+               }
+               try {                   
+                       if (canSeek) {
+                               jsvideo.seekToNextFrame();
+                       } else {
+                               HTML5Video.setCurrentTime(jsvideo, HTML5Video.getCurrentTime(jsvideo) + dt);                                            
+                       }
+               } catch (Throwable e1) {
+               }
+               return canSeek.booleanValue();
+       }
+
+       public static int getFrameCount(HTML5Video jsvideo) {
+               return (int) (getDuration(jsvideo) / 0.033334);
+       }
+
+// HTMLMediaElement properties
+
+//     audioTracks
+//     autoplay
+//     buffered Read only
+//     controller
+//     controls
+//     controlsList Read only
+//     crossOrigin
+//     currentSrc Read only
+//     currentTime
+//     defaultMuted
+//     defaultPlaybackRate
+//     disableRemotePlayback
+//     duration Read only
+//     ended Read only
+//     error Read only
+//     loop
+//     mediaGroup
+//     mediaKeys Read only
+//     mozAudioCaptured Read only
+//     mozFragmentEnd
+//     mozFrameBufferLength
+//     mozSampleRate Read only
+//     muted
+//     networkState Read only
+//     paused Read only
+//     playbackRate
+//     played Read only
+//     preload
+//     preservesPitch
+//     readyState Read only
+//     seekable Read only
+//     seeking Read only
+//     sinkId Read only
+//     src
+//     srcObject
+//     textTracks Read only
+//     videoTracks Read only
+//     volume
+//     initialTime Read only
+//     mozChannels Read only
+
+}
diff --git a/src/swingjs/api/js/J2SInterface.java b/src/swingjs/api/js/J2SInterface.java
new file mode 100644 (file)
index 0000000..8051c96
--- /dev/null
@@ -0,0 +1,83 @@
+package swingjs.api.js;
+
+import java.awt.Component;
+import java.awt.Point;
+import java.util.Hashtable;
+
+
+/**
+ * An interface to J2S.xxx() functions.
+ * 
+ * @author hansonr
+ *
+ */
+
+public interface J2SInterface {
+
+       void addBinaryFileType(String ext);
+
+       void addDirectDatabaseCall(String domain);
+       
+       boolean debugClip();
+       
+       
+       
+       HTML5Applet findApplet(String htmlName);
+
+       Object getCachedJavaFile(String key);
+
+       /**
+        * 
+        * @param isAll true for check of navigator; otherwise just J2S._lang from j2sLang=xx_XX in URI
+        * @return
+        */
+       String getDefaultLanguage(boolean isAll);
+
+       Object getFileData(String fileName, Object fWhenDone, boolean doProcess, boolean isBinary);
+
+       void getFileFromDialog(Object fWhenDone, String type);
+
+       Object getJavaResource(String resourceName, boolean isJavaPath);
+       
+       String getJavaVersion();
+
+       int getKeyModifiers(Object jQueryEvent);
+       
+       Point getMousePosition(Point p);
+       
+       String getResourcePath(String resourceName, boolean isJavaPath);
+
+       Hashtable<String, Object> getSetJavaFileCache(Object object);
+       
+       Object getSwing(); // JSSwingMenu 
+       
+       int getZ(HTML5Applet applet, String frameType);
+
+       boolean isBinaryUrl(String filename);
+
+       boolean isResourceLoaded(String file, boolean done);
+
+       void readyCallback(String appId, String fullId, boolean isReady, 
+                       Object javaApplet, Object javaAppletPanel);
+
+       void saveFile(String fileName, Object data, String mimeType, String encoding);
+       
+       void setDragDropTarget(Component target, DOMNode node, boolean adding);
+
+       void setDraggable(DOMNode tagNode, Object targetNodeOrFDown);
+       
+       void setKeyListener(DOMNode node);
+
+       void setMouse(DOMNode frameNode, boolean isSwingJS);
+
+       int setWindowZIndex(DOMNode domNode, int pos);
+
+       void unsetMouse(DOMNode frameNode);
+
+       String fixCachePath(String uri);
+
+       void showStatus(String msg, boolean doFadeOut);
+
+
+}
+
diff --git a/src/swingjs/api/js/JQuery.java b/src/swingjs/api/js/JQuery.java
new file mode 100644 (file)
index 0000000..4b21993
--- /dev/null
@@ -0,0 +1,15 @@
+package swingjs.api.js;
+
+public interface JQuery {
+
+  JQueryObject $(Object selector);
+
+  DOMNode parseXML(String xmlData);
+  
+  boolean contains(Object outer, Object inner);
+
+  Object parseJSON(String json);
+
+  Object data(Object node, String attr);
+
+}
diff --git a/src/swingjs/api/js/JQueryObject.java b/src/swingjs/api/js/JQueryObject.java
new file mode 100644 (file)
index 0000000..3e53f6e
--- /dev/null
@@ -0,0 +1,85 @@
+package swingjs.api.js;
+
+public interface JQueryObject {
+
+       public interface JQEvent {
+
+       }
+
+       public abstract void appendTo(Object obj);
+       public abstract JQueryObject append(Object span);
+
+       public abstract void bind(String actions, Object f);
+       public abstract void unbind(String actions);
+
+       public abstract void on(String eventName, Object f);
+
+       public abstract JQueryObject focus();
+       public abstract JQueryObject select();
+
+       public abstract int width();
+       public abstract int height();
+       public abstract Object offset();
+
+
+       public abstract void html(String html);
+
+       public abstract DOMNode get(int i);
+
+       public abstract String attr(String key);
+       public abstract JQueryObject attr(String key, String value);
+       public abstract JQueryObject css(String key, String value);
+
+       public abstract JQueryObject addClass(String name);     
+       public abstract JQueryObject removeClass(String name);
+       
+       public abstract JQueryObject show();
+       public abstract JQueryObject hide();
+
+       public abstract void resize(Object fHandleResize);
+
+
+       /**
+        * closest ancestor
+        * 
+        * @param selector
+        * @return
+        */
+       public abstract JQueryObject closest(String selector);
+
+       /**
+        * find all descendants
+        * 
+        * @param selector
+        * @return
+        */
+       public abstract JQueryObject find(String selector);
+
+       public abstract JQueryObject parent();
+       public abstract void before(Object obj);
+       public abstract void after(Object div);
+
+       
+       /**
+        * remove from tree, but do not clear events
+        */
+       public abstract void detach(); // like remove(), but does not change event settings
+       
+       /**
+        * remove from tree and clear all events -- for disposal only
+        */
+       public abstract void remove();
+
+       /**
+        * fully remove all children, clearing all events
+        */
+       public abstract void empty();
+
+       public abstract DOMNode getElement();
+       
+       public static DOMNode getDOMNode(JQueryObject jnode) {
+               return (jnode == null ? null : ((DOMNode[]) (Object) jnode)[0]);
+       }
+       
+
+}
diff --git a/src/swingjs/api/js/JSFunction.java b/src/swingjs/api/js/JSFunction.java
new file mode 100644 (file)
index 0000000..41d1fb3
--- /dev/null
@@ -0,0 +1,12 @@
+package swingjs.api.js;
+
+/**
+ * A flag that this object is really a JavaScript function that, for example,
+ * might be called from setTimeout(). 
+ * 
+ * @author Bob Hanson
+ *
+ */
+public interface JSFunction {
+
+}
diff --git a/src/swingjs/api/js/JSInterface.java b/src/swingjs/api/js/JSInterface.java
new file mode 100644 (file)
index 0000000..f79ae8d
--- /dev/null
@@ -0,0 +1,43 @@
+package swingjs.api.js;
+
+/**
+ * called by SwingJS JavaScript methods
+ * 
+ */
+public interface JSInterface {
+
+       int cacheFileByName(String fileName, boolean isAdd); // $S$Z
+
+       void cachePut(String key, Object data); // $S$O
+
+       void destroy();
+
+       String getFullName();
+
+       void openFileAsyncSpecial(String fileName, int flags); // $S$I
+
+       boolean processMouseEvent(int id, int x, int y, int modifiers, long time, Object jqevent, int scroll); // $I$I$I$I$J$O$I
+
+       void processTwoPointGesture(float[][][] touches); // AFFF
+
+       void setDisplay(HTML5Canvas canvas);
+
+       void setScreenDimension(int width, int height);
+
+       boolean setStatusDragDropped(int mode, int x, int y, String fileName);
+
+       void startHoverWatcher(boolean enable);
+
+       static void setCursor(String c) {
+               /**
+                * @j2sNative
+                * 
+                * try {
+                * 
+                *   document.body.style.cursor = c;
+                * 
+                * } catch (e) {}
+                */
+       }
+
+}
diff --git a/src/swingjs/api/js/README.txt b/src/swingjs/api/js/README.txt
new file mode 100644 (file)
index 0000000..ed8abb4
--- /dev/null
@@ -0,0 +1,6 @@
+package swingjs.api.js
+
+This package contains interfaces to HTML5 objects and JavaScript functions that 
+are potentially useful to developers without use of  @j2sNative. 
+
+Caution should be used. Their persistence is not guaranteed.
\ No newline at end of file
index 6586c04..70b858b 100644 (file)
@@ -6,8 +6,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.PriorityQueue;
-import java.util.Random;
 import java.util.TreeSet;
 
 import javax.swing.event.TreeExpansionEvent;
@@ -22,11 +20,11 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
          private FragSeqNode _rootIDs = new FragSeqNode("IDs");
          private FragSeqNode _rootFolders = new FragSeqNode("Folders");
 
-       private TreeSet<String> _folders = new TreeSet<String>();
-  private Hashtable<String,FragSeqNode> _folderPathToFolderNode = new Hashtable<String,FragSeqNode>();
+       private TreeSet<String> _folders = new TreeSet<>();
+  private Hashtable<String,FragSeqNode> _folderPathToFolderNode = new Hashtable<>();
 
-  private Hashtable<String,FragSeqNode> _idsToNode = new Hashtable<String,FragSeqNode>();
-  private Hashtable<String,ArrayList<FragSeqNode>> _pathToIDFileNodes = new Hashtable<String,ArrayList<FragSeqNode>>();
+  private Hashtable<String,FragSeqNode> _idsToNode = new Hashtable<>();
+  private Hashtable<String,ArrayList<FragSeqNode>> _pathToIDFileNodes = new Hashtable<>();
   
   public enum SORT_MODE{
          PATH,
@@ -77,7 +75,10 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
                  if (parent.getChildCount()==0)
                  {
                          parent.removeFromParent();
-                         _folderPathToFolderNode.remove(parent);
+        // BH was just parent --
+        System.out.println("Varna FragSeqTreeModel removing " + parent + " "
+                + parent.toString());
+        _folderPathToFolderNode.remove(parent.toString());
                          if (parent.getUserObject() instanceof String)
                          {
                                  String path = parent.getUserObject().toString();
@@ -105,7 +106,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
 
   public void removeFolder(String path)
   {
-         ArrayList<FragSeqNode> toBeRemoved = new ArrayList<FragSeqNode>(); 
+         ArrayList<FragSeqNode> toBeRemoved = new ArrayList<>(); 
          Enumeration en = _folderPathToFolderNode.get(path).children();
          while(en.hasMoreElements())
          {
@@ -168,6 +169,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
          insertFileNode(_folderPathToFolderNode.get(folder), m);
   }
   
+  @Override
   public FragSeqNode getRoot()
   {
          return (FragSeqNode) super.getRoot();
@@ -175,13 +177,14 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
   
   public ArrayList<String> getFolders()
   {
-         ArrayList<String> result = new ArrayList<String>(_folders);
+         ArrayList<String> result = new ArrayList<>(_folders);
          return result;
   }
   
   
    FilenameFilter _f = new FilenameFilter(){
-               public boolean accept(File dir, String name) {
+               @Override
+    public boolean accept(File dir, String name) {
                        return name.toLowerCase().endsWith(".dbn") 
                        || name.toLowerCase().endsWith(".ct")
                        || name.toLowerCase().endsWith(".bpseq")
@@ -199,7 +202,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
   }
 
   
-private Hashtable<FragSeqNode,Boolean> _isExpanded = new Hashtable<FragSeqNode,Boolean>();
+private Hashtable<FragSeqNode,Boolean> _isExpanded = new Hashtable<>();
   
 public boolean isExpanded(FragSeqNode n)
 {
@@ -208,9 +211,12 @@ public boolean isExpanded(FragSeqNode n)
        return _isExpanded.get(n);
   }
   else
-         return false;
+  {
+    return false;
+  }
 }
 
+@Override
 public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
        if (event.getSource() instanceof FragSeqTree)
        {
@@ -242,6 +248,7 @@ public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException
        }
 }
 
+@Override
 public void treeWillCollapse(TreeExpansionEvent event)
                throws ExpandVetoException {
        // TODO Auto-generated method stub
diff --git a/swingjs/README-JALVIEW b/swingjs/README-JALVIEW
new file mode 100644 (file)
index 0000000..735c35b
--- /dev/null
@@ -0,0 +1,23 @@
+@BobHanson The SwingJS-site.zip file is in ./swingjs as you'll expect. 
+It gets unzipped to a transfer location (and not to the actual site location 
+to avoid the unzip happening again unecessarily when something changes in that location, 
+e.g. an Eclipse IDE on-the-fly transpile) which is ./build/jalviewjs/tmp/site_swingjs/.
+
+In the meantime, if you're using Eclipse as IDE, it should be transpiling directly 
+into the actual site location, which is ./build/jalviewjs/site, and if you run the task 
+jalviewjsIDE_PrepareSite (under the Gradle Tasks tab) it will sync the files 
+from ./build/jalviewjs/tmp/site_swingjs/ to ./build/jalviewjs/site/ (along with other unzipped 
+files such as those in ./utils/jalviewjs/libjs/*.zip and files from ./utils/jalviewjs/site-resources/).
+
+If you run the task jalviewjsIDE_AssembleSite it will (/should) also build cores 
+into ./build/jalviewjs/tmp/site_core/ and then sync them to ./build/jalviewjs/site/.
+
+The _j2sclasslist.txt that gets used to build core_jalview.js is ./utils/jalviewjs/_j2sclasslist.txt, 
+whereas other lists are that are used to build cores are all the files found in ./utils/jalviewjs/classlists/.
+Basically all the stuff that gradle uses to build jalviewjs is found in ./utils/jalviewjs with 
+the exception of ./swingjs, which I know you wanted as-is in the top-level.
+
+
+
+
+
index f6fdabe..8f842a1 100644 (file)
Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ
index 204bf8d..f18a7e2 100644 (file)
@@ -1 +1 @@
-20210728172208 
+20210720163506 
diff --git a/temp/bbb.dtd.pdf b/temp/bbb.dtd.pdf
new file mode 100644 (file)
index 0000000..93f7302
Binary files /dev/null and b/temp/bbb.dtd.pdf differ
diff --git a/template.html b/template.html
new file mode 100644 (file)
index 0000000..d132460
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>SwingJS test _NAME_</title><meta charset="utf-8" />
+<script src="swingjs/swingjs2.js"></script>
+<script>
+if (!self.SwingJS)alert('swingjs2.js was not found. It needs to be in swingjs folder in the same directory as ' + document.location.href)
+Info = {
+  code: _CODE_,
+  main: _MAIN_,
+  core: "NONE",
+       width: 850,
+       height: 550,
+  readyFunction: null,
+       serverURL: 'https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php',
+       j2sPath: 'swingjs/j2s',
+       console:'sysoutdiv',
+       allowjavascript: true
+}
+</script>
+</head>
+<body>
+<script>
+SwingJS.getApplet('testApplet', Info)
+getClassList = function(){J2S._saveFile('_j2sclasslist.txt', Clazz.ClassFilesLoaded.sort().join('\n'))}
+</script>
+<div style="position:absolute;left:900px;top:30px;width:600px;height:300px;">
+<div id="sysoutdiv" contentEditable="true" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
+This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a>  <a href='javascript:J2S.getProfile()'>start/stop profiling</a><br>see <a href=___j2sflags.htm>___j2sflags.htm</a> for SwingJS URL command-line options<br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+</div>
+</body>
+</html>
index d714970..1514bf0 100644 (file)
@@ -25,13 +25,21 @@ import static org.testng.AssertJUnit.assertNull;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.Profile;
 import jalview.datamodel.ProfileI;
+import jalview.datamodel.Profiles;
 import jalview.datamodel.ProfilesI;
 import jalview.datamodel.ResidueCount;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.HMMFile;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
 
 import java.util.Hashtable;
 
@@ -40,6 +48,7 @@ import org.testng.annotations.Test;
 
 public class AAFrequencyTest
 {
+  HiddenMarkovModel hmm;
 
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
@@ -48,6 +57,17 @@ public class AAFrequencyTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
+  @BeforeClass(alwaysRun = true)
+  public void setUp() throws IOException, MalformedURLException
+  {
+    /*
+     * load a dna (ACGT) HMM file to a HiddenMarkovModel
+     */
+    HMMFile hmmFile = new HMMFile(new FileParse(
+            "test/jalview/io/test_MADE1_hmm.txt", DataSourceType.FILE));
+    hmm = hmmFile.getHMM();
+  }
+
   @Test(groups = { "Functional" })
   public void testCalculate_noProfile()
   {
@@ -412,4 +432,93 @@ public class AAFrequencyTest
         encoded2, 58, encoded1, 25, encoded3, 7 };
     org.testng.Assert.assertEquals(extracted, expected);
   }
+
+  @Test(groups = { "Functional" })
+  public void testExtractHMMProfile()
+          throws MalformedURLException, IOException
+  {
+    int[] expected = { 0, 4, 100, 'T', 71, 'C', 12, 'G', 9, 'A', 9 };
+    int[] actual = AAFrequency.extractHMMProfile(hmm, 17, false, false);
+    for (int i = 0; i < actual.length; i++)
+    {
+      if (i == 2)
+      {
+        assertEquals(actual[i], expected[i]);
+      }
+      else
+      {
+        assertEquals(actual[i], expected[i]);
+      }
+    }
+
+    int[] expected2 = { 0, 4, 100, 'A', 85, 'C', 0, 'G', 0, 'T', 0 };
+    int[] actual2 = AAFrequency.extractHMMProfile(hmm, 2, true, false);
+    for (int i = 0; i < actual2.length; i++)
+    {
+      if (i == 2)
+      {
+        assertEquals(actual[i], expected[i]);
+      }
+      else
+      {
+        assertEquals(actual[i], expected[i]);
+      }
+    }
+
+    assertNull(AAFrequency.extractHMMProfile(null, 98978867, true, false));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testGetAnalogueCount()
+  {
+    /*
+     * 'T' in column 0 has emission probability 0.7859, scales to 7859
+     */
+    int count = AAFrequency.getAnalogueCount(hmm, 0, 'T', false, false);
+    assertEquals(7859, count);
+
+    /*
+     * same with 'use info height': value is multiplied by log ratio
+     * log(value / background) / log(2) = log(0.7859/0.25)/0.693
+     * = log(3.1)/0.693 = 1.145/0.693 = 1.66
+     * so value becomes 1.2987 and scales up to 12987
+     */
+    count = AAFrequency.getAnalogueCount(hmm, 0, 'T', false, true);
+    assertEquals(12987, count);
+
+    /*
+     * 'G' in column 20 has emission probability 0.75457, scales to 7546
+     */
+    count = AAFrequency.getAnalogueCount(hmm, 20, 'G', false, false);
+    assertEquals(7546, count);
+
+    /*
+     * 'G' in column 1077 has emission probability 0.0533, here
+     * ignored (set to 0) since below background of 0.25
+     */
+    count = AAFrequency.getAnalogueCount(hmm, 1077, 'G', true, false);
+    assertEquals(0, count);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testCompleteInformation()
+  {
+    ProfileI prof1 = new Profile(1, 0, 100, "A");
+    ProfileI prof2 = new Profile(1, 0, 100, "-");
+
+    ProfilesI profs = new Profiles(new ProfileI[] { prof1, prof2 });
+    Annotation ann1 = new Annotation(6.5f);
+    Annotation ann2 = new Annotation(0f);
+    Annotation[] annots = new Annotation[] { ann1, ann2 };
+    SequenceI seq = new Sequence("", "AA", 0, 0);
+    seq.setHMM(hmm);
+    AlignmentAnnotation annot = new AlignmentAnnotation("", "", annots);
+    annot.setSequenceRef(seq);
+    AAFrequency.completeInformation(annot, profs, 0, 1);
+    float ic = annot.annotations[0].value;
+    assertEquals(0.91532f, ic, 0.0001f);
+    ic = annot.annotations[1].value;
+    assertEquals(0f, ic, 0.0001f);
+    int i = 0;
+  }
 }
index e6bdbd4..f7d7e2b 100644 (file)
@@ -22,20 +22,17 @@ package jalview.analysis;
 
 import java.util.Locale;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.gui.JvOptionPane;
-import jalview.io.FastaFile;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.PrintStream;
 import java.util.Arrays;
 import java.util.Random;
 
-import org.testng.annotations.BeforeClass;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.io.FastaFile;
 
 /**
  * Generates, and outputs in Fasta format, a random peptide or nucleotide
index b1f0edc..d99f20a 100644 (file)
@@ -51,7 +51,7 @@ public class AlignmentSorterTest
     /*
      * sort with no score features does nothing
      */
-    PA.setValue(AlignmentSorter.class, "sortByFeatureCriteria", null);
+    PA.setValue(AlignmentSorter.getInstance(), "sortByFeatureCriteria", null);
 
     AlignmentSorter.sortByFeature(null, null, 0, al.getWidth(), al,
             AlignmentSorter.FEATURE_SCORE);
@@ -85,7 +85,7 @@ public class AlignmentSorterTest
      * sort by ascending score, no filter on feature type or group
      * NB sort order for the same feature set (none) gets toggled, so descending
      */
-    PA.setValue(AlignmentSorter.class, "sortByFeatureAscending", true);
+    PA.setValue(AlignmentSorter.getInstance(), "sortByFeatureAscending", true);
     AlignmentSorter.sortByFeature(null, null, 0, al.getWidth(), al,
             AlignmentSorter.FEATURE_SCORE);
     assertSame(al.getSequenceAt(3), seq3); // -0.5
index e231e7f..97ef9a0 100644 (file)
@@ -27,7 +27,16 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.analysis.AlignmentUtils.DnaVariant;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -41,7 +50,6 @@ import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.SequenceFeatures;
 import jalview.gui.JvOptionPane;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
@@ -51,18 +59,6 @@ import jalview.io.FormatAdapter;
 import jalview.io.gff.SequenceOntologyI;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
-import jalview.ws.params.InvalidArgumentException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
 
 public class AlignmentUtilsTests
 {
index 2970e3d..c5f956b 100644 (file)
@@ -28,6 +28,14 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.datamodel.Alignment;
@@ -41,16 +49,6 @@ import jalview.gui.JvOptionPane;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.ws.SequenceFetcher;
-import jalview.ws.SequenceFetcherFactory;
-import jalview.ws.params.InvalidArgumentException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
 
 public class CrossRefTest
 {
@@ -455,7 +453,7 @@ public class CrossRefTest
         return new SequenceI[] { pep1, pep2 };
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find UNIPROT xrefs for nucleotide sequence
@@ -471,7 +469,7 @@ public class CrossRefTest
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceFetcherFactory.setSequenceFetcher(null);
+    SequenceFetcher.setMockFetcher(null);
   }
 
   /**
@@ -535,7 +533,7 @@ public class CrossRefTest
         return new SequenceI[] { pep1, pep2 };
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find UNIPROT xrefs for gene and transcripts
@@ -698,7 +696,7 @@ public class CrossRefTest
         }
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find EMBL xrefs for Uniprot seqs and verify that
index 7e3bd86..319c998 100644 (file)
@@ -68,7 +68,7 @@ public class FinderTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("PAD_GAPS",
+    Cache.setPropertyNoSave("PAD_GAPS",
             Boolean.FALSE.toString());
 
     //@formatter:off
index 0587432..71e5bfd 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 
 import java.util.Hashtable;
+import java.util.Map;
 
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
@@ -63,7 +64,7 @@ public class SeqsetUtilsTest
     SequenceFeature sf1 = new SequenceFeature("f1", "foo", 2, 3, "far");
     SequenceFeature sf2 = new SequenceFeature("f2", "foo", 2, 3, "far");
     ds.getSequenceAt(0).addSequenceFeature(sf1);
-    Hashtable unq = SeqsetUtils.uniquify(sqset, true);
+    Map<String, SeqsetUtils.SequenceInfo> unq = SeqsetUtils.uniquify(sqset, true);
     SequenceI[] sqset2 = new SequenceI[] {
         new Sequence(sqset[0].getName(), sqset[0].getSequenceAsString()),
         new Sequence(sqset[1].getName(), sqset[1].getSequenceAsString()) };
index 92f1542..1300fa2 100644 (file)
@@ -23,6 +23,7 @@ package jalview.analysis.scoremodels;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
+import jalview.api.AlignViewportI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.api.analysis.SimilarityParamsI;
 import jalview.datamodel.Alignment;
@@ -32,7 +33,6 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
@@ -283,7 +283,7 @@ public class FeatureDistanceModelTest
   public void testFindDistances_withParams()
   {
     AlignFrame af = setupAlignmentView();
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
     AlignmentView view = viewport.getAlignmentView(false);
 
     ScoreModelI sm = new FeatureDistanceModel();
index 7e775d8..b9959dd 100644 (file)
  */
 package jalview.bin;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.IdentifyFile;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
@@ -41,7 +50,6 @@ import org.testng.annotations.Test;
 import io.github.classgraph.ClassGraph;
 import io.github.classgraph.ModuleRef;
 import io.github.classgraph.ScanResult;
-import jalview.gui.JvOptionPane;
 
 public class CommandLineOperations
 {
@@ -309,11 +317,11 @@ public class CommandLineOperations
 
   @Test(
     groups =
-    { "Functional" },
+    { "Functional", "testben" },
     dataProvider = "headlessModeOutputOperationsData")
   public void testHeadlessModeOutputOperations(String harg, String type,
           String fileName, boolean withAWT, int expectedMinFileSize,
-          int timeout)
+          int timeout, String fileFormatType)
   {
     String cmd = harg + type + " " + fileName;
     // System.out.println(">>>>>>>>>>>>>>>> Command : " + cmd);
@@ -325,6 +333,25 @@ public class CommandLineOperations
     assertTrue(file.exists(), msg);
     FileAssert.assertFile(file, msg);
     FileAssert.assertMinLength(file, expectedMinFileSize);
+    if (fileFormatType!=null && fileFormatType.length()>0)
+    {
+      FileFormatI format = FileFormats.getInstance()
+              .forName(fileFormatType);
+      if (format!=null)
+      {
+        try
+        {
+          FileFormatI exportedType = new IdentifyFile()
+                  .identify(file.getAbsolutePath(), DataSourceType.FILE);
+          assertEquals(exportedType, format,
+                  "Exported file type was wrong");
+        } catch (FileFormatException e)
+        {
+          Assert.fail("Couldn't identify file " + file
+                  + " as an alignment format", e);
+        }
+      }
+    }
     if (worker != null && worker.exit == null)
     {
       worker.interrupt();
@@ -377,51 +404,51 @@ public class CommandLineOperations
     String workingDir = "test/jalview/bin/";
     return new Object[][] { { "nodisplay -open examples/uniref50.fa",
         " -eps", workingDir + "test_uniref50_out.eps", true,
-        MINFILESIZE_BIG, TEST_TIMEOUT },
+        MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "nodisplay -open examples/uniref50.fa", " -eps",
-            workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.eps", false,
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "nogui -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "nogui -open examples/uniref50.fa", " -eps",
-            workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.eps", false,
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -svg",
-            workingDir + "test_uniref50_out.svg", false, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.svg", false,
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -png",
             workingDir + "test_uniref50_out.png", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -html",
-            workingDir + "test_uniref50_out.html", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.html", true,
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -fasta",
             workingDir + "test_uniref50_out.mfa", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Fasta.toString() },
         { "headless -open examples/uniref50.fa", " -clustal",
             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Clustal.toString() },
         { "headless -open examples/uniref50.fa", " -msf",
             workingDir + "test_uniref50_out.msf", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.MSF.toString() },
         { "headless -open examples/uniref50.fa", " -pileup",
             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Pileup.toString() },
         { "headless -open examples/uniref50.fa", " -pir",
             workingDir + "test_uniref50_out.pir", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.PIR.toString() },
         { "headless -open examples/uniref50.fa", " -pfam",
             workingDir + "test_uniref50_out.pfam", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Pfam.toString() },
         { "headless -open examples/uniref50.fa", " -blc",
             workingDir + "test_uniref50_out.blc", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.BLC.toString() },
         { "headless -open examples/uniref50.fa", " -jalview",
             workingDir + "test_uniref50_out.jvp", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT }, };
+            TEST_TIMEOUT, FileFormat.Jalview.toString() }, };
   }
 }
index 2f46eca..f9e99d0 100644 (file)
@@ -49,6 +49,11 @@ public class AlignmentAnnotationTests
     createAnnotation(sq);
     AlignmentAnnotation alc, alo = sq.getAnnotation()[0];
     alc = new AlignmentAnnotation(alo);
+
+    // TODO: this only tests string equals (which is unreliable), should use
+    // refactored tests from StockholmFileTest
+    Assert.assertEquals(alc.toString(), alo.toString());
+
     for (String key : alo.getProperties())
     {
       assertEquals("Property mismatch", alo.getProperty(key),
index d00abb1..1f3dedd 100644 (file)
@@ -1367,7 +1367,35 @@ public class AlignmentTest
             "Temperature Factor", null, false, seq, null);
     assertNotNull(ala);
     assertEquals(seq, ala.sequenceRef);
-    assertEquals("", ala.calcId);
+    assertEquals("", ala.getCalcId());
+  }
+
+  @Test(groups = {"Functional"})
+  public void testUpdateFromOrAddAnnotation()
+  {
+    SequenceI seq = new Sequence("seq1", "FRMLPSRT-A--L-");
+    AlignmentI alignment = new Alignment(new SequenceI[] { seq });
+
+    AlignmentAnnotation ala = alignment.findOrCreateAnnotation(
+            "Temperature Factor", null, false, seq, null);
+
+    assertNotNull(ala);
+    assertEquals(seq, ala.sequenceRef);
+    assertEquals("", ala.getCalcId());
+
+    // Assuming findOrCreateForNullCalcId passed then this should work
+
+    assertTrue(ala == alignment.updateFromOrCopyAnnotation(ala));
+    AlignmentAnnotation updatedAla = new AlignmentAnnotation(ala);
+    updatedAla.description = "updated Description";
+    Assert.assertTrue(
+            ala == alignment.updateFromOrCopyAnnotation(updatedAla));
+    Assert.assertEquals(ala.toString(), updatedAla.toString());
+    updatedAla.calcId = "newCalcId";
+    AlignmentAnnotation newUpdatedAla = alignment
+            .updateFromOrCopyAnnotation(updatedAla);
+    Assert.assertTrue(updatedAla != newUpdatedAla);
+    Assert.assertEquals(updatedAla.toString(), newUpdatedAla.toString());
   }
 
   @Test(groups = "Functional")
index b201c7e..9be344c 100644 (file)
@@ -23,10 +23,10 @@ package jalview.datamodel;
 import static org.testng.Assert.assertEquals;
 
 import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.viewmodel.AlignmentViewport;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -60,7 +60,7 @@ public class AlignmentViewTest
   {
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             ">s1\n0123456789\n", DataSourceType.PASTE);
-    AlignViewport av = af.getViewport();
+    AlignmentViewport av = af.getViewport();
     AlignmentView view = av.getAlignmentView(true);
 
     /*
diff --git a/test/jalview/datamodel/HMMNodeTest.java b/test/jalview/datamodel/HMMNodeTest.java
new file mode 100644 (file)
index 0000000..f2aef06
--- /dev/null
@@ -0,0 +1,42 @@
+package jalview.datamodel;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.HMMFile;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class HMMNodeTest
+{
+  private HiddenMarkovModel hmm;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUp() throws MalformedURLException, IOException
+  {
+    /*
+     * load hmm model of a Kinase domain to a HiddenMarkovModel
+     */
+    HMMFile file = new HMMFile(new FileParse(
+            "test/jalview/io/test_PKinase_hmm.txt", DataSourceType.FILE));
+    hmm = file.getHMM();
+  }
+
+  @Test(groups="Functional")
+  public void testGetMaxMatchEmissionIdex()
+  {
+    assertEquals(hmm.getAlphabetType(), "amino");
+    String symbols = hmm.getSymbols();
+
+    assertEquals(hmm.getNode(1).getMaxMatchEmissionIndex(),
+            symbols.indexOf('Y'));
+
+    assertEquals(hmm.getNode(17).getMaxMatchEmissionIndex(),
+            symbols.indexOf('K'));
+  }
+}
diff --git a/test/jalview/datamodel/HiddenMarkovModelTest.java b/test/jalview/datamodel/HiddenMarkovModelTest.java
new file mode 100644 (file)
index 0000000..65241b4
--- /dev/null
@@ -0,0 +1,162 @@
+package jalview.datamodel;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.HMMFile;
+import jalview.schemes.ResidueProperties;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class HiddenMarkovModelTest {
+
+  HiddenMarkovModel hmm;
+
+  HiddenMarkovModel alignmentHmm;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUp() throws MalformedURLException, IOException
+  {
+    /*
+     * load hmm model of a Kinase domain to a HiddenMarkovModel
+     */
+    HMMFile file = new HMMFile(new FileParse(
+            "test/jalview/io/test_PKinase_hmm.txt", DataSourceType.FILE));
+    hmm = file.getHMM();
+
+    // used to check if consensus sequence is automatically aligned with alignment
+    HMMFile alignmentTest = new HMMFile(
+            new FileParse("test/jalview/io/HMMAlignmentTestHMM.hmm",
+                    DataSourceType.FILE));
+    alignmentHmm = alignmentTest.getHMM();
+  }
+
+  @Test(groups = "Functional")
+  public void testGetMatchEmissionProbabilities()
+          throws MalformedURLException, IOException
+  {
+    /*
+     * raw value in file is 3.67403
+     * saved as probability e^-X = 0.05259 
+     */
+    double mep = hmm.getMatchEmissionProbability(0, 'R');
+    assertEquals(mep, 0.02537400637, 0.0001d);
+    assertEquals(mep, Math.pow(Math.E, -3.67403), 0.0001d);
+
+    mep = hmm.getMatchEmissionProbability(19, 'W');
+    assertEquals(mep, 0.00588228492, 0.0001d);
+    assertEquals(mep, Math.pow(Math.E, -5.13581), 0.0001d);
+
+    // column 160 is a gapped region of the model
+    mep = hmm.getMatchEmissionProbability(160, 'G');
+    assertEquals(mep, 0D, 0.0001d);
+
+    mep = hmm.getMatchEmissionProbability(475, 'A');
+    assertEquals(mep, 0.04995163708, 0.0001d);
+    assertEquals(mep, Math.pow(Math.E, -2.99670), 0.0001d);
+  }
+  
+  @Test(groups = "Functional")
+  public void testGetInsertEmissionProbabilities()
+  {
+    double iep = hmm.getInsertEmissionProbability(2, 'A');
+    assertEquals(iep, Math.pow(Math.E, -2.68618), 0.0001d);
+    // symbol is not case-sensitive
+    assertEquals(iep, hmm.getInsertEmissionProbability(2, 'a'));
+
+    iep = hmm.getInsertEmissionProbability(5, 'T');
+    assertEquals(iep, Math.pow(Math.E, -2.77519), 0.0001d);
+
+    // column 161 is gapped in the hmm
+    iep = hmm.getInsertEmissionProbability(161, 'K');
+    assertEquals(iep, 0D, 0.0001d);
+
+    iep = hmm.getInsertEmissionProbability(472, 'L');
+    assertEquals(iep, Math.pow(Math.E, -2.69355), 0.0001d);
+  }
+
+  @Test(groups = "Functional")
+  public void testGetStateTransitionProbabilities()
+  {
+    // * in model file treated as negative infinity
+    double stp = hmm.getStateTransitionProbability(475,
+            HiddenMarkovModel.MATCHTODELETE);
+    assertEquals(stp, Double.NEGATIVE_INFINITY);
+
+    // file value is 5.01631, saved as e^-5.01631
+    stp = hmm.getStateTransitionProbability(8,
+            HiddenMarkovModel.MATCHTOINSERT);
+    assertEquals(stp, Math.pow(Math.E, -5.01631), 0.0001D);
+
+    stp = hmm.getStateTransitionProbability(36,
+            HiddenMarkovModel.MATCHTODELETE);
+    assertEquals(stp, Math.pow(Math.E, -5.73865), 0.0001D);
+
+    stp = hmm.getStateTransitionProbability(22,
+            HiddenMarkovModel.INSERTTOMATCH);
+    assertEquals(stp, Math.pow(Math.E, -0.61958), 0.0001D);
+
+    stp = hmm.getStateTransitionProbability(80,
+            HiddenMarkovModel.INSERTTOINSERT);
+    assertEquals(stp, Math.pow(Math.E, -0.77255), 0.0001D);
+
+    stp = hmm.getStateTransitionProbability(475,
+            HiddenMarkovModel.DELETETOMATCH);
+    assertEquals(stp, 1D, 0.0001D);
+
+    stp = hmm.getStateTransitionProbability(218,
+            HiddenMarkovModel.DELETETODELETE);
+    assertEquals(stp, Math.pow(Math.E, -0.95510), 0.0001D);
+  }
+  
+  @Test(groups = "Functional")
+  public void testGetConsensusSequence()
+  {
+    SequenceI seq = hmm.getConsensusSequence();
+    String subStr = seq.getSequenceAsString().substring(0, 10);
+    assertEquals(subStr, "yelleklGsG");
+    subStr = seq.getSequenceAsString().substring(150, 161);
+    assertEquals(subStr, "-dllk------");
+
+    // test to see if consensus sequence maps to alignment correctly
+    // see HMMAlignmentTest.sto for corresponding alignment file
+    SequenceI seq2 = alignmentHmm.getConsensusSequence();
+    assertEquals(seq2.getCharAt(0), '-');
+    assertEquals(seq2.getCharAt(7), '-');
+    assertEquals(seq2.getCharAt(8), 's');
+
+  }
+
+  /**
+   * A rather pointless test that reproduces the code implemented and asserts
+   * the result is the same...
+   */
+  @Test(groups = "Functional")
+  public void testGetInformationContent()
+  {
+    /*
+     * information measure is sum over all symbols of 
+     * emissionProb * log(emissionProb / background) / log(2)
+     */
+    Map<Character, Float> uniprotFreqs = ResidueProperties.backgroundFrequencies
+            .get("amino");
+    int col = 4;
+    float expected = 0f;
+    for (char aa : hmm.getSymbols().toCharArray())
+    {
+      double mep = hmm.getMatchEmissionProbability(col, aa);
+      float background = uniprotFreqs.get(aa);
+      expected += mep * Math.log(mep / background);
+    }
+    expected /= Math.log(2D);
+
+    float actual = hmm.getInformationContent(col);
+    assertEquals(actual, expected, 0.0001d);
+  }
+}
index 11b993d..efd71e6 100644 (file)
@@ -31,6 +31,7 @@ import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
 import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.util.List;
 
@@ -487,7 +488,7 @@ public class HiddenSequencesTest
      * represent seqs 2-4 with seq3
      * this hides seq2 and seq4 but not seq3
      */
-    AlignViewport av = new AlignViewport(al);
+    AlignmentViewport av = new AlignViewport(al);
     SequenceGroup sg = new SequenceGroup();
     sg.addSequence(seqs[1], false);
     sg.addSequence(seqs[2], false);
index 696ed76..fc6c99a 100644 (file)
@@ -29,6 +29,8 @@ import static org.testng.Assert.assertTrue;
 import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.gui.JvOptionPane;
 
+import java.util.Arrays;
+
 import org.junit.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -71,6 +73,7 @@ public class ResidueCountTest
     assertEquals(rc.getCount('N'), 1);
     assertEquals(rc.getCount('?'), 0);
     assertEquals(rc.getCount('-'), 0);
+    assertEquals(rc.getTotalResidueCount(), 11);
 
     assertFalse(rc.isCountingInts());
     assertFalse(rc.isUsingOtherData());
@@ -92,6 +95,7 @@ public class ResidueCountTest
     assertEquals(rc.getCount(' '), 4);
     assertEquals(rc.getCount('-'), 4);
     assertEquals(rc.getCount('.'), 4);
+    assertEquals(rc.getTotalResidueCount(), 0);
     assertFalse(rc.isUsingOtherData());
     assertFalse(rc.isCountingInts());
 
@@ -172,6 +176,7 @@ public class ResidueCountTest
     assertEquals(rc.getCount('m'), 13);
     assertEquals(rc.getCount('G'), 0);
     assertEquals(rc.getCount('-'), 0);
+    assertEquals(rc.getTotalResidueCount(), 27);
 
     assertFalse(rc.isCountingInts());
     assertFalse(rc.isUsingOtherData());
@@ -453,4 +458,23 @@ public class ResidueCountTest
     assertEquals(rc.getCount('?'), 6);
     assertEquals(rc.getCount('!'), 7);
   }
+
+  @Test(groups = "Functional")
+  public void testConstructor_forSequences()
+  {
+    SequenceI seq1 = new Sequence("seq1", "abcde--. FCD");
+    SequenceI seq2 = new Sequence("seq2", "ab.kKqBd-.");
+    ResidueCount rc = new ResidueCount(Arrays.asList(seq1, seq2));
+
+    assertEquals(rc.getGapCount(), 7);
+    assertEquals(rc.getTotalResidueCount(), 15); // excludes gaps
+    assertEquals(rc.getCount('a'), 2);
+    assertEquals(rc.getCount('A'), 2);
+    assertEquals(rc.getCount('B'), 3);
+    assertEquals(rc.getCount('c'), 2);
+    assertEquals(rc.getCount('D'), 3);
+    assertEquals(rc.getCount('f'), 1);
+    assertEquals(rc.getCount('K'), 2);
+    assertEquals(rc.getCount('Q'), 1);
+  }
 }
index 622ebb9..ff11756 100644 (file)
@@ -264,7 +264,7 @@ public class SequenceGroupTest
     sg.setDisplayBoxes(false);
     sg.setDisplayText(false);
     sg.setColourText(true);
-    sg.isDefined = true;
+    PA.setValue(sg, "isDefined", true);
     sg.setShowNonconserved(true);
     sg.setOutlineColour(Color.red);
     sg.setIdColour(Color.blue);
@@ -329,7 +329,7 @@ public class SequenceGroupTest
   {
     SequenceI s1 = new Sequence("abcde", "fg");
     SequenceI s2 = new Sequence("foo", "bar");
-    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    List<SequenceI> seqs = new ArrayList<>();
     seqs.add(s1);
     seqs.add(s2);
     SequenceGroup sg = new SequenceGroup(seqs);
index 6e18324..7133455 100644 (file)
@@ -30,14 +30,6 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.analysis.AlignmentGenerator;
-import jalview.commands.EditCommand;
-import jalview.commands.EditCommand.Action;
-import jalview.datamodel.PDBEntry.Type;
-import jalview.gui.JvOptionPane;
-import jalview.util.MapList;
-import jalview.ws.params.InvalidArgumentException;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -51,6 +43,13 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.analysis.AlignmentGenerator;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.datamodel.PDBEntry.Type;
+import jalview.gui.JvOptionPane;
+import jalview.util.MapList;
+
 import junit.extensions.PA;
 
 public class SequenceTest
@@ -351,8 +350,6 @@ public class SequenceTest
      * invalid inputs
      */
     assertNull(sq.findPositions(6, 5));
-    assertNull(sq.findPositions(0, 5));
-    assertNull(sq.findPositions(-1, 5));
 
     /*
      * all gapped ranges
@@ -395,6 +392,16 @@ public class SequenceTest
     assertEquals(new Range(11, 12), sq.findPositions(5, 10)); // DE
     assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot
     assertEquals(new Range(8, 13), sq.findPositions(1, 99));
+
+    /**
+     * now try on a sequence with no gaps
+     */
+    sq.createDatasetSequence();
+    assertEquals(new Range(8, 13),
+            sq.getDatasetSequence().findPositions(1, 99));
+    assertEquals(new Range(8, 13),
+            sq.getDatasetSequence().findPositions(0, 99));
+
   }
 
   /**
index a172a8a..db02528 100644 (file)
@@ -149,10 +149,13 @@ public class FeatureAttributesTest
             "group");
     sf.setValue("kd", "-1");
     sf.setValue("domain", "Metal");
+    sf.setValue("foo", " ");
     sf.setValue("phase", "1");
-    sf.setValue("phase", "reverse");
+    sf.setValue("phase", "1reverse");
     assertEquals(fa.getDatatype("Pfam", "kd"), Datatype.Number);
     assertEquals(fa.getDatatype("Pfam", "domain"), Datatype.Character);
     assertEquals(fa.getDatatype("Pfam", "phase"), Datatype.Mixed);
+    assertNull(fa.getDatatype("Pfam", "unobserved"));
+    assertNull(fa.getDatatype("Pfam", "foo"));// empty values are ignored
   }
 }
index d2559ae..fb80a35 100644 (file)
@@ -56,13 +56,13 @@ public class EnsemblCdnaTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index b0a6a2d..4b12711 100644 (file)
@@ -53,13 +53,13 @@ public class EnsemblCdsTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index 4a6036f..b0d447a 100644 (file)
@@ -58,13 +58,13 @@ public class EnsemblGeneTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index 0652dd6..5515c05 100644 (file)
@@ -52,13 +52,13 @@ public class EnsemblGenomeTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index 61d44d0..12a8254 100644 (file)
@@ -127,13 +127,13 @@ public class EnsemblSeqProxyTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   @DataProvider(name = "ens_seqs")
index 9684603..88e5833 100644 (file)
@@ -77,7 +77,7 @@ public class JmolCommandsTest
     SequenceRenderer sr = new SequenceRenderer(af.getViewport());
     SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
     String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
 
     /*
      * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
@@ -94,6 +94,7 @@ public class JmolCommandsTest
             "B", map, null);
     ssm.addStructureMapping(sm2);
 
+    // TODO - comments in testee suggest this tests an obsolete method!
     String[] commands = testee.colourBySequence(ssm, files, seqs, sr,
             af.alignPanel);
     assertEquals(commands.length, 2);
index 73f7907..4047660 100644 (file)
@@ -98,11 +98,11 @@ public class JmolParserTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN",
+    Cache.setPropertyNoSave("ADD_TEMPFACT_ANN",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     StructureImportSettings.setDefaultStructureFileFormat("PDB");
     StructureImportSettings
index e451ed2..7ef0d16 100644 (file)
@@ -74,7 +74,7 @@ public class JmolViewerTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @Test(groups = { "Functional" })
index 7f9aa9b..9da1d07 100644 (file)
@@ -100,7 +100,7 @@ public class JalviewChimeraView
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @AfterMethod(alwaysRun = true)
index f02b00a..0f7c875 100644 (file)
@@ -23,9 +23,6 @@ package jalview.fts.service.pdb;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.gui.JvOptionPane;
-
-import javax.swing.JComboBox;
 import javax.swing.JInternalFrame;
 
 import org.testng.annotations.AfterMethod;
@@ -33,6 +30,7 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.gui.JvOptionPane;
 import junit.extensions.PA;
 
 public class PDBFTSPanelTest
index 2a768ff..8d8b43c 100644 (file)
@@ -35,6 +35,7 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.api.AlignViewportI;
 import jalview.api.FeatureColourI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
@@ -56,6 +57,7 @@ import jalview.schemes.JalviewColourScheme;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 
 public class AlignFrameTest
 {
@@ -75,7 +77,7 @@ public class AlignFrameTest
   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   /**
@@ -86,7 +88,7 @@ public class AlignFrameTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
@@ -209,7 +211,7 @@ public class AlignFrameTest
   @Test(groups = "Functional")
   public void testChangeColour_background_groupsAndThresholds()
   {
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     AlignmentI al = av.getAlignment();
 
     /*
@@ -343,7 +345,7 @@ public class AlignFrameTest
   @Test(groups = "Functional")
   public void testColourThresholdActions()
   {
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     AlignmentI al = av.getAlignment();
 
     /*
@@ -516,7 +518,7 @@ public class AlignFrameTest
   @Test(groups = "Functional")
   public void testNewView_colourThresholds()
   {
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     AlignmentI al = av.getAlignment();
 
     /*
@@ -581,7 +583,7 @@ public class AlignFrameTest
      */
     af.newView_actionPerformed(null);
     assertEquals(af.alignPanel.getViewName(), "View 1");
-    AlignViewport av2 = af.getViewport();
+    AlignmentViewport av2 = af.getViewport();
     assertNotSame(av, av2);
     assertSame(av2, af.alignPanel.av);
     rs = av2.getResidueShading();
index b3c6b2a..db41f3e 100644 (file)
@@ -27,6 +27,7 @@ import static org.testng.AssertJUnit.assertNotSame;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.api.AlignViewportI;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -54,6 +55,7 @@ import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MapList;
+import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 
 public class AlignViewportTest
@@ -68,7 +70,7 @@ public class AlignViewportTest
 
   AlignmentI al;
 
-  AlignViewport testee;
+  AlignmentViewport testee;
 
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
@@ -81,7 +83,7 @@ public class AlignViewportTest
      * remove any sequence mappings left lying around by other tests
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
   }
 
@@ -107,18 +109,16 @@ public class AlignViewportTest
     /*
      * alignment with reference to mappings
      */
-    AlignFrame af1 = new FileLoader()
-            .LoadFileWaitTillLoaded(">Seq1\nCAGT\n", DataSourceType.PASTE);
+    AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nCAGT\n", DataSourceType.PASTE);
 
     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
-    acf1.addMap(s1, s1,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 1, 4 }, 1, 1));
+    acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 },
+            1, 1));
     AlignedCodonFrame acf2 = new AlignedCodonFrame();
-    acf2.addMap(s1, s1,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 4, 1 }, 1, 1));
+    acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 },
+            1, 1));
 
     List<AlignedCodonFrame> mappings = new ArrayList<>();
     mappings.add(acf1);
@@ -131,7 +131,7 @@ public class AlignViewportTest
      * mappings
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     List<AlignedCodonFrame> sequenceMappings = ssm.getSequenceMappings();
     assertEquals(2, sequenceMappings.size());
     assertTrue(sequenceMappings.contains(acf1));
@@ -153,33 +153,30 @@ public class AlignViewportTest
   @Test(groups = { "Functional" })
   public void testDeregisterMapping_withNoReference()
   {
-    Desktop d = Desktop.instance;
+    Desktop d = Desktop.getInstance();
     assertNotNull(d);
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
 
-    AlignFrame af1 = new FileLoader()
-            .LoadFileWaitTillLoaded(">Seq1\nRSVQ\n", DataSourceType.PASTE);
-    AlignFrame af2 = new FileLoader()
-            .LoadFileWaitTillLoaded(">Seq2\nDGEL\n", DataSourceType.PASTE);
+    AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nRSVQ\n", DataSourceType.PASTE);
+    AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq2\nDGEL\n", DataSourceType.PASTE);
     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
     // need to be distinct
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
-    acf1.addMap(cs1, s1,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
+            new int[] { 1, 12 }, 1, 3));
     AlignedCodonFrame acf2 = new AlignedCodonFrame();
-    acf2.addMap(cs2, s2,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
+            new int[] { 1, 12 }, 1, 3));
     AlignedCodonFrame acf3 = new AlignedCodonFrame();
-    acf3.addMap(cs2, cs2,
-            new MapList(new int[]
-            { 1, 12 }, new int[] { 1, 12 }, 1, 1));
+    acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
+        12 }, 1, 1));
 
     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
     mappings1.add(acf1);
@@ -219,33 +216,30 @@ public class AlignViewportTest
   @Test(groups = { "Functional" })
   public void testDeregisterMapping_withReference()
   {
-    Desktop d = Desktop.instance;
+    Desktop d = Desktop.getInstance();
     assertNotNull(d);
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
 
-    AlignFrame af1 = new FileLoader()
-            .LoadFileWaitTillLoaded(">Seq1\nRSVQ\n", DataSourceType.PASTE);
-    AlignFrame af2 = new FileLoader()
-            .LoadFileWaitTillLoaded(">Seq2\nDGEL\n", DataSourceType.PASTE);
+    AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nRSVQ\n", DataSourceType.PASTE);
+    AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq2\nDGEL\n", DataSourceType.PASTE);
     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
     // need to be distinct
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
-    acf1.addMap(cs1, s1,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
+            new int[] { 1, 12 }, 1, 3));
     AlignedCodonFrame acf2 = new AlignedCodonFrame();
-    acf2.addMap(cs2, s2,
-            new MapList(new int[]
-            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
+            new int[] { 1, 12 }, 1, 3));
     AlignedCodonFrame acf3 = new AlignedCodonFrame();
-    acf3.addMap(cs2, cs2,
-            new MapList(new int[]
-            { 1, 12 }, new int[] { 1, 12 }, 1, 1));
+    acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
+        12 }, 1, 1));
 
     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
     mappings1.add(acf1);
@@ -286,18 +280,18 @@ public class AlignViewportTest
    * Test for JAL-1306 - conservation thread should run even when only Quality
    * (and not Conservation) is enabled in Preferences
    */
-  @Test(groups = { "Functional" }, timeOut = 2000)
+  @Test(groups = { "Functional" }, timeOut=2000)
   public void testUpdateConservation_qualityOnly()
   {
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
+    Cache.setPropertyNoSave("SHOW_QUALITY",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
+    Cache.setPropertyNoSave("SHOW_OCCUPANCY",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.FALSE.toString());
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
@@ -328,7 +322,9 @@ public class AlignViewportTest
   {
     synchronized (this)
     {
-      do
+      System.out.print("waiting...");
+      int n = 3;
+      while (--n >= 0 || viewport.getCalcManager().isWorking())
       {
         try
         {
@@ -336,7 +332,8 @@ public class AlignViewportTest
         } catch (InterruptedException e)
         {
         }
-      } while (viewport.getCalcManager().isWorking());
+      }
+           System.out.println("...done");
     }
   }
 
@@ -346,15 +343,16 @@ public class AlignViewportTest
     /*
      * test for JAL-2283: don't inadvertently turn on colour by conservation
      */
-    Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "None");
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("DEFAULT_COLOUR_PROT", "None");
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.TRUE.toString());
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     ColourSchemeI cs = new PIDColourScheme();
     AlignViewport viewport = af.getViewport();
     viewport.setGlobalColourScheme(cs);
-    assertFalse(viewport.getResidueShading().conservationApplied());
+    assertFalse(viewport.getResidueShading()
+            .conservationApplied());
 
     /*
      * JAL-3201 groups have their own ColourSchemeI instances
@@ -415,7 +413,7 @@ public class AlignViewportTest
   {
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     SequenceGroup sg1 = new SequenceGroup();
     SequenceGroup sg2 = new SequenceGroup();
     SequenceGroup sg3 = new SequenceGroup();
@@ -434,20 +432,17 @@ public class AlignViewportTest
     av.setSelectionGroup(sg3);
     assertTrue(sg3.isDefined()); // unchanged
   }
-
   /**
-   * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits
-   * occupancy row from viewport
+   * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport
    */
   @Test(groups = { "Functional" })
   public void testShowOrDontShowOccupancy()
   {
     // disable occupancy
-    jalview.bin.Cache.setProperty("SHOW_OCCUPANCY",
-            Boolean.FALSE.toString());
+    jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString());
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     Assert.assertNull(av.getAlignmentGapAnnotation(),
             "Preference did not disable occupancy row.");
     int c = 0;
@@ -457,12 +452,11 @@ public class AlignViewportTest
       c++;
     }
     Assert.assertEquals(c, 0, "Expected zero occupancy rows.");
-
+    
     // enable occupancy
-    jalview.bin.Cache.setProperty("SHOW_OCCUPANCY",
-            Boolean.TRUE.toString());
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
+    jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString());
+    af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
     av = af.getViewport();
     Assert.assertNotNull(av.getAlignmentGapAnnotation(),
             "Preference did not enable occupancy row.");
index a137ff6..3c749fd 100644 (file)
@@ -40,6 +40,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.util.Platform;
 import jalview.viewmodel.ViewportRanges;
 
 public class AlignmentPanelTest
@@ -49,15 +50,15 @@ public class AlignmentPanelTest
   @BeforeMethod(alwaysRun = true)
   public void setUp() throws InvocationTargetException, InterruptedException
   {
-    Jalview.main(
-            new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+    Jalview.main(new String[] { "-nonews", 
+        "-props", "test/jalview/testProps.jvprops",
+        "-jabaws", "none"});
 
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
-
+    
     /*
      * ensure the panel has been repainted and so ViewportRanges set
      */
@@ -98,22 +99,29 @@ public class AlignmentPanelTest
     af.alignPanel.setScrollValues(-1, 5);
 
     // setting -ve x value does not change residue
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres);
 
     af.alignPanel.setScrollValues(0, 5);
 
+    // no update necessary now
     // setting 0 as x value does not change residue
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres);
 
     af.alignPanel.setScrollValues(5, 5);
     // setting x value to 5 extends endRes by 5 residues
+    System.out.println(ranges);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + 5);
 
     // scroll to position after hidden columns sets endres to oldres (width) +
     // position
-    int scrollpos = 60;
+    int scrollpos = 53; // was 60, but this is too high to allow full scrolling
+                        // in Windows
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + scrollpos);
 
     // scroll to position within hidden columns, still sets endres to oldres +
@@ -124,6 +132,7 @@ public class AlignmentPanelTest
     af.getViewport().showAllHiddenColumns();
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + scrollpos);
 
     // scroll to position within <width> distance of the end of the alignment
@@ -131,6 +140,7 @@ public class AlignmentPanelTest
     scrollpos = 130;
     af.getViewport().showAllHiddenColumns();
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(),
             af.getViewport().getAlignment().getWidth() - 1);
 
@@ -140,6 +150,7 @@ public class AlignmentPanelTest
     // columns
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(),
             af.getViewport().getAlignment().getWidth() - 1 - 21); // 21 is the
                                                                   // number of
@@ -174,8 +185,8 @@ public class AlignmentPanelTest
   }
 
   /**
-   * Test the variant of calculateIdWidth that only recomputes the width if it
-   * is not already saved in the viewport (initial value is -1)
+   * Test the variant of calculateIdWidth that only recomputes the width if it is
+   * not already saved in the viewport (initial value is -1)
    */
   @Test(groups = "Functional")
   public void testCalculateIdWidth_noArgs()
@@ -198,8 +209,9 @@ public class AlignmentPanelTest
      * note 4 pixels padding are added to the longest sequence name width
      */
     av.setIdWidth(-1); // force recalculation
+    
     d = af.alignPanel.calculateIdWidth();
-    assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
+    assertEquals(d.width, Platform.isWin() ? 172 : 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
     assertEquals(d.height, 12);
     assertEquals(d.width, av.getIdWidth());
   }
@@ -221,7 +233,7 @@ public class AlignmentPanelTest
      * note 4 pixels 'padding' are added to the longest seq name/annotation label
      */
     Dimension d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
+    assertEquals(d.width, Platform.isWin() ? 172 : 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
     assertEquals(d.height, 12); // fixed value (not used?)
     assertEquals(av.getIdWidth(), 18); // not changed by this method
 
@@ -230,11 +242,14 @@ public class AlignmentPanelTest
      */
     SequenceI seq = af.viewport.getAlignment()
             .findSequenceMatch("Q93Z60_ARATH")[0];
-    seq.setName(seq.getName() + "MMMMM");
+    String orig = seq.getName();
+    seq.setName(orig + "MMMMM");
     d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, 211); // 4 + pixel width of "Q93Z60_ARATHMMMMM/1-118"
+    assertEquals(d.width, Platform.isWin() ? 219 : 211); // 4 + pixel width of "Q93Z60_ARATHMMMMM/1-118"
     assertEquals(d.height, 12);
     assertEquals(av.getIdWidth(), 18); // unchanged
+    // for next test:
+    seq.setName(orig);
 
     /*
      * make the longest annotation name even longer
@@ -245,18 +260,19 @@ public class AlignmentPanelTest
     FontMetrics fmfor = af.alignPanel
             .getFontMetrics(af.alignPanel.getAlabels().getFont());
     // Assumption ID_WIDTH_PADDING == 4
+    // AH! But with those added MMMM above, this was NOT the longest label!
     int expwidth = 4 + fmfor.stringWidth(aa.label);
     d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, expwidth); // 228 == ID_WIDTH_PADDING + pixel width of
-                                     // "THIS IS A VERY LONG LABEL INDEED"
+    assertEquals(d.width, expwidth); // 191 == ID_WIDTH_PADDING + pixel width of "THIS IS A VERY LONG LABEL INDEED"
     assertEquals(d.height, 12);
 
     /*
      * override with maxwidth
      * note the 4 pixels padding is added to this value
      */
-    d = af.alignPanel.calculateIdWidth(213);
-    assertEquals(d.width, 217);
+    // BH but we have to be under the max width
+    d = af.alignPanel.calculateIdWidth(180);
+    assertEquals(d.width, 184);
     assertEquals(d.height, 12);
   }
 
@@ -268,7 +284,7 @@ public class AlignmentPanelTest
      */
     int w = af.alignPanel.getVisibleIdWidth(true);
     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
-    assertEquals(w, 115);
+    assertEquals(w, Platform.isWin() ? 112 : 115);
 
     /*
      * width for offscreen rendering is the same
@@ -288,6 +304,6 @@ public class AlignmentPanelTest
      * preference for auto id width overrides fixed width
      */
     Cache.setProperty("FIGURE_AUTOIDWIDTH", Boolean.TRUE.toString());
-    assertEquals(115, af.alignPanel.getVisibleIdWidth(false));
+    assertEquals(Platform.isWin() ? 106 : 115, af.alignPanel.getVisibleIdWidth(false));
   }
 }
index f3c0dee..f0c4632 100644 (file)
@@ -88,14 +88,14 @@ public class AnnotationChooserTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // pin down annotation sort order for test
-    Cache.applicationProperties.setProperty(Preferences.SORT_ANNOTATIONS,
+    Cache.setPropertyNoSave(Preferences.SORT_ANNOTATIONS,
             SequenceAnnotationOrder.NONE.name());
     final String TRUE = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty(Preferences.SHOW_AUTOCALC_ABOVE,
-            TRUE);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", TRUE);
+    Cache.setPropertyNoSave(
+            Preferences.SHOW_AUTOCALC_ABOVE, TRUE);
+    Cache.setPropertyNoSave("SHOW_QUALITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", TRUE);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", TRUE);
 
     AlignmentI al = new FormatAdapter().readFile(TEST_DATA,
             DataSourceType.PASTE, FileFormat.Fasta);
@@ -108,9 +108,11 @@ public class AnnotationChooserTest
    * Add 4 annotations, 3 of them sequence-specific.
    * 
    * <PRE>
-   * ann1 - for sequence 0 - label 'IUPRED' ann2 - not sequence related - label
-   * 'Beauty' ann3 - for sequence 3 - label 'JMol' ann4 - for sequence 2 - label
-   * 'IUPRED' ann5 - for sequence 1 - label 'JMol'
+   * ann1 - for sequence 0 - label 'IUPRED' 
+   * ann2 - not sequence related - label 'Beauty' 
+   * ann3 - for sequence 3 - label 'JMol'
+   * ann4 - for sequence 2 - label 'IUPRED'
+   * ann5 - for sequence 1 - label 'JMol'
    */
   private void addAnnotations()
   {
@@ -182,8 +184,8 @@ public class AnnotationChooserTest
     assertTrue("Not enabled", cb1.isEnabled());
     assertTrue("Not enabled", cb2.isEnabled());
     assertTrue("Not enabled", cb3.isEnabled());
-    assertEquals("Option not selected", cb2,
-            cb2.getCheckboxGroup().getSelectedCheckbox());
+    assertEquals("Option not selected", cb2, cb2.getCheckboxGroup()
+            .getSelectedCheckbox());
 
     // check state variables match checkbox selection
     assertTrue(testee.isApplyToSelectedSequences());
@@ -233,16 +235,15 @@ public class AnnotationChooserTest
     assertTrue("Not enabled", cb1.isEnabled());
     assertFalse("Enabled", cb2.isEnabled());
     assertFalse("Enabled", cb3.isEnabled());
-    assertEquals("Not selected", cb1,
-            cb1.getCheckboxGroup().getSelectedCheckbox());
+    assertEquals("Not selected", cb1, cb1.getCheckboxGroup()
+            .getSelectedCheckbox());
 
     // check state variables match checkbox selection
     assertTrue(testee.isApplyToSelectedSequences());
     assertTrue(testee.isApplyToUnselectedSequences());
 
     assertEquals("Wrong text",
-            MessageManager.getString("label.all_sequences"),
-            cb1.getLabel());
+            MessageManager.getString("label.all_sequences"), cb1.getLabel());
     assertEquals("Wrong text",
             MessageManager.getString("label.selected_sequences"),
             cb2.getLabel());
@@ -276,8 +277,8 @@ public class AnnotationChooserTest
     assertTrue("Hide not enabled", cb2.isEnabled());
 
     // Hide (button 2) selected; note this may change to none (null)
-    assertEquals("Not selected", cb2,
-            cb2.getCheckboxGroup().getSelectedCheckbox());
+    assertEquals("Not selected", cb2, cb2.getCheckboxGroup()
+            .getSelectedCheckbox());
 
     assertTrue("Show is flagged", !testee.isShowSelected());
 
@@ -317,14 +318,14 @@ public class AnnotationChooserTest
     // selection group should make no difference to the result
     // as all annotation types for the alignment are considered
 
-    List<String> types = AnnotationChooser
-            .getAnnotationTypes(parentPanel.getAlignment(), true);
+    List<String> types = AnnotationChooser.getAnnotationTypes(
+            parentPanel.getAlignment(), true);
     assertEquals("Not two annotation types", 2, types.size());
     assertTrue("IUPRED missing", types.contains("IUPRED"));
     assertTrue("JMol missing", types.contains("JMol"));
 
-    types = AnnotationChooser.getAnnotationTypes(parentPanel.getAlignment(),
-            false);
+    types = AnnotationChooser.getAnnotationTypes(
+            parentPanel.getAlignment(), false);
     assertEquals("Not six annotation types", 7, types.size());
     assertTrue("IUPRED missing", types.contains("IUPRED"));
     assertTrue("JMol missing", types.contains("JMol"));
@@ -350,8 +351,8 @@ public class AnnotationChooserTest
     final Checkbox hideCheckbox = (Checkbox) getComponent(testee, 1, 0, 1);
     setSelected(hideCheckbox, true);
 
-    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee, 1,
-            1, 0);
+    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee,
+            1, 1, 0);
     setSelected(allSequencesCheckbox, true);
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
@@ -428,8 +429,8 @@ public class AnnotationChooserTest
     final Checkbox hideCheckbox = (Checkbox) getComponent(testee, 1, 0, 1);
     setSelected(hideCheckbox, true);
 
-    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee, 1,
-            1, 0);
+    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee,
+            1, 1, 0);
     setSelected(allSequencesCheckbox, true);
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
@@ -520,8 +521,8 @@ public class AnnotationChooserTest
     final Checkbox showCheckbox = (Checkbox) getComponent(testee, 1, 0, 0);
     final Checkbox hideCheckbox = (Checkbox) getComponent(testee, 1, 0, 1);
 
-    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee, 1,
-            1, 0);
+    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee,
+            1, 1, 0);
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
             .getAlignmentAnnotation();
@@ -596,8 +597,8 @@ public class AnnotationChooserTest
     final Checkbox showCheckbox = (Checkbox) getComponent(testee, 1, 0, 0);
     setSelected(showCheckbox, true);
 
-    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee, 1,
-            1, 0);
+    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee,
+            1, 1, 0);
     setSelected(allSequencesCheckbox, true);
 
     AlignmentAnnotation[] anns = parentPanel.getAlignment()
@@ -696,9 +697,9 @@ public class AnnotationChooserTest
     // TODO refactor to a test utility class
     cb.setState(select);
     // have to manually fire the action listener
-    cb.getItemListeners()[0].itemStateChanged(
-            new ItemEvent(cb, ItemEvent.ITEM_STATE_CHANGED, cb,
-                    select ? ItemEvent.SELECTED : ItemEvent.DESELECTED));
+    cb.getItemListeners()[0].itemStateChanged(new ItemEvent(cb,
+            ItemEvent.ITEM_STATE_CHANGED, cb, select ? ItemEvent.SELECTED
+                    : ItemEvent.DESELECTED));
   }
 
   /**
@@ -795,8 +796,8 @@ public class AnnotationChooserTest
     final Checkbox hideCheckbox = (Checkbox) getComponent(testee, 1, 0, 1);
     setSelected(hideCheckbox, true);
 
-    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee, 1,
-            1, 0);
+    final Checkbox allSequencesCheckbox = (Checkbox) getComponent(testee,
+            1, 1, 0);
     setSelected(allSequencesCheckbox, true);
 
     setSelected(getTypeCheckbox("JMol"), true);
index 912cd27..36d49dc 100644 (file)
@@ -76,14 +76,14 @@ public class AnnotationColumnChooserTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // pin down annotation sort order for test
-    Cache.applicationProperties.setProperty(Preferences.SORT_ANNOTATIONS,
+    Cache.setPropertyNoSave(Preferences.SORT_ANNOTATIONS,
             SequenceAnnotationOrder.NONE.name());
     final String TRUE = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty(Preferences.SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave(Preferences.SHOW_AUTOCALC_ABOVE,
             TRUE);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_QUALITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", TRUE);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", TRUE);
 
     AlignmentI al = new FormatAdapter().readFile(TEST_DATA,
             DataSourceType.PASTE, FileFormat.Fasta);
index 8e5c06b..8408b80 100644 (file)
@@ -47,10 +47,10 @@ public class AnnotationRowFilterTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
-            Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty(Preferences.SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.TRUE.toString());
+    Cache.setPropertyNoSave(
+            Preferences.SHOW_AUTOCALC_ABOVE, Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
     testee = new AnnotationRowFilter(af.viewport, af.alignPanel)
@@ -104,15 +104,13 @@ public class AnnotationRowFilterTest
             null);
     ann4.setSequenceRef(seq2);
     al.addAnnotation(ann4);
-    AlignmentAnnotation ann5 = new AlignmentAnnotation("Jnet", "Jnet",
-            null);
+    AlignmentAnnotation ann5 = new AlignmentAnnotation("Jnet", "Jnet", null);
     ann5.setSequenceRef(seq2);
     al.addAnnotation(ann5);
     /*
      * a second Jnet annotation for FER_BRANA
      */
-    AlignmentAnnotation ann6 = new AlignmentAnnotation("Jnet", "Jnet",
-            null);
+    AlignmentAnnotation ann6 = new AlignmentAnnotation("Jnet", "Jnet", null);
     ann6.setSequenceRef(seq2);
     al.addAnnotation(ann6);
 
@@ -120,7 +118,8 @@ public class AnnotationRowFilterTest
      * drop-down items with 'Per-sequence only' not checked
      */
     Vector<String> items = testee.getAnnotationItems(false);
-    assertEquals(items.toString(),
+    assertEquals(
+            items.toString(),
             "[Conservation, Quality, Consensus, Occupancy, ann1Label, Significance, Significance_1, Jronn_FER_CAPAA, Jronn_FER_BRANA, Jnet_FER_BRANA, Jnet_FER_BRANA_2]");
     assertEquals(testee.getAnnotationMenuLabel(ann1), "ann1Label");
     assertEquals(testee.getAnnotationMenuLabel(ann2), "Significance");
index 9835189..85b9525 100644 (file)
@@ -39,7 +39,7 @@ public class CalculationChooserTest
   {
     // read-only Jalview properties
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("BLOSUM62_PCA_FOR_NUCLEOTIDE",
+    Cache.setPropertyNoSave("BLOSUM62_PCA_FOR_NUCLEOTIDE",
             Boolean.FALSE.toString());
   }
 
@@ -93,7 +93,7 @@ public class CalculationChooserTest
     /*
      * enable inclusion of BLOSUM62 for nucleotide PCA (JAL-2962)
      */
-    Cache.applicationProperties.setProperty("BLOSUM62_PCA_FOR_NUCLEOTIDE",
+    Cache.setPropertyNoSave("BLOSUM62_PCA_FOR_NUCLEOTIDE",
             Boolean.TRUE.toString());
 
     /*
index 81e37d8..9746af1 100644 (file)
@@ -23,6 +23,7 @@ package jalview.gui;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+
 import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.IOException;
@@ -111,7 +112,6 @@ public class FreeUpMemoryTest
    * maximum retained heap usage (in MB) for a passing test
    */
   private static int MAX_RESIDUAL_HEAP = 45;
-
   /**
    * Configure (read-only) Jalview property settings for test
    */
@@ -122,24 +122,51 @@ public class FreeUpMemoryTest
             new String[]
             { "-nonews", "-props", "test/jalview/testProps.jvprops" });
     String True = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", True);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", True);
-    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", True);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", True);
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS", True);
+    Cache.setPropertyNoSave("SHOW_QUALITY", True);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", True);
+    Cache.setPropertyNoSave("SHOW_OCCUPANCY", True);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", True);
   }
 
+  /**
+   * A simple test that memory is released when all windows are closed.
+   * <ul>
+   * <li>generates a reasonably large alignment and loads it</li>
+   * <li>performs various operations on the alignment</li>
+   * <li>closes all windows</li>
+   * <li>requests garbage collection</li>
+   * <li>asserts that the remaining memory footprint (heap usage) is 'not large'
+   * </li>
+   * </ul>
+   * If the test fails, this suggests that a reference to some large object
+   * (perhaps the alignment data, or some annotation / Tree / PCA data) has
+   * failed to be garbage collected. If this is the case, the heap will need to
+   * be inspected manually (suggest using jvisualvm) in order to track down
+   * where large objects are still referenced. The code (for example
+   * AlignmentViewport.dispose()) should then be updated to ensure references to
+   * large objects are set to null when they are no longer required.
+   * 
+   * @throws IOException
+   */
   @Test(groups = "Memory")
   public void testFreeMemoryOnClose() throws IOException
   {
     File f = generateAlignment();
     f.deleteOnExit();
 
+    long expectedMin = MAX_RESIDUAL_HEAP;
+    long usedMemoryAtStart=getUsedMemory();
+    if (usedMemoryAtStart>expectedMin)
+    {
+      System.err.println("used memory before test is "+usedMemoryAtStart+" > "+expectedMin+"MB .. adjusting minimum.");
+      expectedMin = usedMemoryAtStart;
+    }
     doStuffInJalview(f);
 
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
-    checkUsedMemory(MAX_RESIDUAL_HEAP);
+    checkUsedMemory(expectedMin);
   }
 
   /**
@@ -153,10 +180,8 @@ public class FreeUpMemoryTest
     long availableMemory = Runtime.getRuntime().totalMemory();
     long freeMemory = Runtime.getRuntime().freeMemory();
     long usedMemory = availableMemory - freeMemory;
-
     return (int) (usedMemory / ONE_MB);
   }
-
   /**
    * Requests garbage collection and then checks whether remaining memory in use
    * is less than the expected value (in Megabytes)
index e6f4041..b185f53 100644 (file)
@@ -86,9 +86,13 @@ public class JvSwingUtilsTest
             JvSwingUtils.wrapTooltip(true, tip));
 
     tip = "0123456789012345678901234567890123456789012345678901234567890"; // 61
-    assertFalse(tip.equals(JvSwingUtils.wrapTooltip(false, tip)));
-    assertFalse(("<html>" + tip + "</html>")
-            .equals(JvSwingUtils.wrapTooltip(true, tip)));
+    // n/a -- message is too long for "false"
+//  
+//    assertFalse(tip.equals(JvSwingUtils.wrapTooltip(false, tip)));
+//    
+    
+    assertFalse(("<html>" + tip + "</html>").equals(JvSwingUtils
+            .wrapTooltip(true, tip)));
   }
 
   /**
@@ -100,9 +104,21 @@ public class JvSwingUtilsTest
   public void testWrapTooltip_multilineShortText()
   {
     String tip = "Now is the winter of our discontent<br>Made glorious summer by this sun of York";
-    assertEquals(tip, JvSwingUtils.wrapTooltip(false, tip));
-    assertEquals("<html>" + tip + "</html>",
-            JvSwingUtils.wrapTooltip(true, tip));
+    String tip2 = "Now is the winter of our discontent<br/>Made glorious summer by this sun of York";
+
+// BH not applicable in Jalview; "false" is only for when no <br> and only for short j2s2Discover messages
+//
+//    String s = JvSwingUtils.wrapTooltip(false, tip);
+//    System.out.println("<html>" + tip + "</html>");
+//    assertEquals(tip, s);
+    
+    String s;
+    s = JvSwingUtils.wrapTooltip(true, tip);
+    System.out.println(s);
+    assertEquals("<html>" + tip + "</html>", s);
+    s = JvSwingUtils.wrapTooltip(true, tip2);
+    System.out.println(s);
+    assertEquals("<html>" + tip + "</html>", s);
   }
 
   /**
@@ -112,11 +128,13 @@ public class JvSwingUtilsTest
   @Test(groups = { "Functional" })
   public void testWrapTooltip_longText()
   {
+    // BH should work in Java and JavaScript
     String tip = "Now is the winter of our discontent made glorious summer by this sun of York";
-    String expected = "<style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + tip + " </div>";
-    assertEquals("<html>" + expected + "</html>",
-            JvSwingUtils.wrapTooltip(true, tip));
-    assertEquals(expected, JvSwingUtils.wrapTooltip(false, tip));
+    String expected = JvSwingUtils.HTML_PREFIX + tip + "</div></html>";
+    String s = JvSwingUtils.wrapTooltip(true, tip);
+    assertEquals(expected, s);
+ // BH not applicable in Jalview; "false" is only for when no <br> and only for short j2s2Discover messages
+//    s = JvSwingUtils.wrapTooltip(false, tip);
+//    assertEquals(expected, s);
   }
 }
index 750698a..68da3c8 100644 (file)
@@ -22,17 +22,17 @@ package jalview.gui;
 
 import static org.testng.Assert.assertEquals;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceGroup;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
-
 import javax.swing.JTextArea;
 
-import junit.extensions.PA;
-
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 public class PairwiseAlignmentPanelTest
 {
   @Test(groups = "Functional")
@@ -40,7 +40,7 @@ public class PairwiseAlignmentPanelTest
   {
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
     AlignmentI al = viewport.getAlignment();
 
     /*
@@ -57,7 +57,7 @@ public class PairwiseAlignmentPanelTest
 
     PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport);
 
-    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText();
+    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText().replace("\r\n", "\n");
     String expected = "Score = 80.0\n" + "Length of alignment = 4\n"
             + "Sequence     FER1_PEA/29-32 (Sequence length = 7)\n"
             + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n"
@@ -78,11 +78,11 @@ public class PairwiseAlignmentPanelTest
     String seqs = ">Q93XJ9_SOLTU/23-29\nL-KAISNV\n>FER1_PEA/26-32\nV-TTTKAF\n";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqs,
             DataSourceType.PASTE);
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
 
     PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport);
 
-    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText();
+    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText().replace("\r\n", "\n");
     String expected = "Score = 80.0\n" + "Length of alignment = 4\n"
             + "Sequence     FER1_PEA/29-32 (Sequence length = 7)\n"
             + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n"
index 7636bda..5dada47 100644 (file)
@@ -199,9 +199,10 @@ public class PopupMenuTest
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertTrue(menu.isEnabled());
     String s = MessageManager.getString("label.add_annotations_for");
-    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + s
-            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
+//    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
+//            + "<div class=\"ttip\">" + s
+//            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
+    String expected = "<html>" + s + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
     assertEquals(expected, menu.getToolTipText());
   }
 
@@ -223,10 +224,13 @@ public class PopupMenuTest
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertTrue(menu.isEnabled());
     String s = MessageManager.getString("label.add_annotations_for");
-    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + s
-            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
-    assertEquals(expected, menu.getToolTipText());
+//    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
+//            + "<div class=\"ttip\">" + s
+//            + "<br/>Jmol/secondary structure<br/>PDB/Temp</html>";
+    String expected = "<html>" + s
+            + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
+    s = menu.getToolTipText();
+    assertEquals(expected, s);
   }
 
   /**
@@ -243,6 +247,7 @@ public class PopupMenuTest
     // PDB.secondary structure on Sequence0
     AlignmentAnnotation annotation = new AlignmentAnnotation(
             "secondary structure", "", 0);
+    annotation.annotations = new Annotation[] { new Annotation(2f) };
     annotation.setCalcId("PDB");
     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
     if (addToSequence)
@@ -257,6 +262,7 @@ public class PopupMenuTest
     // PDB.Temp on Sequence1
     annotation = new AlignmentAnnotation("Temp", "", 0);
     annotation.setCalcId("PDB");
+    annotation.annotations = new Annotation[] { new Annotation(2f) };
     seqs.get(1).getDatasetSequence().addAlignmentAnnotation(annotation);
     if (addToSequence)
     {
@@ -270,6 +276,7 @@ public class PopupMenuTest
     // JMOL.secondary structure on Sequence0
     annotation = new AlignmentAnnotation("secondary structure", "", 0);
     annotation.setCalcId("Jmol");
+    annotation.annotations = new Annotation[] { new Annotation(2f) };
     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
     if (addToSequence)
     {
index aa50301..0efd4c9 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.Font;
-import java.awt.FontMetrics;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Font;
+import java.awt.FontMetrics;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
 import jalview.io.DataSourceType;
-import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 
+import jalview.util.Platform;
+import jalview.viewmodel.ViewportRanges;
 import junit.extensions.PA;
 
 public class SeqCanvasTest
 {
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+      // 2.11.2 - beforeMethod setup
+      //          Cache.loadProperties("test/jalview/io/testProps.jvprops");
+      //Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+      //      Boolean.TRUE.toString());
+      //    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+      //     DataSourceType.FILE);
+      //     /*
+      // * wait for Consensus thread to complete
+     // */
+      // do
+       // {
+       //try
+         //{
+         // Thread.sleep(50);
+       //} catch (InterruptedException x)
+         //{
+         //}
+      //    } while (af.getViewport().getCalcManager().isWorking());
+
+    Cache.loadProperties(null);
+    Desktop.getInstance().setVisible(false);
+  }
+
   private AlignFrame af;
 
   /**
@@ -64,8 +92,8 @@ public class SeqCanvasTest
     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    assertEquals(charHeight, 17);
-    assertEquals(charWidth, 12);
+    assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
+    assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
 
     /*
      * first with scales above, left, right
@@ -76,7 +104,8 @@ public class SeqCanvasTest
     av.setScaleRightWrapped(true);
     FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
-    assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+    assertEquals(labelWidth,
+            !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
 
     /*
      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
@@ -85,15 +114,13 @@ public class SeqCanvasTest
     int canvasWidth = 400;
     int canvasHeight = 300;
     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
-    int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
-            canvasHeight);
+    int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
     assertEquals(wrappedWidth, residueColumns);
     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
             2 * charHeight);
-    int repeatingHeight = (int) PA.getValue(testee,
-            "wrappedRepeatHeightPx");
+    int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
 
@@ -200,7 +227,7 @@ public class SeqCanvasTest
     canvasWidth += 2;
     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
             canvasHeight);
-    assertEquals(wrappedWidth, 24); // 2px not enough
+    assertEquals(wrappedWidth, !Platform.isWin() ? 24 : 25); // 2px not enough
     canvasWidth += 1;
     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
             canvasHeight);
@@ -226,17 +253,16 @@ public class SeqCanvasTest
     AlignmentI al = av.getAlignment();
     assertEquals(al.getWidth(), 157);
     assertEquals(al.getHeight(), 15);
-
+  
     av.setWrapAlignment(true);
     av.getRanges().setStartEndSeq(0, 14);
     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    assertEquals(charHeight, 17);
-    assertEquals(charWidth, 12);
-
+    assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
+    assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
-
+  
     /*
      * first with scales above, left, right
      */
@@ -246,7 +272,8 @@ public class SeqCanvasTest
     av.setScaleRightWrapped(true);
     FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
-    assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+    assertEquals(labelWidth,
+            !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
     int annotationHeight = testee.getAnnotationHeight();
 
     /*
@@ -256,19 +283,17 @@ public class SeqCanvasTest
     int canvasWidth = 400;
     int canvasHeight = 300;
     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
-    int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
-            canvasHeight);
+    int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
     assertEquals(wrappedWidth, residueColumns);
     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
             2 * charHeight);
-    int repeatingHeight = (int) PA.getValue(testee,
-            "wrappedRepeatHeightPx");
+    int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
             + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
-
+  
     /*
      * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
      * make canvas height 2 of these plus 3 charHeights 
@@ -278,7 +303,7 @@ public class SeqCanvasTest
             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
-
+  
     /*
      * reduce canvas height by 1 pixel - should not be enough height
      * to draw 3 widths
@@ -286,14 +311,14 @@ public class SeqCanvasTest
     canvasHeight -= 1;
     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
-
+  
     /*
      * turn off scale above - can now fit in 2 and a bit widths
      */
     av.setScaleAboveWrapped(false);
     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
-
+  
     /*
      * reduce height to enough for 2 widths and not quite a third
      * i.e. two repeating heights + spacer + sequence - 1 pixel
@@ -347,32 +372,11 @@ public class SeqCanvasTest
     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
   }
 
-  @BeforeMethod(alwaysRun = true)
-  public void setUp()
-  {
-    Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
-            Boolean.TRUE.toString());
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
-
-    /*
-     * wait for Consensus thread to complete
-     */
-    do
-    {
-      try
-      {
-        Thread.sleep(50);
-      } catch (InterruptedException x)
-      {
-      }
-    } while (af.getViewport().getCalcManager().isWorking());
-  }
-
   @Test(groups = "Functional")
   public void testClear_HighlightAndSelection()
   {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewport av = af.getViewport();
     SearchResultsI highlight = new SearchResults();
     highlight.addResult(
index 8cec90c..82b4619 100644 (file)
@@ -34,7 +34,6 @@ 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;
@@ -53,6 +52,8 @@ import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.util.MessageManager;
 import jalview.viewmodel.ViewportRanges;
+
+
 import junit.extensions.PA;
 
 public class SeqPanelTest
index 9b49232..d3d6476 100644 (file)
@@ -22,6 +22,7 @@ package jalview.gui;
 
 import static org.testng.Assert.assertEquals;
 
+import jalview.api.AlignViewportI;
 import jalview.bin.Jalview;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -41,8 +42,7 @@ import org.testng.annotations.Test;
 public class SequenceRendererTest
 {
   AlignmentI al;
-
-  AlignViewport av;
+  AlignViewportI av;
 
   SequenceI seq1;
 
@@ -51,7 +51,7 @@ public class SequenceRendererTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+    { "-nonews", "-props", "test/jalview/testProps.jvprops" });
   }
 
   @BeforeMethod(alwaysRun = true)
index b74baf1..54b4d71 100644 (file)
@@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
+
 import java.util.Collection;
 import java.util.Vector;
 
@@ -118,9 +119,7 @@ public class StructureChooserTest
 
     upSeq_nocanonical.createDatasetSequence();
     // not a canonical reference
-    upSeq_nocanonical.addDBRef(
-            new DBRefEntry("UNIPROT", "0", "P38398", null, false));
-
+    upSeq_nocanonical.addDBRef(new DBRefEntry("UNIPROT","0","P38398",null,false));
   }
 
   @AfterMethod(alwaysRun = true)
@@ -235,9 +234,7 @@ public class StructureChooserTest
 
       }
     }
-
   }
-
   @Test(groups = { "Functional" })
   public void sanitizeSeqNameTest()
   {
diff --git a/test/jalview/hmmer/HMMERTest.java b/test/jalview/hmmer/HMMERTest.java
new file mode 100644 (file)
index 0000000..04cc3be
--- /dev/null
@@ -0,0 +1,134 @@
+package jalview.hmmer;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.io.HMMFile;
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.simple.Option;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class HMMERTest {
+
+  AlignFrame frame;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpBeforeClass() throws Exception
+  {
+    /*
+     * NB: check HMMER_PATH in testProps.jvprops is valid for
+     * the machine on which this runs
+     */
+    Jalview.main(
+            new String[]
+    { "-noquestionnaire", "-nonews", "-props",
+                "test/jalview/hmmer/testProps.jvprops", "-open",
+                "examples/uniref50.fa" });
+    frame = Desktop.getAlignFrames()[0];
+  }
+
+  @AfterClass(alwaysRun = true)
+  public static void tearDownAfterClass() throws Exception
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  }
+
+  /**
+   * Test with a dependency on locally installed hmmbuild binaries
+   * 
+   * @throws MalformedURLException
+   * @throws IOException
+   */
+  @Test(groups = "External")
+  public void testHMMBuildThenHMMAlign()
+          throws MalformedURLException, IOException
+  {
+    /*
+     * run hmmbuild
+     */
+    testHMMBuild();
+    List<SequenceI> hmms = frame.getViewport().getAlignment()
+            .getHmmSequences();
+    assertFalse(hmms.isEmpty());
+
+    /*
+     * now run hmmalign - by default with respect to the added HMM profile
+     */
+    testHMMAlign();
+  }
+
+  public void testHMMBuild()
+  {
+    /*
+     * set up argument to run hmmbuild for the alignment
+     */
+    ArrayList<ArgumentI> params = new ArrayList<>();
+    String argName = MessageManager.getString("label.hmmbuild_for");
+    String argValue = MessageManager.getString("label.alignment");
+    params.add(
+            new Option(argName, null, false, null, argValue, null, null));
+
+    HMMBuild builder = new HMMBuild(frame, params);
+    builder.run();
+
+    SequenceI seq = frame.getViewport().getAlignment().getSequenceAt(0);
+    HiddenMarkovModel hmm = seq.getHMM();
+    assertNotNull(hmm);
+
+    assertEquals(hmm.getLength(), 148);
+    assertEquals(hmm.getAlphabetType(), "amino");
+    assertEquals(hmm.getName(), "Alignment_HMM");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES),
+            "0.648193");
+  }
+
+  public void testHMMAlign()
+  {
+    HmmerCommand thread = new HMMAlign(frame,
+            new ArrayList<ArgumentI>());
+    thread.run();
+
+    AlignFrame[] alignFrames = Desktop.getAlignFrames();
+    if (alignFrames == null)
+    {
+      fail("No align frame loaded");
+    }
+
+    /*
+     * now have the original align frame, and another for realigned sequences
+     */
+    assertEquals(alignFrames.length, 2);
+    AlignmentI original = alignFrames[0].getViewport().getAlignment();
+    assertNotNull(original);
+    AlignmentI realigned = alignFrames[1].getViewport().getAlignment();
+    assertNotNull(realigned);
+    assertFalse(original.getHmmSequences().isEmpty());
+    assertFalse(realigned.getHmmSequences().isEmpty());
+
+    SequenceI ferCapan = original.findName("FER_CAPAN");
+    assertTrue(ferCapan.getSequenceAsString().startsWith("MA------SVSAT"));
+
+    SequenceI ferCapanRealigned = realigned.findName("FER_CAPAN");
+    assertTrue(ferCapanRealigned.getSequenceAsString()
+            .startsWith("-------m-A----SVSAT"));
+  }
+}
+
diff --git a/test/jalview/hmmer/testProps.jvprops b/test/jalview/hmmer/testProps.jvprops
new file mode 100644 (file)
index 0000000..05cd493
--- /dev/null
@@ -0,0 +1,88 @@
+#---JalviewX Properties File---
+#Fri Apr 25 09:54:25 BST 2014
+SCREEN_Y=768
+SCREEN_X=936
+SHOW_WSDISCOVERY_ERRORS=true
+LATEST_VERSION=2.8.0b1
+SHOW_CONSERVATION=true
+JALVIEW_RSS_WINDOW_SCREEN_WIDTH=550
+JAVA_CONSOLE_SCREEN_WIDTH=450
+LAST_DIRECTORY=/Volumes/Data/Users/jimp/Documents/testing/Jalview/examples
+ID_ITALICS=true
+SORT_ALIGNMENT=No sort
+SHOW_IDENTITY=true
+WSMENU_BYHOST=false
+SEQUENCE_LINKS=EMBL-EBI Search|http\://www.ebi.ac.uk/ebisearch/search.ebi?db\=allebi&query\=$SEQUENCE_ID$
+SHOW_FULLSCREEN=false
+RECENT_URL=http\://www.jalview.org/examples/exampleFile_2_7.jar
+FONT_NAME=SansSerif
+BLC_JVSUFFIX=true
+VERSION_CHECK=false
+YEAR=2011
+SHOW_DBREFS_TOOLTIP=true
+MSF_JVSUFFIX=true
+SCREENGEOMETRY_HEIGHT=1600
+JAVA_CONSOLE_SCREEN_Y=475
+JAVA_CONSOLE_SCREEN_X=830
+PFAM_JVSUFFIX=true
+PIR_JVSUFFIX=true
+STARTUP_FILE=http\://www.jalview.org/examples/exampleFile_2_3.jar
+JAVA_CONSOLE_SCREEN_HEIGHT=162
+PIR_MODELLER=false
+GAP_SYMBOL=-
+SHOW_QUALITY=true
+SHOW_OCCUPANCY=true
+SHOW_GROUP_CONSERVATION=false
+SHOW_JWS2_SERVICES=true
+SHOW_NPFEATS_TOOLTIP=true
+FONT_STYLE=plain
+ANTI_ALIAS=false
+SORT_BY_TREE=false
+RSBS_SERVICES=|Multi-Harmony|Analysis|Sequence Harmony and Multi-Relief (Brandt et al. 2010)|hseparable,gapCharacter\='-',returns\='ANNOTATION'|?tool\=jalview|http\://zeus.few.vu.nl/programs/shmrwww/index.php?tool\=jalview&groups\=$PARTITION\:min\='2',minsize\='2',sep\=' '$&ali_file\=$ALIGNMENT\:format\='FASTA',writeasfile$
+AUTHORFNAMES=Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
+JALVIEW_RSS_WINDOW_SCREEN_HEIGHT=328
+SHOW_GROUP_CONSENSUS=false
+SHOW_CONSENSUS_HISTOGRAM=true
+SHOW_OVERVIEW=false
+AUTHORS=J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
+FIGURE_AUTOIDWIDTH=false
+SCREEN_WIDTH=900
+ANNOTATIONCOLOUR_MIN=ffc800
+SHOW_STARTUP_FILE=false
+RECENT_FILE=examples/uniref50.fa\t/Volumes/Data/Users/jimp/Documents/testing/Jalview/examples/RF00031_folded.stk\t/Volumes/Data/Users/jimp/bs_ig_mult.out
+DEFAULT_FILE_FORMAT=FASTA
+SHOW_JAVA_CONSOLE=false
+VERSION=2.8b1
+FIGURE_USERIDWIDTH=
+WSMENU_BYTYPE=false
+DEFAULT_COLOUR=None
+NOQUESTIONNAIRES=true
+JALVIEW_NEWS_RSS_LASTMODIFIED=Apr 23, 2014 2\:53\:26 PM
+BUILD_DATE=01 November 2013
+PILEUP_JVSUFFIX=true
+SHOW_CONSENSUS_LOGO=false
+SCREENGEOMETRY_WIDTH=2560
+SHOW_ANNOTATIONS=true
+JALVIEW_RSS_WINDOW_SCREEN_Y=0
+USAGESTATS=false
+JALVIEW_RSS_WINDOW_SCREEN_X=0
+SHOW_UNCONSERVED=false
+SHOW_JVSUFFIX=true
+DAS_LOCAL_SOURCE=
+SCREEN_HEIGHT=650
+ANNOTATIONCOLOUR_MAX=ff0000
+AUTO_CALC_CONSENSUS=true
+FASTA_JVSUFFIX=true
+DAS_ACTIVE_SOURCE=uniprot\t
+JWS2HOSTURLS=http\://www.compbio.dundee.ac.uk/jabaws
+PAD_GAPS=false
+CLUSTAL_JVSUFFIX=true
+SHOW_ENFIN_SERVICES=true
+FONT_SIZE=10
+RIGHT_ALIGN_IDS=false
+USE_PROXY=false
+WRAP_ALIGNMENT=false
+#DAS_REGISTRY_URL=http\://www.dasregistry.org/das/ # retired 01/05/2015
+DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+logs.Jalview.level=DEBUG
+HMMER_PATH=/Users/gmcarstairs/software/hmmer-3.1b2-macosx-intel/binaries
index 689bd59..6e8cd98 100644 (file)
@@ -68,9 +68,9 @@ public class AnnotatedPDBFileInputTest
   @BeforeMethod(alwaysRun = true)
   public void setup() throws Exception
   {
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     FileLoader loader = new FileLoader(false);
     AlignFrame af = loader.LoadFileWaitTillLoaded("examples/1gaq.txt",
@@ -207,7 +207,7 @@ public class AnnotatedPDBFileInputTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
 
   }
 
diff --git a/test/jalview/io/BSMLFileTest.java b/test/jalview/io/BSMLFileTest.java
new file mode 100644 (file)
index 0000000..3052658
--- /dev/null
@@ -0,0 +1,68 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class BSMLFileTest
+{
+  @Test(groups="Functional")
+  public void testParse_BSML() throws IOException
+  {
+    //@formatter:off
+    String data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
+            "<Bsml>\r\n" + 
+            "  <Definitions>\r\n" + 
+            "    <Sequences>\r\n" + 
+            "      <Sequence\r\n" + 
+            "        comment=\"Sequence Data Contained in 'Multiple-alignment' section\"\r\n" + 
+            "        id=\"SEQ-ID:181170\" length=\"30\" molecule=\"dna\" title=\"EP1\">\r\n" + 
+            "        <Feature-tables>\r\n" + 
+            "          <Feature-table title=\"GENE\">\r\n" + 
+            "            <Feature class=\"GENE\" title=\"TEST-001\">\r\n" + 
+            "              <Interval-loc complement=\"0\" endpos=\"20\" startpos=\"10\"/>\r\n" + 
+            "              <Resource id=\"GENE-ID:181171\"/>\r\n" + 
+            "            </Feature>\r\n" + 
+            "          </Feature-table>\r\n" + 
+            "        </Feature-tables>\r\n" + 
+            "      </Sequence>\r\n" + 
+            "    </Sequences>\r\n" + 
+            "    <Tables>\r\n" + 
+            "      <Multiple-alignment-table molecule-type=\"nucleotide\">\r\n" + 
+            "        <Sequence-alignment sequences=\"1\">\r\n" + 
+            "          <Sequence-data seq-name=\"EP1\">--AATTTT-ATTTAGTGTCT-----------</Sequence-data>\r\n" + 
+            "          <Alignment-consensus/>\r\n" + 
+            "        </Sequence-alignment>\r\n" + 
+            "      </Multiple-alignment-table>\r\n" + 
+            "    </Tables>\r\n" + 
+            "    <Notebook notebook=\"\"/>\r\n" + 
+            "  </Definitions>\r\n" + 
+            "</Bsml>\r\n" + 
+            "";
+    BSMLFile cf = new BSMLFile(data, DataSourceType.PASTE);
+// why twice?    cf.parse();
+    SequenceI[] seqs = cf.getSeqsAsArray();
+    assertEquals(seqs.length, 1);
+    SequenceI seq = seqs[0];
+    assertEquals(seq.getName(), "EP1");
+    assertEquals(seq.getStart(), 1);
+    assertEquals(seq.getEnd(), 31);
+    assertTrue(seq.getSequenceAsString().equals("--AATTTT-ATTTAGTGTCT-----------"));
+    SequenceFeaturesI features = seq.getFeatures();
+    List<SequenceFeature> genes = features.getAllFeatures("GENE");
+    assertEquals(genes.size(), 1);
+    SequenceFeature sf = genes.get(0);
+    assertEquals(sf.getDescription(), "TEST-001");
+    assertEquals(sf.begin + "," + sf.end, "10,20");
+    
+  }
+
+}
index a356621..c62ccfb 100644 (file)
@@ -265,23 +265,23 @@ public class BackupFilesTest
   {
     Cache.loadProperties(propsFile);
 
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enabled));
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             BackupFilesPresetEntry.SAVEDCONFIG, bfpe.toString());
     /*
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enabled));
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX, suffix);
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX_DIGITS,
+    Cache.setPropertyNoSave(BackupFiles.SUFFIX, suffix);
+    Cache.setPropertyNoSave(BackupFiles.SUFFIX_DIGITS,
             Integer.toString(digits));
-    Cache.applicationProperties.setProperty(BackupFiles.REVERSE_ORDER,
+    Cache.setPropertyNoSave(BackupFiles.REVERSE_ORDER,
             Boolean.toString(reverse));
-    Cache.applicationProperties.setProperty(BackupFiles.NO_MAX,
+    Cache.setPropertyNoSave(BackupFiles.NO_MAX,
             Boolean.toString(noMax));
-    Cache.applicationProperties.setProperty(BackupFiles.ROLL_MAX,
+    Cache.setPropertyNoSave(BackupFiles.ROLL_MAX,
             Integer.toString(rollMax));
-    Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
+    Cache.setPropertyNoSave(BackupFiles.CONFIRM_DELETE_OLD,
             "false");
             */
   }
index 3ca6ed8..59cb3cd 100644 (file)
@@ -184,7 +184,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
       {
         // retrieve dbref
 
-        SequenceFetcher sf = new SequenceFetcher(Desktop.instance,
+        SequenceFetcher sf = new SequenceFetcher(Desktop.getInstance(),
                 forSource, forAccession);
         sf.run();
         AlignFrame[] afs = Desktop.getAlignFrames();
@@ -211,7 +211,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
       }
       else
       {
-        Desktop.instance.closeAll_actionPerformed(null);
+        Desktop.getInstance().closeAll_actionPerformed(null);
         // recover stored project
         af = new FileLoader(false).LoadFileWaitTillLoaded(
                 savedProjects.get(first).toString(), DataSourceType.FILE);
@@ -278,7 +278,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
           }
           else
           {
-            Desktop.instance.closeAll_actionPerformed(null);
+            Desktop.getInstance().closeAll_actionPerformed(null);
             pass3 = 0;
             // recover stored project
             File storedProject = savedProjects.get(nextxref);
@@ -389,7 +389,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
                 }
                 else
                 {
-                  Desktop.instance.closeAll_actionPerformed(null);
+                  Desktop.getInstance().closeAll_actionPerformed(null);
                   // recover stored project
                   File storedProject = savedProjects.get(nextnextxref);
                   if (storedProject == null)
index 95ac679..5ea2488 100644 (file)
@@ -72,7 +72,7 @@ public class FeaturesFileTest
      * remove any sequence mappings created so they don't pollute other tests
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
   }
 
index 27cfa5e..b589e94 100644 (file)
@@ -20,8 +20,6 @@
  */
 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;
@@ -53,15 +51,14 @@ 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())));
-    // GenBank/ENA
+    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())));
     assertFalse(formats.isIdentifiable(null));
 
     /*
@@ -86,25 +83,26 @@ public class FileFormatsTest
   @Test(groups = "Functional")
   public void testGetWritableFormats()
   {
-    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
+    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3]";
     FileFormats formats = FileFormats.getInstance();
     assertEquals(formats.getWritableFormats(true).toString(), expected);
-    expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, Jalview]";
+    expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, Jalview, HMMER3]";
     assertEquals(formats.getWritableFormats(false).toString(), expected);
   }
 
   @Test(groups = "Functional")
   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, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    String writable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3]";
+    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, HMMER3, BSML]";
     FileFormats formats = FileFormats.getInstance();
+    System.out.println(formats.getReadableFormats().toString());
     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, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, BSML]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
 
@@ -112,8 +110,8 @@ public class FileFormatsTest
      * re-register the format: it gets added to the end of the list
      */
     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, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview, Fasta]";
+    writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3, 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, HMMER3, BSML, Fasta]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
   }
@@ -169,7 +167,8 @@ 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)
index 7898ed5..e6d7c4b 100644 (file)
  */
 package jalview.io;
 
+import static org.testng.Assert.assertEquals;
+
+import jalview.bin.Cache;
+
 import org.junit.Assert;
 import org.testng.annotations.Test;
 
@@ -39,4 +43,34 @@ public class FileLoaderTest
     // Data source type expected to be DataSourceType.URL
     Assert.assertEquals(DataSourceType.URL, fileLoader.protocol);
   }
+
+  @Test(groups = "Functional")
+  public void testUpdateRecentlyOpened()
+  {
+    // ensure properties file is read-only
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+
+    String recent = "RECENT_FILE";
+    Cache.removeProperty(recent);
+
+    String prop = FileLoader.updateRecentlyOpened("a/b/c",
+            DataSourceType.FILE);
+    assertEquals(prop, "a/b/c");
+
+    prop = FileLoader.updateRecentlyOpened("d/e/f", DataSourceType.FILE);
+    assertEquals(prop, "d/e/f\ta/b/c");
+
+    // revisiting a file moves it to the top of the list
+    prop = FileLoader.updateRecentlyOpened("a/b/c", DataSourceType.FILE);
+    assertEquals(prop, "a/b/c\td/e/f");
+    
+    // history list is limited to the most recent 11 items
+    for (int i = 0; i < 20; i++)
+    {
+      prop = FileLoader.updateRecentlyOpened(String.format("%d.fa", i),
+              DataSourceType.FILE);
+    }
+    assertEquals(prop,
+            "19.fa\t18.fa\t17.fa\t16.fa\t15.fa\t14.fa\t13.fa\t12.fa\t11.fa\t10.fa\t9.fa");
+  }
 }
index 8f3ad2f..d63fb70 100644 (file)
@@ -56,8 +56,17 @@ public class FormatAdapterTest
   {
     try
     {
-      AlignmentI al = new FormatAdapter().readFile("examples/uniref50.fa",
+      AlignmentI al;
+      if (format == FileFormat.HMMER3)
+      {
+        al = new FormatAdapter().readFile("examples/uniref50.hmm",
+                DataSourceType.FILE, FileFormat.HMMER3);
+      }
+      else
+      {
+        al = new FormatAdapter().readFile("examples/uniref50.fa",
               DataSourceType.FILE, FileFormat.Fasta);
+      }
 
       /*
        * 'gap' is the gap character used in the alignment data file here,
@@ -73,8 +82,9 @@ public class FormatAdapterTest
       AlignmentI reloaded = new FormatAdapter().readFile(formatted,
               DataSourceType.PASTE, format);
       List<SequenceI> reread = reloaded.getSequences();
-      assertEquals("Wrong number of reloaded sequences", seqs.length,
-              reread.size());
+        assertEquals("Wrong number of reloaded sequences", seqs.length,
+                reread.size());
+
 
       int i = 0;
       for (SequenceI seq : reread)
@@ -130,7 +140,7 @@ public class FormatAdapterTest
   @DataProvider(name = "formats")
   static Object[][] getFormats()
   {
-    List<FileFormatI> both = new ArrayList<FileFormatI>();
+    List<FileFormatI> both = new ArrayList<>();
     for (FileFormatI format : FileFormats.getInstance().getFormats())
     {
       if (format.isReadable() && format.isWritable()
diff --git a/test/jalview/io/HMMAlignmentTest.sto b/test/jalview/io/HMMAlignmentTest.sto
new file mode 100644 (file)
index 0000000..f0f1f4a
--- /dev/null
@@ -0,0 +1,16 @@
+# STOCKHOLM 1.0
+#=GS Q7XA98_TRIPR/1-152   DR UNIPROT ; Q7XA98 
+#=GS FER1_PEA/1-149       DR UNIPROT ; P09911 
+#=GS Q93XJ9_SOLTU/1-144   DR UNIPROT ; Q93XJ9 
+#=GS FER_CAPAN/1-144      DR UNIPROT ; Q9ZTS2 
+#=GS FER1_SOLLC/1-144     DR UNIPROT ; Q43517 
+#=GS FER_CAPAA/1-97       DR UNIPROT ; P83527 
+FER_CAPAA/1-97             ------------------------------------------------------------------------ASYKVKLITPDGPIEFDCPDDVYILDQAEEAGHDLPYSCRAGSCSSCAGKIAGGAVDQTDGNFLDDDQLEEGWVLTCVAYPQSDVTIETHKEAELVG-
+FER_CAPAN/1-144            MA------SVSATMISTSFMPRKPAVTSL-KPIPNVGE--ALFGLKS-A--NGGKVTCMASYKVKLITPDGPIEFDCPDNVYILDQAEEAGHDLPYSCRAGSCSSCAGKIAGGAVDQTDGNFLDDDQLEEGWVLTCVAYPQSDVTIETHKEAELVG-
+FER1_SOLLC/1-144           -------------MA------SISGTMISTSFLPRKPAVTSL-KAISNVGE--ALFGLKS-G--RNGRITCMASYKVKLITPEGPIEFECPDDVYILDQAEEEGHDLPYSCRAGSCSSCAGKVTAGSVDQSDGNFLDEDQEAAGFVLTCVAYPKGDVTIETHKEEELTA-
+Q93XJ9_SOLTU/1-144         --------MA------SISGTMISTSFLPRKPVVTSL-KAISNVGE--ALFGLKS-G--RNGRITCMASYKVKLITPDGPIEFECPDDVYILDQAEEEGHDLPYSCRAGSCSSCAGKVTAGTVDQSDGKFLDDDQEAAGFVLTCVAYPKCDVTIETHKEEELTA-
+FER1_PEA/1-149             -------------MATT---PALYGTAVSTSFLRTQPMPMSV-TTTKAFSN--GFLGLKT-SLKRGDLAVAMASYKVKLVTPDGTQEFECPSDVYILDHAEEVGIDLPYSCRAGSCSSCAGKVVGGEVDQSDGSFLDDEQIEAGFVLTCVAYPTSDVVIETHKEEDLTA-
+Q7XA98_TRIPR/1-152         -------------MATT---PALYGTAVSTSFMRRQPVPMSV-ATTTTTKAFPSGFGLKSVSTKRGDLAVAMATYKVKLITPEGPQEFDCPDDVYILDHAEEVGIELPYSCRAGSCSSCAGKVVNGNVNQEDGSFLDDEQIEGGWVLTCVAFPTSDVTIETHKEEELTA-
+#=GC SS_cons                                                                              EEE    EEEEEEEE    HHHHH          e    e  e EE     EE          HHHH E   e  EE   EEEEE         
+#=GC Iron_Sulphur_Contacts -------------------------------------------------------------------------------------------------e----e--e-----------------------------e---------------------
+//
diff --git a/test/jalview/io/HMMAlignmentTestHMM.hmm b/test/jalview/io/HMMAlignmentTestHMM.hmm
new file mode 100644 (file)
index 0000000..649f5f9
--- /dev/null
@@ -0,0 +1,478 @@
+HMMER3/f [3.2 | June 2018]
+NAME  Alignment_HMM
+LENG  152
+ALPH  amino
+RF    no
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Fri Jun  7 14:44:10 2019
+NSEQ  6
+EFFN  2.071289
+CKSUM 2312940853
+STATS LOCAL MSV      -10.2278  0.70920
+STATS LOCAL VITERBI  -10.8843  0.70920
+STATS LOCAL FORWARD   -4.3996  0.70920
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   2.41432  3.67931  2.75921  2.60121  3.50018  2.76530  3.83614  2.93106  2.71371  2.66765  3.66760  3.24838  3.26026  3.17831  3.19279  2.51076  2.65615  2.59873  5.09464  3.62512
+          2.68544  4.42237  2.77531  2.73135  3.46365  2.40524  3.72506  3.29365  2.67752  2.69366  4.24294  2.90358  2.73751  3.18158  2.89812  2.37898  2.77531  2.98530  4.58488  3.61515
+          0.78057  1.55851  1.10441  0.67815  0.70837  0.00000        *
+      1   2.59800  4.39329  3.63625  3.18787  3.71456  3.43841  4.11392  3.03068  3.09157  2.71409  1.85445  3.48425  4.00395  3.44434  3.39575  1.59145  2.94832  2.79270  5.21434  3.95539      9 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02133  4.25401  4.97636  0.61958  0.77255  0.98598  0.46693
+      2   1.50579  4.32755  4.18645  3.78216  3.88559  3.56425  4.59767  2.41074  3.70275  2.69336  3.78707  3.89382  4.19613  3.99133  3.94010  2.99086  3.05698  1.28977  5.52248  4.31320     10 v - - -
+          2.68550  4.42242  2.77537  2.73141  3.46371  2.40530  3.72512  3.29371  2.67758  2.69372  4.24707  2.90364  2.73757  3.18164  2.89818  2.37841  2.77443  2.98536  4.58494  3.61520
+          0.90209  1.16285  1.26696  1.01535  0.44985  0.39057  1.12909
+      3   3.42707  4.91214  4.61207  4.26925  3.52422  4.22326  4.89422  2.75377  4.00666  2.06722  0.68834  4.47974  4.70831  4.36739  4.15005  3.81331  3.75358  2.81142  5.41119  4.20659     14 M - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01938  4.34894  5.07129  0.61958  0.77255  0.90177  0.52062
+      4   1.43643  4.42503  4.31312  3.86585  3.75424  3.86050  4.64018  1.40277  3.76673  2.49922  3.64972  4.05060  4.39156  4.05997  3.99981  3.25891  3.19718  2.11006  5.45161  4.24039     15 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.14665  4.34894  2.09167  0.61958  0.77255  0.90177  0.52062
+      5   2.37098  4.31475  3.56770  3.24979  4.37798  3.10030  4.31086  3.75066  3.26247  3.47251  4.31621  3.42494  3.83407  3.57718  3.57680  1.30947  1.53387  3.24501  5.72919  4.50336     16 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02198  4.22428  4.94663  0.61958  0.77255  0.54114  0.87248
+      6   2.50652  4.44141  3.83949  3.54853  4.61304  3.23185  4.57545  3.99871  3.56049  3.72110  4.55726  3.64948  3.99392  3.85973  3.84879  1.49216  1.01986  3.44946  5.96659  4.76085     17 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24086  4.47354  1.59630  0.61958  0.77255  0.76398  0.62700
+      7   2.65517  4.36127  3.81549  3.38335  3.69064  3.54500  4.25486  1.58799  3.30272  2.65201  3.68407  3.64162  4.10535  3.63002  3.58335  1.61226  3.00284  2.44965  5.24306  3.98781     18 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02133  4.25401  4.97636  0.61958  0.77255  0.98598  0.46693
+      8   2.74723  4.45808  3.83003  3.43336  1.55716  3.57739  4.01030  3.12129  3.39844  2.75883  3.78862  3.64312  4.13759  3.66183  3.66464  1.60155  3.09781  2.89427  4.64509  3.10366     19 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02133  4.25401  4.97636  0.61958  0.77255  0.98598  0.46693
+      9   2.65532  4.43699  3.65638  3.26130  3.78761  1.68622  4.19753  3.09268  3.18548  2.76736  1.66329  3.54773  4.04431  3.53861  3.47551  2.87376  3.01742  2.85730  5.28180  4.04464     20 m - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02133  4.25401  4.97636  0.61958  0.77255  0.55834  0.84900
+     10   2.62413  4.55150  3.73217  3.46502  4.58891  3.32606  4.53947  3.93771  3.48022  3.66044  4.54532  3.64966  1.08714  3.81713  3.77509  2.80225  1.57704  3.45878  5.93284  4.72852     21 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01709  4.47354  5.19589  0.61958  0.77255  0.51701  0.90709
+     11   1.99709  4.68552  3.40260  2.84388  3.94615  3.60689  3.90388  3.32023  2.73340  2.98773  2.29384  3.29447  4.03205  3.12399  2.03393  2.35985  2.99523  3.04365  5.29921  4.03061     22 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     12   3.03612  4.58206  4.17469  3.58665  3.60032  4.09338  4.33760  1.51772  1.94651  1.71212  3.50362  3.91731  4.43832  3.70785  3.50867  3.39096  3.26453  2.43051  5.23091  4.03724     23 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     13   2.76197  4.70799  3.41034  2.96484  3.84310  3.56022  4.02290  3.51807  2.97071  3.16868  4.04204  3.37800  2.07618  3.30589  3.36146  1.59778  3.07503  3.20545  5.27554  2.20871     24 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     14   1.42932  4.46589  4.02364  3.69643  4.72988  1.43294  4.68906  4.13282  3.71549  3.83309  4.64260  3.74322  4.03851  3.97444  4.00639  2.72003  1.63454  3.53847  6.07503  4.89460     25 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     15   2.63382  4.43500  3.98903  3.52691  4.05377  3.47381  4.41118  3.21360  3.46955  3.09630  4.00516  3.71495  4.11115  3.75748  3.76367  1.74048  1.56254  1.69711  5.50933  4.30015     26 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     16   1.97870  4.28154  4.12278  3.55312  2.14335  3.81420  4.17500  2.68382  3.44988  2.41995  2.61784  3.79310  4.21338  3.68984  3.66833  3.11084  1.96434  2.49922  4.91664  3.71472     27 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     17   2.91846  4.34842  4.48199  3.90988  3.46317  4.07572  4.44144  1.98330  3.78456  1.67828  3.41359  4.10010  4.43748  4.00384  3.95399  2.01206  3.16092  1.64497  5.07625  3.88829     28 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     18   2.70635  4.56892  3.60528  3.14080  3.96671  3.53723  4.15403  3.33644  3.10057  1.82443  3.93202  3.49380  2.11690  3.43662  3.45226  1.62468  3.04334  3.04998  5.39218  4.13558     29 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.26457  4.57201  1.50451  0.61958  0.77255  0.62415  0.76726
+     19   2.81401  4.82005  3.48128  2.94818  4.34473  3.53963  3.92028  3.69738  2.45527  3.31575  4.19707  3.33652  4.04563  3.10077  1.62480  2.90920  1.49986  3.36258  5.52959  4.29493     30 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01981  4.32725  5.04960  0.61958  0.77255  0.42245  1.06549
+     20   2.99742  5.17558  3.29695  2.86766  4.77169  3.64255  3.98284  4.20460  1.15211  3.71930  4.54717  3.31939  4.14896  3.12831  2.80934  1.59962  3.28379  3.79417  5.81941  4.54197     31 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     21   3.28763  4.90910  4.31235  3.99239  1.36983  4.02580  4.37777  3.48169  3.95787  2.96396  4.13543  4.15449  1.08518  4.20039  4.16663  3.51751  3.64052  3.33099  4.84797  3.27396     32 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     22   3.45018  4.70771  5.41218  4.85806  3.55317  4.84496  5.27184  1.41698  4.72877  1.42021  2.43827  4.98314  5.05481  4.81112  4.77841  4.21177  3.68367  1.42720  5.55642  4.46185     33 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     23   2.82485  4.73326  3.55705  3.00955  4.05482  3.65786  4.00613  3.34731  2.73677  3.06317  3.96177  3.42018  1.80573  3.23126  2.02314  2.96454  3.09977  1.95730  5.39957  4.14889     34 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     24   2.86127  5.09016  3.19616  2.69781  4.52201  3.59071  3.86031  3.94606  2.49967  3.49458  4.30305  1.96704  4.04133  2.99863  1.96172  2.88186  1.74316  3.56959  5.66035  4.34483     35 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     25   2.78170  4.95829  3.16704  2.62944  4.23097  3.58469  3.79757  3.63325  2.16991  3.23781  4.05928  3.13605  3.98936  2.12578  2.95429  2.00470  3.01690  2.13727  5.47298  4.15357     36 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     26   2.74867  4.60831  3.61740  3.20842  4.05654  1.87017  4.23901  3.42440  3.19407  1.83184  4.02720  3.54846  1.75260  3.52860  3.53526  2.92904  3.10347  3.12934  5.47730  4.23070     37 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24245  4.57201  1.58496  0.61958  0.77255  0.62415  0.76726
+     27   2.23893  4.60373  3.28448  1.87774  3.79882  3.57859  3.83870  3.03021  2.72644  2.79800  2.80144  3.21864  3.97823  3.06477  3.13889  2.82027  2.92566  2.31720  5.19891  3.92417     38 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.34327  4.34894  1.28144  0.61958  0.77255  0.43345  1.04488
+     28   2.73943  4.82202  3.20350  2.68202  4.15055  3.52581  3.79474  3.47895  1.71004  3.13955  3.99838  3.15504  2.24076  2.96517  2.84990  2.80694  2.99350  2.43471  5.40910  4.11885     39 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01981  4.32725  5.04960  0.61958  0.77255  0.92238  0.50679
+     29   1.67523  4.32939  3.80070  3.28713  3.65556  3.52435  4.14641  2.82922  3.20444  2.62909  2.04920  3.56786  4.04881  3.50886  3.50296  2.86733  2.23962  2.60434  5.15734  3.94638     40 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01981  4.32725  5.04960  0.61958  0.77255  0.42245  1.06549
+     30   1.66814  4.43166  3.95026  3.47077  3.91143  3.54985  4.34218  1.84764  3.41576  2.94090  3.86903  3.70285  4.13774  3.70337  3.71262  1.58997  3.04021  2.78308  5.38688  4.17260     41 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     31   2.86839  4.40063  4.28613  3.75197  3.53211  3.91378  4.39907  2.56140  3.63254  1.42290  3.49235  3.96842  4.35205  3.89101  3.84593  1.98860  3.14663  1.72465  5.13588  3.93489     42 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.33933  4.57201  1.28224  0.61958  0.77255  0.62415  0.76726
+     32   2.99338  4.73521  3.52318  3.19423  1.49578  3.74751  3.84834  3.30385  3.23971  2.88691  3.93859  1.69947  4.23548  3.52506  3.56009  3.13754  3.28417  3.09751  4.46980  2.84938     43 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02133  4.25401  4.97636  0.61958  0.77255  0.39057  1.12909
+     33   2.26113  4.65266  3.39149  2.85535  3.96561  2.14052  3.94035  3.32256  2.32255  3.01451  3.87297  3.29920  4.01793  3.16391  3.22857  2.83216  2.44156  2.03814  5.33010  4.05751     44 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     34   2.14527  4.45044  3.76875  3.26603  3.85788  2.04177  4.18979  3.17104  3.21967  1.81090  3.81461  3.56903  4.08500  3.51989  3.54833  2.88152  1.96723  2.89522  5.29995  4.07724     45 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     35   2.77531  5.06924  3.07796  1.89721  4.35250  3.56090  3.75513  2.86917  1.82937  3.33902  4.13804  3.06897  3.95918  2.88565  2.93068  2.78088  2.17797  3.41042  5.54533  4.19589     46 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24245  4.57201  1.58496  0.61958  0.77255  0.62415  0.76726
+     36   2.66334  4.78591  3.14690  2.69736  4.34791  3.40164  3.87711  3.75678  2.10710  3.36111  4.18006  3.15163  3.92504  3.04074  3.00575  1.44108  2.29363  3.36678  5.57460  4.27063     47 s - - -
+          2.68590  4.42232  2.77527  2.73130  3.46361  2.40520  3.72502  3.29361  2.67748  2.69362  4.24697  2.90308  2.73747  3.18154  2.89808  2.37894  2.77488  2.98526  4.58484  3.61510
+          0.44576  1.04019  5.07129  0.13971  2.03720  0.43345  1.04488
+     37   1.35032  4.33598  4.11743  3.57274  2.66462  3.74950  4.26894  2.70612  3.48547  2.58049  3.54278  3.80339  4.21776  3.73820  3.72893  3.07578  2.40333  2.07300  5.08335  3.87699     49 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.26457  4.57201  1.50451  0.61958  0.77255  0.62415  0.76726
+     38   2.67849  4.67202  3.20606  2.69337  3.93262  2.63512  3.80028  3.31587  2.17250  1.96311  3.82914  3.16211  3.94141  3.00575  3.00084  2.28900  2.93113  3.03056  5.26473  3.98290     50 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01981  4.32725  5.04960  0.61958  0.77255  0.92238  0.50679
+     39   2.23745  4.71916  3.05672  2.16980  2.23785  3.50580  3.78395  3.31902  2.66523  2.97723  3.83443  2.49003  3.93379  2.99259  3.10154  2.77864  2.93862  3.03813  5.25364  3.93663     51 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.29014  4.32725  1.43280  0.61958  0.77255  0.42245  1.06549
+     40   2.71194  4.71147  3.14073  2.76488  2.63325  1.94411  3.91859  3.50545  2.86219  3.15174  4.02297  1.82298  3.98451  3.17614  3.28134  2.81220  3.01803  3.18804  5.26894  3.87083     52 n - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01895  4.37135  5.09369  0.61958  0.77255  0.87955  0.53609
+     41   2.64945  4.49564  3.49208  3.06414  3.88051  1.81354  4.08661  3.23766  3.04151  1.74255  3.84911  3.42067  2.61702  3.37781  3.38586  2.83194  2.98495  2.96040  5.30850  4.06003     53 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01895  4.37135  5.09369  0.61958  0.77255  0.44572  1.02266
+     42   2.20625  4.93221  3.17339  2.72120  4.55189  1.72555  3.94981  3.99413  1.81667  3.55820  4.34814  3.19358  3.98997  3.09380  3.14134  2.24894  3.05082  3.55894  5.74349  4.41072     54 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     43   2.73118  4.74977  3.26283  2.70881  2.98752  2.82347  3.82752  3.34686  1.92589  2.48911  3.84846  3.19660  3.99345  3.02672  3.10018  2.03919  2.96671  3.06691  5.29037  3.99373     55 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24245  4.57201  1.58496  0.61958  0.77255  0.62415  0.76726
+     44   3.21892  4.54748  4.94678  4.39384  1.84065  4.45647  4.69231  2.23828  4.25181  1.55834  3.19076  4.52046  4.72563  4.36468  4.33750  3.79737  3.45238  1.39236  5.03910  3.75691     56 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01938  4.34894  5.07129  0.61958  0.77255  0.43345  1.04488
+     45   2.63358  4.56206  3.94281  3.76669  4.90587  0.86987  4.83032  4.31802  3.87414  4.03413  4.86498  3.81723  4.11786  4.13859  4.13012  2.82336  1.52160  3.69008  6.22345  5.06760     57 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24245  4.57201  1.58496  0.61958  0.77255  0.62415  0.76726
+     46   3.01864  1.79566  4.69259  4.19257  3.41038  4.04365  4.66782  2.43586  3.98238  1.14404  3.36027  4.28885  4.50409  4.22027  4.10902  3.45690  3.32610  2.36331  5.19668  3.99182     58 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01938  4.34894  5.07129  0.61958  0.77255  0.90177  0.52062
+     47   2.96377  4.89266  3.53249  2.95386  4.04795  3.74350  3.88142  3.34726  1.40052  2.91530  1.93101  3.37640  4.14009  3.07637  2.67530  3.06693  3.18523  3.14375  5.36699  4.12971     59 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01938  4.34894  5.07129  0.61958  0.77255  0.43345  1.04488
+     48   1.77870  4.80105  3.33239  2.80291  4.36522  3.47131  3.95619  3.78270  2.73318  3.38875  4.19761  3.24889  3.98437  3.11824  1.99740  1.90727  2.40269  3.38967  5.61455  4.31002     60 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.21961  4.57201  1.67756  0.61958  0.77255  0.62415  0.76726
+     49   2.60823  4.64033  3.22015  2.80894  4.20933  3.36458  3.98710  3.59284  2.85357  3.27469  4.11620  1.86137  3.93152  3.18866  3.26591  1.65119  2.95563  2.38198  5.53276  4.23147     61 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01895  4.37135  5.09369  0.61958  0.77255  0.44572  1.02266
+     50   2.74410  4.67335  3.45404  3.06703  3.90844  1.64792  4.12669  3.60477  3.11681  3.26108  4.13393  3.44191  4.09025  3.43093  3.49590  1.86622  3.09650  3.26485  5.35022  1.97056     62 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.11556  4.57201  2.31473  0.61958  0.77255  0.62415  0.76726
+     51   2.95370  5.14232  3.38998  2.75626  4.47479  3.70680  3.76608  3.85318  1.57496  2.58800  4.21663  3.22595  4.07617  2.90174  1.75899  2.96747  2.46355  3.52788  5.54040  4.28024     63 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01709  4.47354  5.19589  0.61958  0.77255  0.76398  0.62700
+     52   2.97237  4.48471  4.17487  3.61038  3.68717  4.08286  4.40175  1.59509  2.09243  2.51779  3.58882  3.93788  4.44245  3.78487  3.66371  3.38387  3.21319  1.46639  5.29622  4.08064     64 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01709  4.47354  5.19589  0.61958  0.77255  0.51701  0.90709
+     53   3.16272  5.37499  3.59177  2.92070  4.82565  3.84597  3.83774  4.18579  1.50049  3.63868  4.47141  3.36065  4.21246  2.96157  1.48253  3.15294  1.98320  3.83770  5.70684  4.49260     65 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     54   2.69126  2.25915  3.80687  3.28606  3.66656  2.21575  4.13449  3.01259  3.21697  1.80809  3.65267  2.64888  4.11014  3.51421  3.51970  2.94231  2.98955  2.76835  5.12838  3.90654     66 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     55   2.75976  4.54905  2.30455  2.95518  3.70676  2.79648  3.96957  2.00953  2.95066  2.74085  2.27446  3.39166  4.08996  3.25986  3.33214  2.95036  2.99901  2.76002  5.14787  3.89753     67 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     56   1.92745  4.55633  3.54968  2.99110  3.77370  3.65270  3.97236  3.10509  2.88439  2.02402  3.71145  3.40529  4.07525  3.24934  2.56281  2.91984  1.98149  2.86151  5.18773  3.94368     68 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     57   1.83228  4.53823  3.61230  3.10354  4.02012  3.49038  4.12836  2.50681  3.07752  3.08124  3.94943  3.45725  2.07017  3.39222  3.45240  1.83561  2.99081  3.04929  5.41506  4.16867     69 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     58   2.75895  4.66749  2.12963  2.81059  3.81488  3.63897  3.90096  3.17855  2.82626  2.89854  3.77788  3.28020  4.04584  3.14685  3.24113  2.89220  2.45960  2.10817  5.21741  2.34581     70 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     59   1.87134  2.92490  3.41990  2.89562  4.22753  2.00930  4.00910  3.62340  1.87199  3.26968  4.10116  3.31508  4.00325  3.20209  3.23675  2.78496  2.99855  3.26410  5.53516  4.25405     71 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.62415  0.76726
+     60   2.89677  4.42989  4.26135  3.71893  3.52156  3.94729  4.39343  2.57065  3.59726  2.35872  1.80824  3.96209  2.13460  3.86316  3.82140  3.27132  3.16554  1.68431  5.14387  3.95469     72 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01548  4.57201  5.29435  0.61958  0.77255  0.40804  1.09349
+     61   1.57565  4.67644  3.61371  3.07202  3.92167  3.75428  4.06548  1.97069  1.96866  2.90804  3.83592  3.49072  4.17868  3.32201  3.23603  3.03786  3.11158  2.81687  5.33764  4.09102     73 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     62   2.78306  4.91407  3.16080  1.90266  4.17861  3.59010  3.85833  3.57055  2.67830  2.17323  4.03601  3.16932  4.01281  3.02684  3.12616  1.86668  2.50737  3.25963  5.47226  4.15166     74 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     63   3.70553  4.96355  5.18038  4.77967  1.51175  4.68042  4.04025  1.78081  4.59062  2.70856  3.92100  4.53151  4.97996  4.54894  4.56550  4.02683  3.92721  3.14972  4.19952  1.17802     75 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     64   2.92406  5.37226  1.90980  2.50543  4.72545  3.56703  3.85582  4.19183  1.50834  3.69445  4.47134  3.05483  4.03152  2.97675  3.09023  2.88240  2.07165  3.77594  5.84089  4.43845     76 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     65   2.78562  2.01948  4.55150  4.07045  3.82424  3.73349  4.63777  2.73929  3.93362  2.80343  3.79417  4.09169  2.08695  4.16452  4.09382  3.13564  3.15495  1.28567  5.38169  4.19434     77 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     66   3.00177  5.48795  1.87629  2.49660  4.83835  3.57687  3.90289  4.32338  1.47116  3.80689  4.58836  3.05453  2.14999  3.02804  3.16908  2.94349  3.25201  3.89612  5.94044  4.52232     78 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     67   2.97021  5.07355  1.83355  2.70799  4.34142  2.06896  4.05903  3.73788  2.99051  1.56224  4.26678  3.22685  4.13776  3.25977  3.46241  3.01746  3.25070  3.43787  5.68476  4.34458     79 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     68   2.80435  4.51738  3.70485  3.17345  3.71222  3.76651  4.10838  1.80977  3.12705  2.73704  3.66708  2.15696  2.34690  3.43898  3.47179  3.04517  3.05888  2.24183  5.18327  3.94621     80 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     69   3.21770  4.57086  4.97188  4.44662  3.86947  4.49942  5.05720  1.47430  4.33664  2.56546  3.72458  4.61110  4.86186  4.58040  4.51238  3.86070  1.54232  1.34555  5.64266  4.43926     81 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     70   2.95708  5.14521  2.99403  1.83121  4.19063  3.63668  3.92702  3.82818  2.78051  3.41772  4.26662  3.17833  1.74182  3.12106  3.22848  2.97355  3.20630  3.50738  5.52563  2.17177     82 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     71   2.81050  4.79261  2.28753  2.03040  2.37706  3.65659  3.90550  2.14897  2.80356  2.99311  3.87365  3.24848  4.06373  3.12317  3.23928  2.91155  3.04567  3.04162  5.32210  4.01594     83 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     72   2.93437  5.03828  3.04311  1.83946  4.30552  1.65755  3.99888  3.68879  2.85177  1.98910  4.20097  3.22855  4.11576  3.18781  3.28771  2.98470  3.19963  3.39021  5.62197  4.29753     84 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     73   2.73902  2.32080  1.99570  2.81784  4.32430  3.51217  4.01811  3.72234  2.87487  3.36392  4.18957  3.27606  1.98571  3.19798  3.31847  2.82453  2.44358  3.35784  5.62280  4.31413     85 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     74   2.87088  4.85397  3.36105  2.86591  4.06081  3.69200  3.97062  2.23427  2.75935  3.04437  3.96579  3.33519  2.30215  1.57203  3.13528  2.97368  3.12105  3.13247  5.42664  4.13968     86 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     75   1.84740  5.71599  1.70450  1.29585  5.04192  3.56048  4.02374  4.54881  2.96239  4.03556  4.84247  3.02480  4.13163  3.16811  3.53263  3.05937  3.42628  4.10758  6.18823  4.70375     87 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     76   3.06953  5.41519  1.80939  1.67828  1.68690  3.60061  3.98096  4.10134  2.91401  3.66294  4.51080  3.08865  4.12438  3.15808  3.43938  3.03000  3.32748  3.75306  5.79034  4.33444     88 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     77   3.03870  5.47436  1.90526  1.29548  4.76783  3.57223  3.95900  4.16399  2.84250  3.75797  4.57501  3.05039  4.09259  3.10162  3.37273  2.98560  3.29722  2.13649  5.96102  4.53077     89 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     78   1.74787  1.64658  4.29549  3.78786  3.51816  3.70291  4.33651  2.97775  3.65907  2.75066  3.68827  3.90525  4.24339  3.90389  3.85120  3.07083  3.08999  2.74475  5.04806  2.07593     90 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     79   2.75948  4.56996  3.79425  3.38335  4.06237  1.91193  4.35624  1.91599  3.36983  3.10692  4.02732  3.67147  1.55829  3.68001  3.68915  2.97218  3.12763  3.01951  5.50730  4.27905     91 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     80   2.84361  5.11662  1.88614  2.55687  4.33854  3.57668  2.26268  3.81219  2.65486  2.21008  4.20214  3.09171  4.01205  2.98715  3.13133  2.39282  3.08430  3.46799  5.59000  4.21533     92 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     81   4.05062  6.09585  0.25016  3.16592  5.63180  4.01455  4.88658  5.44743  4.14657  4.91966  5.97362  3.74150  4.71639  4.16606  4.71915  3.96503  4.42100  5.01815  6.60717  5.45534     93 D - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     82   3.04959  4.58334  4.21832  3.66721  3.55820  4.11355  4.41299  2.59646  3.48731  1.61108  3.47616  3.98523  4.47164  2.16526  3.73518  3.41507  3.28754  1.39698  5.22079  4.02388     94 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     83   1.82715  4.61744  3.67020  3.22382  3.72690  3.63976  4.15441  3.35282  3.20066  3.04408  3.94999  3.56979  2.08621  3.51946  3.54139  2.98757  3.12307  3.07635  5.20580  1.61753     95 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     84   2.90062  4.56201  3.71550  2.09080  3.49327  3.86304  4.08810  1.59101  3.18089  2.67620  3.64335  3.61449  4.25289  3.49651  3.50065  3.15073  3.13529  2.70012  5.01515  2.15048     96 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     85   2.86216  4.89215  3.16897  1.91855  4.11409  3.63867  3.95829  3.48538  2.81326  1.65439  4.02074  3.25472  4.09032  3.16123  3.23386  1.94184  3.11683  3.21094  5.46706  4.16201     97 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     86   3.02911  2.20051  1.43610  1.72118  4.75550  3.57025  3.97871  4.20820  2.86443  3.76403  4.57605  3.07440  4.09745  3.12563  3.38808  2.98631  3.29630  3.81727  5.95041  4.53316     98 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     87   2.95229  5.31841  3.20763  2.65443  4.67641  2.24293  2.35827  4.11573  2.37176  3.59677  4.38392  3.16553  4.07082  2.08202  1.87641  2.92797  3.16779  3.72847  5.70861  4.38173     99 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     88   0.89304  4.83593  3.52252  3.21237  4.28827  3.57365  1.98662  3.92815  3.18973  3.57384  4.45958  3.56834  4.20309  3.58992  3.52232  3.01049  3.26669  3.54231  5.67260  4.31414    100 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     89   3.32897  5.98995  1.59036  1.21041  5.29223  1.85016  4.13193  4.84216  3.17456  4.29824  5.13981  3.02434  4.20540  3.29334  3.80439  3.19395  3.62119  4.37723  6.44195  4.89720    101 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     90   2.86612  5.04422  3.04967  1.53080  4.31043  3.59969  3.90678  3.70979  2.72310  2.13413  4.17356  3.16516  4.05445  3.07158  3.17171  1.93423  3.11943  3.39271  5.59276  4.25151    102 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     91   2.33839  2.33319  3.66936  2.51274  3.71047  3.68123  4.05819  3.02481  3.07985  2.77576  3.67017  3.50531  2.35114  3.38402  3.43065  2.95965  3.00527  2.02342  5.15389  3.92054    103 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     92   2.77841  4.71648  3.50561  3.12657  4.00129  1.51312  4.19518  3.69627  3.18257  3.34991  4.21891  3.49374  4.13113  3.49456  3.56191  1.76331  3.14172  3.34204  5.43765  2.07352    104 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     93   2.80900  4.65790  3.51214  3.01139  3.79431  3.68037  2.38933  2.16836  2.94391  2.94776  3.84277  3.43128  4.12697  3.30452  3.30672  1.48154  3.07555  2.99103  5.22365  3.91367    105 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     94   3.04847  1.50371  1.61592  2.10236  4.66715  3.58851  4.05112  4.08665  2.97076  3.69723  4.53735  3.13921  4.13665  3.22062  3.48126  3.03480  3.33057  3.72643  5.91738  4.53055    106 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     95   1.88410  4.65574  3.78265  3.20892  3.82389  3.79819  4.09423  3.12221  2.89200  1.51545  3.75540  3.58503  4.21920  3.38078  2.03442  3.09778  3.14791  2.92061  5.27033  4.04785    107 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     96   1.54090  4.57715  3.92273  3.65745  4.90631  1.66510  4.74228  4.35275  3.75572  4.01671  4.81278  3.76174  1.32436  4.00941  4.06725  2.79701  3.13028  3.70679  6.22047  5.03095    108 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     97   2.97420  5.05368  3.30132  2.83355  4.07677  2.15834  3.92294  3.75113  1.76958  3.34315  4.20275  3.31006  4.15184  3.13365  2.98906  3.02497  3.21508  3.44631  5.40486  1.71904    109 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     98   2.82535  4.57572  4.10028  3.71301  4.03925  3.66632  4.58086  1.72884  3.64091  3.01747  4.04865  3.90026  4.29816  3.95980  3.90549  0.98108  3.22247  2.79320  5.58742  4.33006    110 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+     99   1.43514  0.82892  4.80681  4.58666  4.69417  3.39195  5.16406  3.80758  4.43086  3.75239  4.68183  4.17266  4.21161  4.63066  4.50594  2.88757  3.18548  3.35323  6.15457  5.03517    111 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    100   2.82498  4.95715  3.40974  2.94255  4.70228  1.94565  4.08107  4.14251  2.73685  3.69574  4.49749  3.36420  4.09658  3.23391  1.46522  1.83152  3.16614  3.68558  5.84898  4.56169    112 r - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    101   1.10475  4.52228  4.11576  3.83581  4.96435  1.64082  4.84380  4.42439  3.89199  4.08943  4.86937  3.83086  4.09259  4.12724  4.17193  1.57246  3.10246  3.72147  6.28518  5.11952    113 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    102   1.43394  1.83402  4.58768  4.30129  4.75045  1.21055  5.01466  4.12708  4.21833  3.87731  4.70556  4.01744  4.11929  4.41365  4.36995  2.77887  3.09436  3.53695  6.15056  5.02931    114 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    103   1.63449  4.47604  4.14466  3.71946  4.28385  3.44523  4.59529  3.45965  3.67241  3.33633  4.23051  3.81973  4.14313  3.94095  3.94969  1.28973  3.08609  1.71945  5.72686  4.53067    115 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    104   2.83251  1.48569  1.76496  3.05212  4.63546  1.87091  4.34311  4.05699  3.30285  3.71503  4.55202  3.45874  4.14769  3.56082  3.73402  2.94445  3.23212  3.61792  5.93755  4.65533    116 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    105   2.96957  5.32904  3.14366  2.65593  4.72494  3.66151  3.84186  4.16441  1.75186  3.64779  4.43821  3.16888  4.08411  1.93241  2.79580  1.59020  3.19856  3.76805  5.76348  4.43156    117 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    106   2.66373  4.49207  4.03296  3.57936  4.18285  3.48606  4.48323  3.36835  3.52695  3.23079  4.12951  3.75581  4.14350  3.81520  3.82610  1.34147  1.75798  1.80637  5.62511  4.41600    118 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    107   2.74792  1.62573  1.91234  3.05734  4.31516  3.50848  4.22275  3.63214  3.15497  3.36887  4.23026  3.44805  4.10111  3.45245  3.56167  2.88548  1.89159  3.28014  5.67623  4.40213    119 c - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    108   0.68941  4.56331  4.20169  4.04692  5.05279  1.58716  5.03141  4.47978  4.15844  4.19817  5.00681  3.95737  4.16246  4.36890  4.37009  2.84226  3.19149  3.77919  6.37750  5.25838    120 A - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    109   3.02964  5.08328  3.17150  3.12104  5.13170  0.81179  4.59407  4.73207  3.60754  4.31434  5.15959  1.65030  4.27627  3.83105  4.02354  3.11995  3.49284  4.12591  6.35429  5.05698    121 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    110   2.85780  4.78493  3.46807  2.92281  2.21689  3.71308  3.94679  3.34785  1.56806  3.01008  3.90816  3.37147  4.12450  3.18495  3.10090  2.97945  2.06570  3.09328  5.28703  3.97445    122 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    111   3.56792  4.79047  5.56843  5.05882  3.83185  5.08010  5.63680  1.72792  4.96256  1.47356  3.59353  5.21772  5.28003  5.11439  5.06846  4.48190  3.81459  0.88361  5.91262  4.77603    123 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    112   2.34885  4.88546  1.50680  2.76582  4.28202  3.57605  3.99821  3.49749  2.86169  3.28542  4.14261  3.25275  4.06485  3.17747  3.31619  2.88335  2.45367  2.13395  5.60583  4.28774    124 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    113   2.37560  5.43186  1.88002  2.44821  4.76975  2.31003  3.85108  4.26118  2.66530  3.74586  4.51061  2.44214  4.01416  2.00579  3.18288  2.86422  3.16817  3.82620  5.88838  4.45514    125 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    114   3.02825  5.34001  1.64973  2.62556  5.04958  1.43770  4.18261  4.55907  3.15840  4.08500  4.89492  3.15845  4.14205  3.34808  3.70413  1.79379  3.38532  4.04813  6.24427  4.81118    126 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    115   2.43421  5.39468  1.99800  2.11130  4.72686  3.53375  3.80066  4.21905  2.58750  3.69532  4.44801  2.47018  3.98139  1.98470  3.09824  2.34600  3.10833  3.78019  5.82949  4.40030    127 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    116   2.94597  4.44901  4.44561  3.93108  3.62623  2.15331  4.54520  2.49864  3.80946  1.67061  3.57798  4.11336  4.45736  4.06092  4.00094  3.35990  3.23101  1.30872  5.24560  4.04661    128 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    117   3.07108  5.65983  1.61666  1.64591  4.97722  3.55644  3.94110  4.49006  1.85258  3.95163  4.73218  2.37323  4.08443  3.06951  3.35115  2.98315  3.32751  4.04015  6.08373  4.61352    129 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    118   2.90641  5.14851  3.03065  1.82407  2.34336  3.62785  3.86089  3.81266  2.66227  3.36932  4.21751  3.14311  4.06155  1.65090  3.10716  2.91351  3.14323  3.49036  5.52105  4.11540    130 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    119   2.76255  4.86202  3.19852  2.29238  4.13565  2.23517  3.86729  3.53680  2.70242  2.16346  4.00273  3.18654  4.00924  3.04372  3.14909  2.07641  2.54383  3.22686  5.44173  4.12929    131 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    120   3.57941  5.58171  0.75523  3.12318  4.02169  3.96847  4.37908  4.42174  3.52990  3.92216  4.91170  3.63855  4.53554  3.76654  3.97529  3.57745  3.86789  4.12842  1.75169  3.95153    132 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    121   2.91174  5.02616  1.86612  2.75649  4.50983  1.54341  4.12514  3.82111  3.05693  3.54399  4.40141  3.25444  4.12333  3.31620  3.53368  2.97056  3.23532  1.93277  5.82328  4.48241    133 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    122   2.86459  5.20437  2.00723  2.52234  4.49985  3.55603  3.85351  3.95142  2.67319  2.15274  4.30753  2.15442  4.01247  2.99055  3.16622  2.05025  3.11283  3.57971  5.70819  4.32388    134 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    123   2.86858  4.64306  3.62963  3.12613  1.70785  3.76769  4.03967  3.15671  3.04812  2.80119  3.75818  3.52490  4.19369  2.11823  3.38968  3.05795  1.97932  2.94141  5.07338  3.71197    135 f - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    124   2.87729  2.25286  3.84987  2.10126  3.62200  3.85343  4.19223  2.84299  3.24572  1.47166  3.58556  3.69315  4.26287  3.56918  3.54313  3.15439  3.12814  2.66630  5.14969  3.92844    136 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    125   1.87554  4.89400  1.55330  2.83635  4.32557  3.59793  4.10822  3.41524  3.01358  3.31001  4.20357  3.31594  4.12580  3.30435  3.46699  2.95750  3.16753  1.90970  5.68921  4.37289    137 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    126   1.31978  5.65673  1.53971  2.00100  5.00455  3.56144  4.04702  4.49528  2.99731  4.00710  4.82437  3.04396  4.14090  3.19815  3.56394  3.06909  3.43192  4.06642  6.17533  4.70374    138 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    127   2.99154  5.46776  1.93424  1.79632  4.74629  2.13218  3.90636  4.24433  2.76518  3.75551  4.54564  3.02592  4.05721  3.04023  3.29236  2.93391  3.24309  3.83702  5.91396  2.21504    139 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    128   2.90782  4.87069  3.38284  2.90818  2.18962  3.72084  3.95729  3.47130  2.86207  3.05902  3.98760  3.36869  2.19368  1.63399  3.25068  3.00729  3.16046  3.21829  5.19587  3.77282    140 q - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    129   2.80387  4.50973  3.68288  2.53916  3.64508  3.79101  4.04638  2.09044  3.05762  2.30112  3.59386  3.52646  4.16714  2.16471  3.40576  3.04110  3.03585  1.93346  5.11678  3.88419    141 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    130   2.33816  4.92974  3.14216  1.79211  4.20735  3.58332  3.86460  3.60039  2.68643  2.17242  4.06142  3.16489  4.01325  3.03072  3.13839  1.97098  3.03629  3.28400  5.49619  4.17164    142 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    131   2.04954  5.30098  1.94774  2.15701  4.64724  2.73655  3.83132  4.12007  2.63294  3.63223  4.40230  3.02948  3.99372  2.95114  3.13793  2.82654  2.09314  3.70436  5.79378  4.38447    143 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    132   2.67461  1.98295  4.54536  4.13152  4.08175  1.38240  4.74089  3.14924  4.01069  3.12077  4.06082  4.04085  4.22311  4.23030  4.16807  2.96147  3.11654  1.64169  5.59063  4.41633    144 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    133   2.83984  4.25742  4.52040  3.93471  2.15049  3.96796  4.23092  2.63475  3.77248  2.37225  3.34682  4.03138  4.33444  3.94714  3.87337  3.27306  1.99292  1.78236  2.52955  3.47977    145 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    134   1.94019  4.61061  5.17838  4.67117  3.96915  4.65508  5.28276  1.37973  4.57265  2.60668  3.79310  4.80809  5.00205  4.81289  4.73487  4.03650  3.57041  1.05280  5.80976  4.59927    146 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    135   2.95269  4.69517  3.60665  1.97843  3.45446  3.85775  4.03090  3.11104  3.08550  1.51175  3.71657  3.54821  4.25024  3.42171  3.42726  3.14413  3.18521  2.92923  4.99169  2.16477    147 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    136   2.73695  4.65605  3.95869  3.70106  4.76353  3.44035  4.74373  4.12362  3.71195  3.84701  4.72350  3.83266  1.83108  4.03586  3.98938  2.92296  0.79061  3.61700  6.10688  4.92055    148 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    137   2.98616  1.77306  3.54257  2.96917  4.20397  3.77066  2.12431  3.65954  1.76469  3.27311  4.13902  3.40552  4.18321  3.14102  2.82770  3.07008  3.21991  3.37163  5.45841  4.17830    149 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    138   2.87318  2.27487  4.08101  3.51686  3.73262  3.86520  4.28384  2.71684  1.98443  2.72135  3.68760  3.80830  4.30540  3.67099  3.52284  3.18558  3.14569  1.41553  5.23503  4.02368    150 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    139   1.47784  5.66115  1.72234  1.61325  4.99518  3.55917  4.01510  4.49306  2.94403  3.99053  4.79675  3.03102  4.12373  3.15930  3.50625  3.04342  3.40107  4.05871  6.14898  4.67614    151 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    140   1.90208  4.30196  4.42388  3.85048  2.53064  3.96808  4.20458  2.66664  3.71202  2.43952  3.40720  3.98922  4.33968  3.90366  3.85104  3.27006  3.09799  1.78444  4.75310  1.94066    152 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    141   2.90716  5.18104  2.93680  1.74324  4.63204  3.55056  3.97551  4.06458  2.79684  3.63682  4.44671  3.14754  1.69442  3.12392  3.26513  2.91368  2.00011  3.66927  5.83669  4.46781    153 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    142   2.78359  4.49365  3.67376  3.10170  3.62714  3.76072  4.00455  2.07844  2.47123  1.90888  3.58652  3.50028  4.13823  2.73193  3.32251  3.01272  2.16843  2.73906  5.07658  3.84773    154 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    143   2.78538  4.94129  3.12446  1.90128  4.30138  2.71211  3.90968  3.70356  2.74123  3.32793  4.14951  3.17742  4.01886  3.07350  3.19942  1.81320  3.05695  2.10287  5.57786  4.24807    155 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01431  4.64966  5.37201  0.61958  0.77255  0.48576  0.95510
+    144   3.00331  5.31213  1.34717  2.61563  4.93207  1.88993  4.12688  4.41067  3.06726  3.95757  4.76779  3.14959  4.12417  3.28737  3.59771  2.99405  1.91587  3.94167  6.13257  4.71628    156 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.24219  4.64967  1.58216  0.61958  0.77255  0.48576  0.95510
+    145   2.90502  4.57765  3.77724  3.34069  3.46532  3.78667  1.98962  2.88032  3.15748  2.73793  3.75784  3.66796  4.25545  3.57022  3.42567  3.15372  3.18669  1.26808  5.01387  3.61579    157 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    146   2.75147  4.74628  3.34357  2.80400  4.07335  3.57581  3.87957  3.30949  1.78780  3.06148  3.94193  3.25653  4.01680  3.07201  2.96883  2.85636  1.75071  2.41258  5.38823  4.11449    158 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    147   3.00773  4.73948  3.43003  1.82450  3.89452  3.85717  4.23450  1.28531  3.14323  2.70935  3.80542  3.55715  4.30487  3.50591  3.49765  3.23918  3.26469  2.47035  5.46041  4.19035    159 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    148   3.57591  5.69104  2.89034  0.43897  5.13067  3.74172  4.42582  4.71128  3.39211  4.24880  5.23832  3.41868  4.38352  3.65470  3.83330  3.52091  3.89687  4.34990  6.18698  4.96489    160 E - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    149   2.88350  5.07456  2.83549  1.62789  4.53394  3.47506  3.97205  3.88890  2.80700  3.53770  4.39728  3.11273  4.03065  3.14364  3.24643  2.90638  1.35985  3.52987  5.77718  4.42810    161 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    150   3.16212  4.85727  3.80335  3.36209  3.16774  3.96001  1.35846  3.22944  3.06257  1.59155  3.82989  3.70773  4.37953  3.54897  3.31681  3.33732  3.40874  3.10705  4.77246  3.24032    162 h - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    151   2.94073  5.06198  3.33349  2.84187  4.57011  3.62118  3.89060  3.90975  1.20057  3.49273  4.35032  3.28330  4.09402  3.04189  2.67455  2.98263  1.83732  3.56058  5.65563  4.40287    163 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01794  4.42541  5.14776  0.61958  0.77255  0.82155  0.57937
+    152   1.71060  5.15430  2.74244  1.20101  4.65798  3.45089  3.98759  4.03463  2.85903  3.66257  4.51252  3.07555  4.02822  3.15387  3.33026  2.91028  3.22047  3.64649  5.88103  4.50506    164 e - - -
+          2.68526  4.42272  2.77529  2.72949  3.46401  2.40536  3.72542  3.29401  2.67788  2.69262  4.24737  2.90394  2.73787  3.18194  2.89848  2.37934  2.77449  2.98523  4.58524  3.61550
+          0.55049  0.85960        *  1.51709  0.24763  0.00000        *
+//
diff --git a/test/jalview/io/HMMFileTest.java b/test/jalview/io/HMMFileTest.java
new file mode 100644 (file)
index 0000000..cf74f55
--- /dev/null
@@ -0,0 +1,531 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.HMMNode;
+import jalview.datamodel.HiddenMarkovModel;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import junit.extensions.PA;
+
+public class HMMFileTest {
+
+  HMMFile fn3;
+
+  HMMFile pKinase;
+
+  HMMFile made1;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUp() throws IOException
+  {
+    fn3 = new HMMFile("test/jalview/io/test_fn3_hmm.txt",
+            DataSourceType.FILE);
+
+    pKinase = new HMMFile("test/jalview/io/test_PKinase_hmm.txt",
+            DataSourceType.FILE);
+
+    made1 = new HMMFile("test/jalview/io/test_MADE1_hmm.txt",
+            DataSourceType.FILE);
+  }
+
+  @Test(groups = "Functional")
+  public void testParse() throws IOException
+  {
+    HiddenMarkovModel hmm = pKinase.getHMM();
+    assertEquals(hmm.getName(), "Pkinase");
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER), "PF00069.17");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
+            "Protein kinase domain");
+    assertEquals(hmm.getLength(), 260);
+    assertNull(hmm.getProperty(HMMFile.MAX_LENGTH));
+    assertEquals(hmm.getAlphabetType(), "amino");
+    assertFalse(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Thu Jun 16 11:44:06 2011");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "54");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES),
+            "3.358521");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3106786190");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD),
+            "70.30 70.30");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "70.30 70.30");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "70.20 70.20");
+  
+    assertEquals(hmm.getSymbols(), "ACDEFGHIKLMNPQRSTVWY");
+  
+    assertEquals(hmm.getMatchEmissionProbability(0, 'Y'), 0.16102, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(11, 'P'), 0.0130, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(24, 'I'), 0.02583, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(83, 'C'), 0.008549,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(332, 'E'), 0.07998,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(381, 'D'), 0.014465,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(475, 'Y'), 0.02213,
+            0.001d);
+  
+    assertEquals(hmm.getInsertEmissionProbability(1, 'C'), 0.012, 0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(14, 'H'), 0.02411,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(23, 'L'), 0.06764,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(90, 'D'), 0.0623, 0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(374, 'T'), 0.0623,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(470, 'P'), 0.0647,
+            0.001d);
+  
+    assertEquals(hmm.getStateTransitionProbability(2, 6), 0.3848, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(38, 3), 0.5382, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(305, 3), 0.2916, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(380, 0), 0.99, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(453, 1), 0.0066, 0.001d);
+  
+    assertEquals(hmm.getNodeMapPosition(3), 3);
+    assertEquals(hmm.getReferenceAnnotation(7), '-');
+    assertEquals(hmm.getConsensusResidue(23), 't');
+    assertEquals(hmm.getMaskedValue(30), '-');
+    assertEquals(hmm.getConsensusStructure(56), 'S');
+  
+    assertEquals(hmm.getNodeMapPosition(78), 136);
+    assertEquals(hmm.getReferenceAnnotation(93), '-');
+    assertEquals(hmm.getConsensusResidue(145), 'a');
+    assertEquals(hmm.getMaskedValue(183), '-');
+    assertEquals(hmm.getConsensusStructure(240), 'H');
+  }
+
+  /**
+   * Test that Jalview can parse an HMM file even with a bunch of 'mandatory'
+   * fields missing (including no MAP annotation or // terminator line)
+   * 
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParse_minimalFile() throws IOException
+  {
+    /*
+     * ALPH is absent, alphabet inferred from HMM header line
+     * Optional COMPO line is absent
+     * first line after HMM is a guide line for readability
+     * next line is BEGIN node insert emissions
+     * next line is BEGIN node transitions
+     * next line is first sequence node match emissions 1.1 1.2 1.3
+     * next line is first sequence node insert emissions 1.4 1.5 1.6
+     * last line is first sequence node transitions
+     */
+    //@formatter:off
+    String hmmData = 
+            "HMMER3\n" +
+            "HMM P M J\n" +
+            // both spec and parser require a line after the HMM line
+            " m->m m->i m->d i->m i->i d->m d->d\n" +
+            " 0.1 0.2 0.3\n" + 
+            " 0.4 0.5 0.6 0.7 0.8 0.9 0.95\n" + 
+            " 1 1.1 1.2 1.3 - - - - -\n" + 
+            " 1.4 1.5 1.6\n" + 
+            " 1.7 1.8 1.9 2.0 2.1 2.2 2.3\n" +
+            " 2 1.01 1.02 1.03 - - - - -\n" + 
+            " 1.04 1.05 1.06\n" + 
+            " 1.7 1.8 1.9 2.0 2.1 2.2 2.3\n";
+    //@formatter:on
+    HMMFile parser = new HMMFile(hmmData, DataSourceType.PASTE);
+    HiddenMarkovModel hmm = parser.getHMM();
+    assertNotNull(hmm);
+    assertEquals(hmm.getSymbols(), "PMJ");
+    // no LENG property: this should return node count excluding BEGIN node
+    assertEquals(hmm.getLength(), 2);
+
+    // node 1 (implicitly mapped to column 0)
+    double prob = hmm.getMatchEmissionProbability(0, 'p');
+    assertEquals(prob, Math.pow(Math.E, -1.1));
+    prob = hmm.getInsertEmissionProbability(0, 'J');
+    assertEquals(prob, Math.pow(Math.E, -1.6));
+
+    // node 2 (implicitly mapped to column 1)
+    prob = hmm.getMatchEmissionProbability(1, 'M');
+    assertEquals(prob, Math.pow(Math.E, -1.02));
+    prob = hmm.getInsertEmissionProbability(1, 'm');
+    assertEquals(prob, Math.pow(Math.E, -1.05));
+  }
+  
+  @Test(groups = "Functional")
+  public void testParseHeaderLines_amino() throws IOException
+  {
+    FileReader fr = new FileReader(
+            new File("test/jalview/io/test_fn3_hmm.txt"));
+    BufferedReader br = new BufferedReader(fr);
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    HMMFile testee = new HMMFile();
+    PA.setValue(testee, "hmm", hmm);
+    testee.parseHeaderLines(br);
+    br.close();
+    fr.close();
+  
+    assertEquals(hmm.getName(), "fn3");
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER), "PF00041.13");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
+            "Fibronectin type III domain");
+    assertEquals(hmm.getProperty(HMMFile.LENGTH), "86");
+    assertNull(hmm.getProperty(HMMFile.MAX_LENGTH));
+    assertEquals(hmm.getAlphabetType(), "amino");
+    assertFalse(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Fri Jun 20 08:22:31 2014");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "106");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES),
+            "11.415833");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3564431818");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD), "8.00 7.20");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "8.00 7.20");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "7.90 7.90");
+    assertEquals(hmm.getViterbi(), "-9.7737  0.71847");
+    assertEquals(hmm.getMSV(), "-9.4043  0.71847");
+    assertEquals(hmm.getForward(), "-3.8341  0.71847");
+  }
+  
+  @Test(groups = "Functional")
+  public void testParseHeaderLines_dna() throws IOException
+  {
+    FileReader fr = new FileReader(
+            new File("test/jalview/io/test_MADE1_hmm.txt"));
+    BufferedReader br = new BufferedReader(fr);
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    HMMFile testee = new HMMFile();
+    PA.setValue(testee, "hmm", hmm);
+    testee.parseHeaderLines(br);
+    br.close();
+    fr.close();
+  
+    assertEquals(hmm.getName(), "MADE1");
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER),
+            "DF0000629.2");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
+            "MADE1 (MAriner Derived Element 1), a TcMar-Mariner DNA transposon");
+    assertEquals(hmm.getProperty(HMMFile.LENGTH), "80");
+    assertEquals(hmm.getProperty(HMMFile.MAX_LENGTH), "426");
+    assertEquals(hmm.getAlphabetType(), "DNA");
+    assertTrue(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertFalse(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Tue Feb 19 20:33:41 2013");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "1997");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES), "3.911818");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3015610723");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD),
+            "2.324 4.234");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "2.343 1.212");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "2.354 5.456");
+    assertEquals(hmm.getViterbi(), "-9.3632  0.71858");
+    assertEquals(hmm.getMSV(), "-8.5786  0.71858");
+    assertEquals(hmm.getForward(), "-3.4823  0.71858");
+  }
+  
+  @Test(groups = "Functional")
+  public void testFillList() throws IOException
+  {
+    Scanner scanner1 = new Scanner("1.3 2.4 5.3 3.9 9.8 4.7 4.3 2.3 6.9");
+    ArrayList<Double> filledArray = new ArrayList<>();
+  
+    filledArray.add(0.27253);
+    filledArray.add(0.0907);
+    filledArray.add(0.00499);
+    filledArray.add(0.02024);
+    filledArray.add(0.00005);
+    filledArray.add(0.00909);
+    filledArray.add(0.01357);
+    filledArray.add(0.10026);
+    filledArray.add(0.001);
+  
+    double[] testList = HMMFile.parseDoubles(scanner1, 9);
+
+    for (int i = 0; i < 9; i++)
+    {
+      assertEquals(testList[i], filledArray.get(i), 0.001d);
+    }
+
+    filledArray.clear();
+    scanner1.close();
+  
+    Scanner scanner2 = new Scanner(
+            "1.346 5.554 35.345 5.64 1.4");
+    filledArray.add(0.2603);
+    filledArray.add(0.00387);
+    filledArray.add(0d);
+    filledArray.add(0.00355);
+    filledArray.add(0.2466);
+  
+    testList = HMMFile.parseDoubles(scanner2, 5);
+
+    for (int i = 0; i < 5; i++)
+    {
+      assertEquals(testList[i], filledArray.get(i), 0.001d);
+    }
+  }
+  
+  @Test(groups = "Functional")
+  public void testParseModel() throws IOException
+  {
+    FileReader fr = new FileReader(
+            new File("test/jalview/io/test_MADE1_hmm.txt"));
+    BufferedReader br = new BufferedReader(fr);
+    HiddenMarkovModel testHMM = new HiddenMarkovModel();
+    String line = null;
+    do
+    {
+      line = br.readLine(); // skip header lines up to HMM plus one
+    } while (!line.startsWith("HMM "));
+    br.readLine();
+
+    made1.parseModel(br);
+    testHMM = made1.getHMM();
+
+    br.close();
+    fr.close();
+  
+    assertEquals(testHMM.getMatchEmissionProbability(1, 'C'), 0.09267,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(25, 'G'), 0.07327,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(1092, 'C'), 0.04184,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(1107, 'G'), 0.07,
+            0.001d);
+  
+    assertEquals(testHMM.getInsertEmissionProbability(0, 'G'), 0.25,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(247, 'T'), 0.2776,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(1096, 'T'), 0.25,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(1111, 'T'), 0.25,
+            0.001d);
+
+    assertEquals(testHMM.getStateTransitionProbability(1, 0), 0.9634,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(5, 1), 0.0203,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(14, 3), 0.2515,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(65, 4), 0.78808,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(1080, 2), 0.01845,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(1111, 6),
+            Double.NEGATIVE_INFINITY);
+  }
+  
+  @Test(groups = "Functional")
+  public void testParseAnnotations()
+  {
+    HMMFile testFile = new HMMFile();
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    PA.setValue(testFile, "hmm", hmm);
+
+    List<HMMNode> nodes = new ArrayList<>();
+    nodes.add(new HMMNode()); // BEGIN node
+  
+    hmm.setProperty(HMMFile.CONSENSUS_RESIDUE, "yes");
+    hmm.setProperty(HMMFile.MAP, "yes");
+    hmm.setProperty(HMMFile.REFERENCE_ANNOTATION, "yes");
+    hmm.setProperty(HMMFile.CONSENSUS_STRUCTURE, "yes");
+    hmm.setProperty(HMMFile.MASKED_VALUE, "yes");
+    Scanner scanner = new Scanner("1345 t t t t");
+    HMMNode node = new HMMNode();
+    nodes.add(node);
+    testFile.parseAnnotations(scanner, node);
+  
+    hmm.setProperty(HMMFile.CONSENSUS_RESIDUE, "yes");
+    hmm.setProperty(HMMFile.MAP, "no");
+    hmm.setProperty(HMMFile.REFERENCE_ANNOTATION, "yes");
+    hmm.setProperty(HMMFile.CONSENSUS_STRUCTURE, "no");
+    hmm.setProperty(HMMFile.MASKED_VALUE, "no");
+    Scanner scanner2 = new Scanner("- y x - -");
+    node = new HMMNode();
+    nodes.add(node);
+    testFile.parseAnnotations(scanner2, node);
+
+    hmm.setNodes(nodes);
+
+    assertEquals(hmm.getNodeMapPosition(1), 1345);
+    assertEquals(hmm.getConsensusResidue(1), 't');
+    assertEquals(hmm.getReferenceAnnotation(1), 't');
+    assertEquals(hmm.getMaskedValue(1), 't');
+    assertEquals(hmm.getConsensusStructure(1), 't');
+  
+    scanner.close();
+  }
+  
+  /**
+   * tests to see if file produced by the output matches the file from the input
+   * 
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testPrint_roundTrip() throws IOException
+  {
+    String output = pKinase.print();
+    HMMFile pKinaseClone = new HMMFile(
+            new FileParse(output, DataSourceType.PASTE));
+    HiddenMarkovModel pKinaseHMM = pKinase.getHMM();
+    HiddenMarkovModel pKinaseCloneHMM = pKinaseClone.getHMM();
+  
+    checkModelsMatch(pKinaseHMM, pKinaseCloneHMM);
+  }
+
+  /**
+   * A helper method to check two HMM models have the same values
+   * 
+   * @param model1
+   * @param model2
+   */
+  protected void checkModelsMatch(HiddenMarkovModel model1,
+          HiddenMarkovModel model2)
+  {
+    assertEquals(model1.getLength(), model2.getLength());
+
+    for (int i = 0; i < model1.getLength(); i++)
+    {
+      String msg = "For Node" + i;
+      assertEquals(model1.getNode(i).getMatchEmissions(),
+              model2.getNode(i).getMatchEmissions(), msg);
+      assertEquals(model1.getNode(i).getInsertEmissions(),
+              model2.getNode(i).getInsertEmissions(), msg);
+      assertEquals(model1.getNode(i).getStateTransitions(),
+              model2.getNode(i).getStateTransitions(), msg);
+  
+      if (i > 0)
+      {
+        assertEquals(model1.getNodeMapPosition(i),
+                model2.getNodeMapPosition(i), msg);
+        assertEquals(model1.getReferenceAnnotation(i),
+                model2.getReferenceAnnotation(i), msg);
+        assertEquals(model1.getConsensusResidue(i),
+                model2.getConsensusResidue(i), msg);
+      }
+    }
+  }
+  
+  @Test(groups = "Functional")
+  public void testAppendProperties() throws FileNotFoundException
+  {
+    StringBuilder sb = new StringBuilder();
+    fn3.appendProperties(sb);
+
+    Scanner testScanner = new Scanner(sb.toString());
+  
+    String[] expected = new String[] { "HMMER3/f [3.1b1 | May 2013]",
+        "NAME  fn3", "ACC   PF00041.13",
+        "DESC  Fibronectin type III domain", "LENG  86", "ALPH  amino",
+        "RF    no", "MM    no", "CONS  yes", "CS    yes", "MAP   yes",
+        "DATE  Fri Jun 20 08:22:31 2014", "NSEQ  106", "EFFN  11.415833",
+        "CKSUM 3564431818", "GA    8.00 7.20", "TC    8.00 7.20",
+        "NC    7.90 7.90", "STATS LOCAL MSV       -9.4043  0.71847",
+        "STATS LOCAL VITERBI   -9.7737  0.71847",
+        "STATS LOCAL FORWARD   -3.8341  0.71847" };
+  
+    for (String value : expected)
+    {
+      assertEquals(testScanner.nextLine(), value);
+    }
+  
+    testScanner.close();
+  }
+  
+  @Test(groups = "Functional")
+  public void testAppendModelAsString() throws FileNotFoundException
+  {
+    StringBuilder sb = new StringBuilder();
+    fn3.appendModelAsString(sb);
+    String string = sb.toString();
+
+    assertEquals(findValue(2, 2, 2, string), "4.42225");
+    assertEquals(findValue(12, 14, 1, string), "2.79307");
+    assertEquals(findValue(6, 24, 3, string), "0.48576");
+    assertEquals(findValue(19, 33, 2, string), "4.58477");
+    assertEquals(findValue(20, 64, 2, string), "3.61505");
+    assertEquals(findValue(3, 72, 3, string), "6.81068");
+    assertEquals(findValue(10, 80, 2, string), "2.69355");
+    assertEquals(findValue(16, 65, 1, string), "2.81003");
+    assertEquals(findValue(14, 3, 1, string), "2.69012");
+    assertEquals(findValue(11, 32, 1, string), "4.34805");
+  }
+  
+  /**
+   * A helper method to find a token in the model string
+   * 
+   * @param symbolIndex
+   *          index of symbol being searched. First symbol has index 1.
+   * @param nodeIndex
+   *          index of node being searched. Begin node has index 0. First node
+   *          has index 1.
+   * @param line
+   *          index of line being searched in node. First line has index 1.
+   * @param model
+   *          string model being searched
+   * @return value at specified position
+   */
+  private String findValue(int symbolIndex, int nodeIndex, int line,
+          String model)
+  {
+    String value = "";
+    Scanner scanner = new Scanner(model);
+    scanner.nextLine();
+    scanner.nextLine();
+  
+    for (int lineIndex = 0; lineIndex < line - 1; lineIndex++)
+    {
+      scanner.nextLine();
+    }
+    for (int node = 0; node < nodeIndex; node++)
+    {
+      scanner.nextLine();
+      scanner.nextLine();
+      scanner.nextLine();
+    }
+  
+    for (int symbol = 0; symbol < symbolIndex; symbol++)
+    {
+      value = scanner.next();
+      if ("COMPO".equals(value))
+      {
+        scanner.next();
+      }
+      else if (value.length() < 7)
+      {
+        scanner.next();
+      }
+    }
+    scanner.close();
+    return value;
+  }
+}
+
index dde23e6..10a4dc3 100644 (file)
@@ -71,16 +71,16 @@ public class Jalview2xmlBase
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @BeforeTest(alwaysRun = true)
   public static void clearDesktop()
   {
-    if (Desktop.instance != null && Desktop.getFrames() != null
+    if (Desktop.getInstance() != null && Desktop.getFrames() != null
             && Desktop.getFrames().length > 0)
     {
-      Desktop.instance.closeAll_actionPerformed(null);
+      Desktop.getInstance().closeAll_actionPerformed(null);
     }
   }
 
index 1d138c0..d72e677 100644 (file)
@@ -64,7 +64,7 @@ public class JalviewExportPropertiesTests
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
 
   }
 
index bb5d1c4..270de2d 100644 (file)
@@ -62,22 +62,22 @@ public class SequenceAnnotationReportTest
     SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
     StringBuilder sb = new StringBuilder();
     sb.append("123456");
-    SequenceFeature sf = new SequenceFeature("disulfide bond", "desc", 1, 3,
-            1.2f, "group");
+    SequenceFeature sf = new SequenceFeature("disulfide bond", "desc", 1,
+            3, 1.2f, "group");
 
     // residuePos == 2 does not match start or end of feature, nothing done:
     sar.appendFeature(sb, 2, null, sf, null, 0);
     assertEquals("123456", sb.toString());
 
-    // residuePos == 1 matches start of feature, text appended (but no <br/>)
+    // residuePos == 1 matches start of feature, text appended (but no <br>)
     // feature score is not included
     sar.appendFeature(sb, 1, null, sf, null, 0);
     assertEquals("123456disulfide bond 1:3", sb.toString());
 
     // residuePos == 3 matches end of feature, text appended
-    // <br/> is prefixed once sb.length() > 6
+    // <br> is prefixed once sb.length() > 6
     sar.appendFeature(sb, 3, null, sf, null, 0);
-    assertEquals("123456disulfide bond 1:3<br/>disulfide bond 1:3",
+    assertEquals("123456disulfide bond 1:3<br>disulfide bond 1:3",
             sb.toString());
   }
 
@@ -105,12 +105,8 @@ public class SequenceAnnotationReportTest
     sfl.add(sf);
     sfl.add(sf);
     sfl.add(sf);
-    int n = sar.appendFeatures(sb, 1, sfl, new FeatureRenderer(null), 200); // text
-                                                                            // should
-                                                                            // terminate
-                                                                            // before
-                                                                            // 200
-                                                                            // characters
+    int n = sar.appendFeatures(sb, 1, sfl,
+            new FeatureRenderer(null), 200); // text should terminate before 200 characters
     String s = sb.toString();
     assertTrue(s.length() < 200);
     assertEquals(n, 7); // should be 7 features left over
@@ -151,8 +147,8 @@ public class SequenceAnnotationReportTest
      */
     minmax.put("METAL", new float[][] { { 0f, 1f }, null });
     sar.appendFeature(sb, 1, fr, sf, null, 0);
-    // <br/> is appended to a buffer > 6 in length
-    assertEquals("METAL 1 3; Fe2-S<br/>METAL 1 3; Fe2-S Score=1.3",
+    // <br> is appended to a buffer > 6 in length
+    assertEquals("METAL 1 3; Fe2-S<br>METAL 1 3; Fe2-S Score=1.3",
             sb.toString());
 
     /*
@@ -332,7 +328,7 @@ public class SequenceAnnotationReportTest
     expected = "<i>SeqDesc\n" + "\n"
             + "<br/>Metal ; Desc<br/>Type1 ; Nonpos</i>";
     assertEquals(expected, sb.toString());
-
+    
     /*
      * 'linkonly' features are ignored; this is obsolete, as linkonly
      * is only set by DasSequenceFetcher, and DAS is history
@@ -412,7 +408,7 @@ public class SequenceAnnotationReportTest
   {
     SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
     StringBuilder sb = new StringBuilder();
-
+  
     SequenceI seq = new Sequence("s1", "ABC");
 
     int maxSources = (int) PA.getValue(sar, "MAX_SOURCES");
@@ -420,13 +416,13 @@ public class SequenceAnnotationReportTest
     {
       seq.addDBRef(new DBRefEntry("PDB" + i, "0", "3iu1"));
     }
-
+    
     int maxRefs = (int) PA.getValue(sar, "MAX_REFS_PER_SOURCE");
     for (int i = 0; i <= maxRefs; i++)
     {
       seq.addDBRef(new DBRefEntry("Uniprot", "0", "P3041" + i));
     }
-
+  
     sar.createSequenceAnnotationReport(sb, seq, true, true, null, true);
     String report = sb.toString();
     assertTrue(report.startsWith("<i>\n" + "<br/>\n" + "UNIPROT P30410,\n"
index b1995ab..6849351 100644 (file)
@@ -384,10 +384,11 @@ public class StockholmFileTest
                                   && seq_new[in]
                                           .getSequenceFeatures() != null));
           // compare sequence features
-          if (seq_original[i].getSequenceFeatures() != null
+          if (!ignoreFeatures
+                  && seq_original[i].getSequenceFeatures() != null
                   && seq_new[in].getSequenceFeatures() != null)
           {
-            System.out.println("There are feature!!!");
+            System.out.println("Checking feature equivalence.");
             sequenceFeatures_original = seq_original[i]
                     .getSequenceFeatures();
             sequenceFeatures_new = seq_new[in].getSequenceFeatures();
diff --git a/test/jalview/io/test_MADE1_hmm.txt b/test/jalview/io/test_MADE1_hmm.txt
new file mode 100644 (file)
index 0000000..32231db
--- /dev/null
@@ -0,0 +1,268 @@
+HMMER3/f [3.1 | February 2013]
+NAME  MADE1
+ACC   DF0000629.2
+DESC  MADE1 (MAriner Derived Element 1), a TcMar-Mariner DNA transposon
+LENG  80
+MAXL  426
+ALPH  DNA
+RF    yes
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Tue Feb 19 20:33:41 2013
+NSEQ  1997
+EFFN  3.911818
+CKSUM 3015610723
+GA 2.324 4.234
+TC 2.343 1.212
+NC 2.354 5.456
+STATS LOCAL MSV       -8.5786  0.71858
+STATS LOCAL VITERBI   -9.3632  0.71858
+STATS LOCAL FORWARD   -3.4823  0.71858
+HMM          A        C        G        T   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   1.24257  1.59430  1.62906  1.16413
+          1.38629  1.38629  1.38629  1.38629
+          0.03960  3.94183  3.94183  1.46634  0.26236  0.00000        *
+      1   2.69765  2.44396  2.81521  0.24089      1 t x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03960  3.94183  3.94183  1.46634  0.26236  1.09861  0.40547
+      2   2.72939  2.37873  2.85832  0.24244      2 t x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03725  4.00179  4.00179  1.46634  0.26236  1.09861  0.40547
+      3   0.16099  3.16370  2.87328  2.99734      3 a x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03604  4.03416  4.03416  1.46634  0.26236  1.09861  0.40547
+      4   1.98862  2.42132  0.42649  2.10770      4 g x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03539  4.05203  4.05203  1.46634  0.26236  1.09861  0.40547
+      5   1.96369  2.69532  0.36534  2.32099      5 g x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03764  4.06427  3.92372  1.46634  0.26236  1.09861  0.40547
+      6   2.56994  2.11239  2.71946  0.30571      6 t x - -
+          1.37159  1.41129  1.39124  1.37159
+          0.03806  3.89715  4.07214  1.50442  0.25122  1.00714  0.45454
+      7   2.58388  2.10353  2.64646  0.31253     12 t x - -
+          1.38764  1.38524  1.38764  1.38465
+          0.03494  4.03864  4.09125  1.40070  0.28293  1.09237  0.40860
+      8   2.18552  2.70201  0.28821  2.64645     14 g x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03628  4.09157  3.96779  1.46634  0.26236  1.09861  0.40547
+      9   2.16916  2.82142  0.28427  2.60854     15 g x - -
+          1.38091  1.39033  1.38365  1.39033
+          0.03566  4.00237  4.08886  1.38021  0.28972  1.01958  0.44745
+     10   2.45517  2.15232  2.42886  0.34277     18 t x - -
+          1.39065  1.39065  1.39065  1.37335
+          0.03536  4.01212  4.09576  1.39554  0.28462  1.09775  0.40589
+     11   2.10260  2.95484  0.28160  2.64222     21 g x - -
+          1.36740  1.40555  1.40555  1.36740
+          0.03843  3.92069  4.02468  1.44733  0.26814  1.09856  0.40549
+     12   2.54740  0.30185  2.61355  2.21647     26 c x - -
+          1.38748  1.38276  1.38748  1.38748
+          0.03457  4.05446  4.09623  1.40847  0.28040  1.05496  0.42803
+     13   0.28443  2.72003  2.32214  2.48149     28 a x - -
+          1.38740  1.38740  1.38298  1.38740
+          0.03441  4.05976  4.10001  1.41198  0.27926  1.09780  0.40587
+     14   0.29412  2.55413  2.49679  2.35701     30 a x - -
+          1.38194  1.39067  1.38194  1.39067
+          0.03505  4.02482  4.10005  1.39522  0.28473  1.09929  0.40512
+     15   0.18837  2.99710  2.82270  2.77556     33 a x - -
+          1.39015  1.39472  1.37503  1.38539
+          0.03725  3.97815  4.02618  1.37955  0.28994  1.10102  0.40426
+     16   0.50816  2.05151  2.22111  1.82407     37 a x - -
+          1.36727  1.38730  1.39683  1.39405
+          0.04830  3.89881  3.61610  1.29026  0.32186  1.05306  0.42905
+     17   2.11260  2.73141  0.29747  2.64152     41 g x - -
+          1.36913  1.40376  1.40376  1.36913
+          0.03705  3.93681  4.08299  1.44872  0.26771  1.07479  0.41759
+     18   2.24459  1.90539  2.34054  0.43234     46 t x - -
+          1.33632  1.42493  1.39937  1.38665
+          0.04427  3.64574  4.06297  1.70501  0.20061  1.21309  0.35279
+     19   0.44322  2.17202  2.18055  2.03175     57 a x - -
+          1.41047  1.41471  1.36338  1.35797
+          0.03970  3.81957  4.07540  1.65588  0.21186  1.22788  0.34660
+     20   0.33340  2.42691  2.40824  2.25160     66 a x - -
+          1.29389  1.44615  1.37917  1.43324
+          0.04223  3.70146  4.09459  1.55158  0.23815  1.05880  0.42598
+     21   2.50563  1.98543  2.69601  0.33746     74 t x - -
+          1.39462  1.39462  1.42862  1.32990
+          0.04184  3.80216  3.98177  1.80466  0.17976  1.00279  0.45705
+     22   2.54484  1.97505  2.66483  0.33806     84 t x - -
+          1.39134  1.39489  1.38662  1.37246
+          0.03877  3.97504  3.95038  1.37620  0.29107  1.13932  0.38572
+     23   2.10159  2.83856  0.29282  2.61635     88 g x - -
+          1.39682  1.39682  1.35536  1.39682
+          0.05046  3.75402  3.65808  1.08330  0.41321  1.13019  0.39004
+     24   2.25298  0.61854  2.50691  1.29221     90 c x - -
+          1.35803  1.49605  1.46737  1.24379
+          0.06091  3.28322  3.83564  1.89752  0.16245  1.28788  0.32276
+     25   1.27819  2.23285  0.76242  1.91259    106 g x - -
+          1.29024  1.67349  1.68279  1.04597
+          0.05752  3.44263  3.73311  2.58671  0.07825  1.26818  0.33037
+     26   1.86925  2.58352  0.39466  2.33986    131 g x - -
+          1.31084  1.49412  1.46666  1.29002
+          0.04698  3.54257  4.07715  2.25245  0.11109  0.86163  0.54900
+     27   2.38297  1.93394  2.39162  0.39800    151 t x - -
+          1.33582  1.47359  1.44163  1.30411
+          0.04951  3.48445  4.03783  2.15951  0.12260  1.21681  0.35122
+     28   2.41717  2.17810  2.62774  0.32113    170 t x - -
+          1.36805  1.48060  1.37439  1.32840
+          0.04849  3.50958  4.05014  2.58370  0.07850  1.22399  0.34822
+     29   2.57764  2.35132  2.56552  0.28512    194 t x - -
+          1.43829  1.43458  1.24787  1.43829
+          0.04667  3.56670  4.05428  2.49706  0.08591  1.23744  0.34267
+     30   2.47248  2.07688  2.62257  0.33172    215 t x - -
+          1.25120  1.52623  1.70635  1.15531
+          0.08932  3.31524  3.01336  2.81842  0.06156  1.22909  0.34610
+     31   2.25937  2.13157  2.02027  0.43957    248 t x - -
+          1.18172  1.43522  1.72841  1.28150
+          0.07936  2.93117  3.77395  2.46269  0.08906  0.60457  0.79034
+     32   2.04508  2.84981  0.30490  2.58263    280 g x - -
+          1.17665  1.66785  1.66218  1.16056
+          0.05998  3.23615  3.96853  2.83684  0.06040  1.01952  0.44749
+     33   2.45103  0.38098  2.56776  1.87147    317 c x - -
+          1.24153  1.52524  1.60663  1.22783
+          0.05538  3.39046  3.90294  2.73920  0.06680  1.18729  0.36391
+     34   2.22082  0.36258  2.75077  2.02704    347 c x - -
+          1.15008  1.62014  1.86511  1.10673
+          0.06086  3.18178  4.04341  2.94504  0.05403  1.25991  0.33363
+     35   0.27033  2.66664  2.52541  2.43767    388 a x - -
+          1.24951  1.47565  1.41392  1.42074
+          0.07123  3.00373  3.95552  3.13655  0.04440  1.28173  0.32512
+     36   2.83107  2.41670  2.97197  0.22235    439 t x - -
+          1.37071  1.57683  1.38637  1.23972
+          0.05293  3.45216  3.91807  2.54402  0.08181  1.14651  0.38235
+     37   2.52322  2.25084  2.45909  0.31611    465 t x - -
+          1.26335  1.55077  1.59008  1.19965
+          0.07504  3.13329  3.55006  3.08962  0.04659  1.13108  0.38962
+     38   0.45807  2.30687  1.98940  2.03143    512 a x - -
+          1.15472  1.67511  1.53797  1.26320
+          0.09820  3.13076  2.99876  2.79197  0.06326  1.39915  0.28343
+     39   2.37471  0.42180  2.44763  1.80427    550 c x - -
+          1.23785  1.49058  1.48364  1.35502
+          0.06081  3.19472  4.01643  2.41851  0.09327  0.94671  0.49105
+     40   2.32826  1.95481  2.36781  0.40458    578 t x - -
+          1.36586  1.46001  1.43000  1.29720
+          0.05257  3.39673  4.03256  1.84862  0.17133  1.40979  0.27997
+     41   2.68669  2.13935  2.81520  0.28200    592 t x - -
+          1.34965  1.42793  1.45781  1.31633
+          0.04735  3.57826  3.99988  2.09424  0.13144  1.22129  0.34934
+     42   2.55904  2.16444  2.70859  0.29952    609 t x - -
+          1.12072  1.61936  1.63578  1.26895
+          0.07346  3.25910  3.42962  2.85641  0.05919  1.38363  0.28857
+     43   1.99923  1.61027  2.26343  0.57851    646 t x - -
+          1.32290  1.58747  1.61095  1.11018
+          0.06656  3.08568  3.97944  2.44774  0.09046  0.75593  0.63407
+     44   0.23887  2.79899  2.55209  2.60783    675 a x - -
+          1.18557  1.50323  1.59070  1.31590
+          0.05597  3.38637  3.88222  2.46900  0.08847  1.27945  0.32599
+     45   0.29593  2.53488  2.53903  2.32335    701 a x - -
+          1.08710  1.54222  1.59276  1.40430
+          0.07539  2.94521  3.91062  1.91623  0.15918  1.22327  0.34852
+     46   2.58352  2.40524  2.76700  0.25955    725 t x - -
+          1.19685  1.58503  1.74852  1.14293
+          0.06124  3.18279  4.02089  2.82961  0.06085  1.05474  0.42814
+     47   2.13251  2.88788  0.29508  2.50964    764 g x - -
+          1.20891  1.55463  1.68206  1.19000
+          0.06526  3.12574  3.94910  2.41448  0.09367  1.10396  0.40280
+     48   2.23841  2.99164  0.25118  2.72900    792 g x - -
+          1.26330  1.55339  1.52606  1.24355
+          0.05464  3.34968  4.01313  2.78872  0.06347  1.15133  0.38012
+     49   2.57533  0.32900  2.64632  2.01501    824 c x - -
+          1.35118  1.39828  1.40141  1.39516
+          0.04340  3.79297  3.91506  1.59549  0.22666  1.20075  0.35806
+     50   0.46433  2.04127  2.23437  2.00605    833 a x - -
+          1.23062  1.36903  1.62282  1.36182
+          0.05764  3.31530  3.92762  2.28791  0.10700  1.07910  0.41536
+     51   0.27513  2.77017  2.28518  2.57549    853 a x - -
+          1.27958  1.58726  1.46109  1.25394
+          0.05750  3.30072  3.96214  2.60776  0.07656  1.25708  0.33475
+     52   0.20149  2.86434  2.84551  2.69770    883 a x - -
+          1.23645  1.62259  1.71174  1.10368
+          0.05756  3.26729  4.02702  2.54508  0.08172  1.27391  0.32814
+     53   0.26982  2.65833  2.50477  2.46835    911 a x - -
+          1.36005  1.50358  1.48100  1.22550
+          0.06921  3.37553  3.42118  2.36646  0.09851  1.27560  0.32748
+     54   0.40022  2.19284  2.22687  2.20396    934 a x - -
+          1.12070  1.60472  1.53213  1.35895
+          0.05523  3.36752  3.94966  2.42917  0.09224  0.84774  0.55928
+     55   2.11356  0.46400  2.46442  1.79955    960 c x - -
+          1.23932  1.35913  1.50478  1.46331
+          0.05187  3.47055  3.94022  2.35854  0.09933  1.12102  0.39445
+     56   1.85868  0.79440  2.22069  1.25971    983 c x - -
+          1.21951  1.50212  1.51138  1.34185
+          0.06404  3.29054  3.69705  1.75742  0.18933  1.18410  0.36532
+     57   1.33272  2.32720  0.71452  1.90215    999 g x - -
+          1.12229  1.49343  1.56653  1.42255
+          0.04920  3.46654  4.08749  2.17995  0.11996  1.31769  0.31164
+     58   2.48337  0.43652  2.46331  1.68683   1017 c x - -
+          1.34704  1.55461  1.38112  1.28222
+          0.04823  3.61532  3.90311  2.20911  0.11631  1.00864  0.45368
+     59   0.41659  2.44509  1.93972  2.20507   1034 a x - -
+          1.38198  1.38198  1.39194  1.38932
+          0.03641  3.98130  4.06929  1.35873  0.29704  1.31330  0.31325
+     60   0.41612  2.39160  1.97116  2.21075   1037 a x - -
+          1.03649  1.46430  1.57421  1.57557
+          0.04769  3.52580  4.06641  2.32461  0.10294  0.84329  0.56263
+     61   2.66264  2.12302  2.82746  0.28581   1056 t x - -
+          1.36925  1.39635  1.38930  1.39048
+          0.04097  3.97400  3.84718  1.39433  0.28502  1.12205  0.39395
+     62   2.26510  2.13196  2.42551  0.37231   1060 t x - -
+          1.37965  1.39147  1.39147  1.38264
+          0.04082  3.91610  3.90805  1.24613  0.33914  0.95192  0.48776
+     63   0.41244  2.25761  2.16787  2.12907   1062 a x - -
+          1.34515  1.41203  1.41203  1.37753
+          0.04054  3.77835  4.08203  1.30483  0.31638  1.11819  0.39582
+     64   2.51464  0.37905  2.62296  1.82008   1068 c x - -
+          1.39543  1.38753  1.39233  1.37008
+          0.03854  3.90584  4.03535  1.36573  0.29463  1.13682  0.38689
+     65   2.16380  2.11332  2.18714  0.42765   1073 t x - -
+          1.38764  1.38471  1.38519  1.38764
+          0.03575  4.05376  4.03073  1.40080  0.28289  1.03825  0.43707
+     66   2.79349  2.39141  2.87271  0.23478   1075 t x - -
+          1.37227  1.39101  1.39101  1.39101
+          0.03597  4.01447  4.05827  1.39017  0.28639  1.06429  0.42308
+     67   2.82488  2.47749  2.93179  0.21887   1078 t x - -
+          1.38141  1.39112  1.38915  1.38353
+          0.03661  3.99477  4.04370  1.35958  0.29675  1.13439  0.38804
+     68   2.77679  2.30433  2.90694  0.24425   1081 t x - -
+          1.37593  1.38989  1.45520  1.32825
+          0.04447  3.68736  3.99242  1.76176  0.18843  0.98580  0.46703
+     69   2.47698  3.17398  0.19595  2.95437   1093 g x - -
+          1.38264  1.38264  1.39734  1.38264
+          0.05358  3.96553  3.40487  1.40348  0.28202  1.03112  0.44100
+     70   2.84327  0.27906  2.97336  2.00890   1097 c x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03412  4.08811  4.08811  1.46634  0.26236  0.69006  0.69625
+     71   0.21870  2.83638  2.69251  2.65798   1098 a x - -
+          1.37446  1.37942  1.39640  1.39509
+          0.03670  3.93983  4.09935  1.41905  0.27700  1.10002  0.40476
+     72   2.35233  0.46085  2.23804  1.78715   1103 c x - -
+          1.38536  1.38781  1.38781  1.38421
+          0.03493  4.03822  4.09272  1.39310  0.28542  1.09638  0.40658
+     73   2.57111  0.32543  2.74124  1.98892   1105 c x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03381  4.09688  4.09688  1.46634  0.26236  1.09626  0.40664
+     74   0.27014  2.61416  2.53262  2.47636   1106 a x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03461  4.09267  4.05587  1.46634  0.26236  1.09748  0.40603
+     75   0.52873  2.16549  1.91736  1.90409   1107 a x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03426  4.08396  4.08396  1.46634  0.26236  1.07423  0.41788
+     76   2.33134  0.38082  2.65861  1.90055   1108 c x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03466  4.07266  4.07266  1.46634  0.26236  1.09861  0.40547
+     77   2.20588  0.45134  2.35553  1.84373   1109 c x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03550  4.04912  4.04912  1.46634  0.26236  1.09861  0.40547
+     78   2.69018  2.22054  2.82311  0.26898   1110 t x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.03711  4.00561  4.00561  1.46634  0.26236  1.09861  0.40547
+     79   0.16248  3.15867  2.86159  2.98963   1111 a x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.04048  3.92018  3.92018  1.46634  0.26236  1.09861  0.40547
+     80   0.17484  3.04770  2.86638  2.88183   1112 a x - -
+          1.38629  1.38629  1.38629  1.38629
+          0.02045  3.90014        *  1.46634  0.26236  0.00000        *
+//
diff --git a/test/jalview/io/test_PKinase_hmm.txt b/test/jalview/io/test_PKinase_hmm.txt
new file mode 100644 (file)
index 0000000..e1ad9f9
--- /dev/null
@@ -0,0 +1,806 @@
+HMMER3/e [3.0 | March 2010]
+NAME  Pkinase
+ACC   PF00069.17
+DESC  Protein kinase domain
+LENG  260
+ALPH  amino
+RF    no
+CONS  yes
+CS    yes
+MAP   yes
+DATE  Thu Jun 16 11:44:06 2011
+NSEQ  54
+EFFN  3.358521
+CKSUM 3106786190
+GA    70.30 70.30
+TC    70.30 70.30
+NC    70.20 70.20
+STATS LOCAL MSV      -10.7215  0.70254
+STATS LOCAL VITERBI  -11.6541  0.70254
+STATS LOCAL FORWARD   -5.2305  0.70254
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   2.60017  4.19886  2.94089  2.63789  3.35087  2.89119  3.48337  2.79435  2.60265  2.43454  3.62613  3.06133  3.41286  3.09693  2.94507  2.65650  2.87761  2.67871  4.54052  3.43274
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.00000        *
+      1   2.91704  4.31028  4.76351  3.63148  2.10912  3.47710  4.39734  2.27639  3.96619  1.99282  3.42844  4.19567  4.43771  4.11903  3.67403  3.22736  3.14918  2.35905  3.32515  1.82622      1 y - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      2   2.77204  4.20708  3.06476  1.97968  4.66603  3.66509  3.19858  3.37903  2.25347  3.34018  4.38833  2.97180  4.05804  2.36838  2.43173  2.67542  2.50268  2.83403  5.78758  4.38976      2 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      3   2.90076  4.04294  3.31590  2.80413  3.08157  3.87481  4.12616  2.18958  2.49735  1.97194  3.73714  3.32717  3.98753  3.23115  3.12315  2.99137  2.62008  2.29426  5.21478  3.75979      3 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      4   2.89383  4.85429  3.10311  2.63174  3.71383  2.56808  3.47232  2.82076  2.61844  1.98342  3.72320  3.05498  4.17347  3.05527  3.32194  2.85608  2.93671  2.18878  5.40057  3.87183      4 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      5   2.68261  5.39185  2.80437  1.87471  4.73638  2.79887  3.69591  4.22191  2.19487  3.69685  4.08896  3.12413  3.70490  2.48424  2.08916  2.53260  3.02309  3.43812  5.82480  4.41426      5 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      6   2.77253  4.06085  3.07881  2.53373  4.10647  3.71564  3.68370  2.64290  2.00036  2.72968  4.17370  3.24239  3.08607  2.77632  2.72820  2.69921  2.54335  2.71931  5.60386  4.26496      6 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      7   3.42930  4.74416  5.34765  4.76823  3.71832  4.71430  5.09461  1.33015  3.54417  1.12594  3.33523  4.84641  5.00427  4.73010  4.64986  4.05097  3.66327  1.83083  5.51142  4.37013      7 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      8   2.57460  4.94286  4.00732  3.93216  5.40516  0.41612  5.15134  4.86315  4.18766  4.52736  5.33508  4.05990  4.45245  3.74651  4.46171  3.15888  3.14590  4.15180  6.71159  5.52767      8 G - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+      9   2.30218  5.38172  3.03799  2.02026  4.18991  3.65799  3.84554  3.67215  2.28945  3.68495  4.42365  2.84227  3.73039  2.44555  2.39428  2.00278  2.95859  3.63127  5.81726  4.40923      9 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     10   4.54226  6.07621  5.39207  5.44513  6.34643  0.07137  6.34600  6.33790  5.73770  5.76452  6.83374  5.56171  5.46536  5.98137  5.73783  4.76037  5.08470  5.67502  7.03864  6.52156     10 G - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     11   2.06444  5.37337  2.91595  2.40063  4.70969  2.61181  3.84889  4.18948  2.46835  3.67503  4.41668  2.62917  4.05392  2.88235  2.66711  2.00495  2.47288  3.39649  5.81159  4.40596     11 s - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     12   2.90920  4.46494  4.12493  3.14570  1.52027  3.22992  4.24969  2.92840  3.46467  2.66529  3.12827  3.55640  4.33979  3.72214  3.72236  2.53517  2.98442  2.55125  5.06498  2.30159     12 f - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     13   2.21104  4.80722  4.64199  4.55509  5.54982  0.52550  5.49025  5.04875  4.70671  4.72740  5.47955  4.30215  4.43233  4.84666  4.86038  1.95116  3.45005  4.17507  6.87595  5.79039     13 G - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     14   2.71551  3.46186  3.01248  2.27453  4.55840  3.55853  3.50842  3.55064  2.03335  3.34882  4.31847  3.17065  4.07218  2.83960  2.83653  2.25762  2.26864  2.61469  5.72841  4.35037     14 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     15   3.17885  3.97005  5.63014  5.16553  4.35293  4.89782  5.77682  2.09398  5.06494  2.94083  4.14496  5.20428  5.30924  5.30763  5.20260  4.32158  3.61414  0.44769  6.26043  5.04118     15 V - - E
+          2.68625  4.42232  2.77526  2.73102  3.46361  2.40519  3.72501  3.29361  2.67747  2.69306  4.24696  2.90353  2.73746  3.18153  2.89807  2.37893  2.77526  2.98499  4.58484  3.61510
+          0.09563  2.43065  5.73865  0.67073  0.71608  0.48576  0.95510
+     16   2.91101  3.31342  4.36759  3.41772  2.45121  4.01227  4.31421  2.75922  2.81772  2.31733  3.50420  3.69862  4.38187  3.88257  3.14725  3.02772  3.14303  2.46483  3.37889  1.69905     18 y - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     17   2.88484  5.28431  3.03179  2.31849  4.58595  3.47769  3.54625  2.98124  1.69233  2.43413  4.04064  3.16353  4.06907  2.72555  2.58277  2.55274  3.04512  3.20501  5.74377  4.36100     19 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     18   1.57125  2.56460  3.38436  3.39201  3.66458  2.34190  4.20526  2.93932  3.32823  2.44127  3.26099  3.74334  4.30466  3.60817  3.63469  3.01628  2.92537  2.55308  5.13581  3.91882     20 a - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     19   2.89298  4.40558  3.44708  2.63051  3.77015  3.77747  2.87940  2.57515  2.33949  2.85128  3.20054  3.37379  4.16367  2.65910  2.52913  2.67954  2.59930  2.46709  5.42679  3.74337     21 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     20   2.88350  5.37811  2.42858  2.28192  4.71668  3.65945  2.35349  4.19788  2.20596  2.91798  4.42054  2.38258  4.05280  2.81490  2.60119  2.77457  3.11461  3.76528  3.43428  3.80493     22 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     21   2.59359  5.30144  3.00667  2.46268  4.60954  3.67285  3.22394  2.91749  2.13087  2.60378  4.35201  3.05590  3.51881  2.48537  2.35096  2.62551  3.00998  2.84793  5.75687  4.36947     23 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     22   2.64500  5.38003  2.46961  2.36386  4.71962  2.36153  3.71093  3.77963  2.19342  3.27291  4.42213  2.49968  3.44456  2.94396  2.94770  2.23767  2.90215  3.58800  5.81601  3.79868     24 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     23   2.71410  5.35453  2.26257  2.17828  4.68327  3.06138  3.85215  3.65600  2.51871  3.65284  4.39957  2.64619  4.05631  2.95399  2.98284  2.41102  1.94961  3.19379  5.79713  3.78693     25 t - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03260  5.01631  3.67114  0.61958  0.77255  0.48576  0.95510
+     24   2.77509  4.50579  3.06414  2.40741  4.68877  2.10468  3.84020  4.16638  2.52713  3.65577  4.39945  2.31826  2.82304  2.55347  2.45557  2.51717  3.10489  3.55245  5.79545  3.68618     26 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.13496  4.99383  2.12471  0.61958  0.77255  0.54753  0.86365
+     25   2.82390  4.23357  2.59579  2.09029  4.65095  3.47120  3.53571  3.50264  1.93281  3.24335  4.10745  2.98984  3.99409  2.41262  2.43858  2.60001  2.84781  3.57090  5.75206  3.98239     27 k - - E
+          2.68634  4.42166  2.77510  2.73101  3.46370  2.40521  3.72473  3.29370  2.67713  2.69347  4.24706  2.90350  2.73742  3.18145  2.89817  2.37895  2.77536  2.98516  4.58493  3.61485
+          0.21086  2.33296  2.37401  1.37928  0.29003  0.81455  0.58490
+     26   2.79249  4.86773  3.27912  2.48247  3.81836  3.65430  3.86742  2.36980  2.31612  2.40000  3.66176  2.98804  3.52938  2.45192  2.80383  2.80149  2.71760  2.93241  5.39419  3.83742     36 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01259  4.77670  5.49905  0.61958  0.77255  0.27298  1.43175
+     27   2.94684  4.40681  4.93000  4.33270  2.14562  4.19543  4.43742  2.60780  4.12995  2.51432  3.32273  4.32339  4.55150  4.25562  4.15094  3.50804  3.26683  1.12964  4.39266  2.23526     37 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     28   0.67405  4.13502  4.78133  4.26234  4.00859  4.13420  4.88775  2.58668  4.14876  2.87877  3.59530  4.37511  4.66004  4.38446  4.34251  3.50924  3.44597  2.20111  5.62436  4.43519     38 A - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     29   2.77559  4.05359  4.82280  4.21814  3.45081  3.88127  4.46447  1.69887  3.29990  2.06046  2.92956  4.25618  4.49083  4.18293  4.07725  3.28739  3.19210  1.31639  4.98894  3.80930     39 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     30   4.13385  6.06880  4.58555  3.84767  5.73274  4.53840  4.41573  5.06650  0.32256  4.37802  5.30885  4.14872  4.92009  3.55775  2.54913  4.11741  4.25754  4.75872  6.25921  5.22828     40 K - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     31   2.53136  3.93442  3.55191  2.54113  3.59001  3.81267  3.67691  2.09853  2.22431  2.85063  3.63116  3.45300  4.19661  2.87681  2.71089  2.96116  2.67441  2.46338  5.34241  3.76111     41 i - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     32   2.80227  4.30542  4.81704  3.71619  2.31247  3.45194  4.41496  1.85614  4.00698  1.68898  3.06826  4.22398  4.44809  4.15126  4.03957  3.11345  3.01936  1.82478  4.92889  3.75130     42 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     33   2.72320  4.19698  2.89173  2.30854  4.21536  3.66470  3.63903  3.54937  1.81020  3.33297  4.38970  2.50126  3.12497  2.85129  2.61476  2.47307  2.93549  3.43057  4.63863  3.86554     43 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     34   2.73748  5.16363  3.24304  2.62749  4.42406  3.39882  3.64889  3.08793  1.75481  2.64054  4.22767  2.81684  3.49630  2.92572  2.69253  2.71691  2.78362  2.91622  5.65055  3.09244     44 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     35   2.82342  4.21564  2.57688  2.18304  4.09635  3.66104  3.29269  4.17416  2.24176  3.24419  4.40775  3.13331  3.02670  2.66210  2.30119  2.38622  2.91913  2.84969  5.80393  4.13733     45 e - - H
+          2.68618  4.42225  2.77520  2.73124  3.46354  2.40513  3.72495  3.29354  2.67741  2.69355  4.24690  2.90347  2.73731  3.18147  2.89801  2.37887  2.77520  2.98519  4.58477  3.61503
+          0.02362  3.90596  5.73865  0.48782  0.95182  0.48576  0.95510
+     36   2.81479  5.38805  2.34307  2.08229  3.84722  3.00939  3.24618  4.21561  2.24819  3.24444  4.42924  2.80965  3.63231  2.74299  2.65208  2.41080  2.72103  3.26063  5.82196  4.16400     47 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     37   2.42659  5.32934  2.94543  1.98753  4.27035  3.66735  3.85733  2.97282  2.27697  3.20526  3.75542  2.69174  3.50027  2.79550  2.45131  2.66535  2.70832  3.28701  5.77800  4.07668     48 e - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     38   2.82479  5.36482  2.46403  2.36491  4.23121  3.19666  3.53460  3.47934  2.01766  2.87145  4.06028  2.53791  3.88358  2.73438  2.79605  2.39315  2.94316  3.30320  5.80464  3.67777     49 k - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     39   2.81259  3.82036  2.58997  2.16257  3.93187  3.50795  3.44913  3.32347  2.23760  3.42042  3.43188  3.03914  3.18139  2.98833  2.90722  1.97318  3.04237  3.44327  4.85883  4.01646     50 s - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04017  5.01631  3.41907  0.61958  0.77255  0.48576  0.95510
+     40   2.75150  4.68420  2.78864  2.20581  3.96394  3.14028  3.85474  3.62649  2.17804  2.84544  3.93254  2.66146  3.79370  2.72658  2.87564  2.43841  2.71937  3.23202  3.36778  4.34996     51 k - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01020  4.98634  5.70869  0.61958  0.77255  0.56699  0.83755
+     41   2.52028  4.29421  2.93844  2.26593  3.28616  3.42961  3.89113  3.40312  1.92589  2.89626  3.59280  3.04537  4.08209  2.42956  3.02333  2.89065  2.87512  2.78376  5.62895  3.97726     52 k - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.06456  4.98634  2.88790  0.61958  0.77255  0.56699  0.83755
+     42   2.52102  4.66200  2.60886  2.12080  3.90563  3.43164  3.41299  3.76759  2.19798  2.95231  4.37667  2.71584  3.31098  2.66775  2.46389  2.65499  2.89357  3.57073  4.74853  4.36793     53 e - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.06881  4.93254  2.82535  0.61958  0.77255  0.69302  0.69327
+     43   2.65676  5.27743  2.81592  2.16715  4.59834  3.44096  3.79920  2.82354  2.10889  3.57233  3.78816  2.93522  4.00237  2.20078  2.58862  2.72374  2.99283  3.18643  5.72482  3.30249     54 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04191  4.87514  3.39892  0.61958  0.77255  0.80592  0.59181
+     44   2.46416  5.14935  3.14274  2.35064  4.42785  3.46788  3.62138  3.06885  2.39724  2.90472  4.00413  2.77252  4.01153  2.56699  2.69346  2.37713  2.33258  3.34725  5.62407  3.30627     55 t - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.15381  4.84499  2.00472  0.61958  0.77255  0.85820  0.55151
+     45   2.15178  5.11690  2.80767  2.47016  4.06926  3.56450  3.25874  3.42510  2.38373  2.72748  4.17388  2.58132  3.95629  2.68997  2.59104  2.52019  2.89357  3.10461  5.58730  3.84420     56 a - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.06869  4.70471  2.85898  0.61958  0.77255  1.05523  0.42788
+     46   2.33893  4.38200  3.78128  3.08857  2.87706  3.76539  4.03303  2.50172  2.82237  2.26545  3.48958  3.56901  4.14175  3.13269  2.43377  3.02211  2.55318  2.18677  4.40164  3.75633     57 v - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.07407  4.65033  2.78326  0.61958  0.77255  1.03937  0.43646
+     47   2.66580  4.77769  3.25859  2.69691  3.69577  3.61140  3.56963  3.36841  2.45062  2.28390  3.86113  2.83101  3.99793  3.01640  2.00932  2.40384  2.97180  2.98514  3.81809  4.01106     58 r - - H
+          2.68588  4.42292  2.77574  2.73101  3.46421  2.40561  3.72562  3.29362  2.67747  2.69305  4.24679  2.90348  2.73776  3.18179  2.89693  2.37874  2.77432  2.98554  4.58544  3.61510
+          0.56084  0.85690  5.33966  1.67148  0.20822  0.22069  1.61932
+     48   3.87852  6.35819  2.88185  0.33512  5.75300  3.98086  4.66680  5.41085  3.73193  4.87592  5.81368  3.19190  4.67406  3.86930  4.28895  3.74624  4.21685  4.94226  6.88673  5.40485     78 E - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     49   2.34469  4.06185  4.67394  3.74635  3.19070  3.72385  4.38165  1.69529  3.89930  1.68521  3.30505  3.88179  4.42519  3.50953  3.71087  3.12279  3.14596  1.99339  4.94125  3.41486     79 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     50   2.41611  3.92039  3.06862  2.24055  4.69692  3.38839  3.50460  4.17440  2.17094  3.43336  4.01855  2.84870  4.05421  2.22409  2.42800  2.49697  2.93959  3.48161  5.80403  3.10747     80 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     51   2.65097  4.31309  4.79937  4.19096  3.25102  4.08746  4.41763  1.57396  3.99634  1.77576  3.18772  3.58572  4.45127  4.14513  4.03729  2.91921  3.15786  1.73265  4.93848  3.33785     81 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     52   2.71474  4.35776  4.49839  3.90546  3.26558  3.60358  3.11363  2.58541  3.76519  1.24138  2.56017  3.78382  4.40165  3.77674  3.90460  2.91080  2.88501  2.43218  4.97232  3.44306     82 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     53   2.21717  5.37509  3.13954  2.36108  4.71254  3.44569  3.50643  4.19289  1.90576  2.89362  4.41783  2.84264  3.50886  2.51670  2.27883  2.73413  2.79427  3.37900  5.81221  4.40629     83 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     54   2.88332  4.76187  3.15794  2.34306  4.64959  3.18467  2.98724  4.11774  2.20976  2.84622  3.53657  2.69640  4.06011  2.81953  2.22587  2.26090  2.67755  3.39248  5.77872  3.89328     84 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     55   2.92075  3.71086  4.82119  4.21031  2.91851  4.08375  4.41406  2.48059  4.00923  1.14255  2.77088  4.22517  4.44739  4.15194  3.51737  2.79440  3.04088  2.14784  4.92583  3.58797     85 l - - H
+          2.68619  4.42231  2.77526  2.73130  3.46360  2.40505  3.72501  3.29360  2.67737  2.69361  4.24696  2.90322  2.73746  3.18153  2.89797  2.37878  2.77526  2.98514  4.58483  3.61509
+          0.21093  1.81438  3.60337  0.15203  1.95873  0.48576  0.95510
+     56   2.87228  4.32697  2.43695  2.52023  4.72535  3.09966  3.42727  4.21076  2.24746  3.68599  4.14994  2.21640  3.70120  2.45856  2.42604  2.20075  2.84715  3.77128  5.81417  4.40372     87 s - - S
+          2.68626  4.42233  2.77498  2.73131  3.46362  2.40515  3.72479  3.29362  2.67733  2.69363  4.24698  2.90355  2.73719  3.18141  2.89809  2.37895  2.77498  2.98527  4.58485  3.61511
+          0.08622  2.53438  5.71435  0.90816  0.51628  0.44628  1.02166
+     57   2.95437  4.05540  4.02453  3.66262  4.73022  3.00582  0.63840  4.27369  3.61156  3.92435  4.76053  3.90244  4.38221  3.97389  3.93213  2.36608  3.39859  3.79517  6.08861  4.80370     91 H - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     58   2.74925  4.59154  2.64384  2.02057  4.69046  3.19970  3.85151  4.16644  2.39568  3.65893  3.99620  3.06676  1.73396  2.95270  2.98758  2.52836  2.96698  3.57815  5.80119  4.39902     92 p - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     59   2.77600  3.63123  3.60742  3.04389  2.88763  3.83093  3.22852  3.27052  2.82124  2.96220  3.83761  1.44485  4.21384  3.33393  3.26463  3.06163  2.84916  2.86372  5.30436  2.85576     93 n - - S
+          2.68619  4.42226  2.77521  2.73125  3.46355  2.40506  3.72496  3.29355  2.67742  2.69356  4.24691  2.90348  2.73741  3.18148  2.89802  2.37880  2.77521  2.98520  4.58478  3.61504
+          0.02736  3.73933  5.73865  0.64347  0.74542  0.48576  0.95510
+     60   3.43736  4.73250  5.40681  4.85032  3.21534  3.89737  5.21348  0.72821  4.70181  2.29095  3.77438  4.92025  5.08011  4.86610  4.77171  4.12622  3.68020  1.72131  5.65218  4.46593     96 i - - B
+          2.68620  4.42119  2.77522  2.73126  3.46356  2.40515  3.72497  3.29356  2.67743  2.69357  4.24692  2.90349  2.73742  3.18149  2.89780  2.37889  2.77522  2.98521  4.58479  3.61505
+          0.04171  3.27988  5.73865  0.65380  0.73410  0.48576  0.95510
+     61   3.22247  3.52386  5.20134  4.62057  3.74032  4.49428  4.89197  1.38422  4.44333  2.03444  3.40884  4.64781  4.83078  4.60265  4.48778  3.55308  3.01459  1.16992  5.37248  4.18666     99 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     62   2.72238  4.59459  3.13585  2.30027  4.72566  3.17722  3.67251  4.20868  2.00822  3.53187  4.42604  2.97500  3.44674  2.41134  2.06447  2.53711  2.32763  3.77297  5.81907  4.41084    100 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     63   2.75790  4.02501  4.89076  4.27684  2.25991  4.10991  4.44304  2.31340  4.06541  1.38378  2.56521  4.26823  4.47075  4.19670  4.07763  3.42134  2.72551  2.21249  4.93572  3.01728    101 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     64   2.79378  4.36122  4.48297  3.70404  2.84684  4.03133  3.55103  1.94759  2.82835  1.82248  3.32801  4.05424  4.39941  3.70850  3.13439  3.15759  3.14407  2.41334  4.25902  2.30786    102 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     65   2.47264  4.33075  2.07830  2.03157  4.71750  2.27093  3.18950  4.19892  2.58523  3.68152  4.42156  3.04070  4.05295  2.84140  2.67254  2.59104  3.11515  3.76615  5.81569  3.60292    103 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     66   2.10377  4.03530  4.82986  4.21790  2.76136  4.07923  4.41023  2.08081  4.01359  2.30827  2.78821  4.22543  4.44340  4.15406  4.03913  2.38166  2.60707  1.97272  3.19782  3.33258    104 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     67   2.82605  3.67894  3.38070  3.01266  1.82001  3.97118  3.82789  2.76491  3.48734  2.34143  3.24926  3.10725  3.78017  3.42990  3.73647  2.98277  3.13977  2.32864  3.87637  2.72935    105 f - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04987  5.01631  3.16967  0.61958  0.77255  0.48576  0.95510
+     68   2.55964  4.21441  3.10666  1.94329  4.49707  3.21758  3.46239  3.37098  2.39709  3.00488  3.51821  3.06808  4.06187  2.54621  2.66989  2.75991  2.68522  3.13970  5.68635  3.14043    106 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02094  4.97674  4.28127  0.61958  0.77255  0.42506  1.06054
+     69   2.80310  5.38415  2.22807  2.18295  4.72718  3.65266  3.36574  4.21171  2.46641  3.56699  4.42536  2.57848  3.46451  2.86899  2.36310  2.58224  2.17995  3.52075  5.81804  3.65548    107 t - - E
+          2.68640  4.42162  2.77535  2.73147  3.46354  2.40551  3.72458  3.29233  2.67716  2.69348  4.24762  2.90375  2.73699  3.18187  2.89837  2.37858  2.77480  2.98522  4.58549  3.61548
+          0.36809  1.18844  5.72853  1.68950  0.20409  0.46837  0.98355
+     70   2.88244  5.39287  2.47300  2.29715  3.79460  2.82884  3.28660  4.22374  2.09832  3.69810  4.43357  2.45444  3.07035  2.85151  2.47539  2.34133  3.01376  3.78345  5.82560  4.10620    126 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     71   2.57798  5.39101  2.06614  2.42374  4.73510  2.82383  3.84381  4.22040  2.28304  3.69587  4.43195  2.62257  3.38372  2.71683  2.73886  2.29050  2.66881  3.34289  5.82427  4.18691    127 d - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     72   2.58231  5.35788  2.77781  2.22432  4.08337  3.66211  2.49021  4.16401  2.37240  3.16748  4.40241  2.52484  4.05523  2.76334  2.63486  2.49164  2.89823  3.30761  4.95167  3.05734    128 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     73   2.78575  3.74303  4.78141  4.17260  2.95491  4.07322  4.40160  1.89067  3.97854  1.60353  2.95383  3.71592  3.11906  4.12798  3.50390  3.07790  2.85145  2.24111  4.92509  2.91282    129 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     74   2.58399  2.76389  4.59675  3.99881  3.19843  4.04871  3.62339  2.65148  3.84111  2.12752  3.45456  3.62454  3.46238  4.02369  3.94739  2.79096  3.14543  2.42948  3.13888  1.74690    130 y - - E
+          2.68624  4.42096  2.77500  2.73129  3.46360  2.40508  3.72501  3.29360  2.67747  2.69353  4.24696  2.90343  2.73746  3.18153  2.89807  2.37893  2.77510  2.98525  4.58483  3.61509
+          0.10354  2.49668  4.13492  0.66960  0.71726  0.48576  0.95510
+     75   3.07392  4.44555  4.97084  3.81418  2.87255  4.25097  4.57886  1.74564  4.16653  1.31678  2.12605  4.39109  4.59167  3.71495  4.19337  3.56380  3.30386  2.29390  5.05348  3.89648    133 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.52175  0.90012
+     76   2.86449  3.41378  5.01300  4.41506  3.07458  4.28568  4.64425  1.49634  4.22335  2.27773  3.54368  4.43380  4.63588  3.70581  4.25773  3.60458  3.30826  1.19505  5.13861  3.95920    134 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.46390  0.99109
+     77   2.91733  4.35063  4.55668  3.96112  2.40516  4.04680  4.36140  2.56068  3.81048  1.70108  1.79340  3.59899  4.41359  3.09405  3.93172  3.15547  2.48177  2.59902  4.96582  3.78095    135 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     78   2.76564  5.57030  2.50020  1.03018  4.90220  3.70297  3.44513  4.39514  2.75995  3.87044  4.62325  3.16505  3.19754  2.75935  3.27129  2.65802  3.28146  3.95557  6.00401  4.57374    136 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     79   2.81579  3.90369  4.68991  4.08721  2.27644  4.06399  3.63829  2.69790  3.58050  1.84024  3.31248  4.16081  4.42953  4.07816  3.60709  3.36667  3.01039  2.57256  4.40465  1.54393    137 y - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     80   2.45920  2.17262  4.69646  4.09287  3.22405  3.32199  3.21264  2.59887  3.91610  2.06244  2.25581  4.16309  3.61702  4.08102  3.98811  3.14866  3.14614  2.03766  4.93767  3.46778    138 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04171  5.01631  3.37486  0.61958  0.77255  0.48576  0.95510
+     81   2.70314  5.37371  2.37994  1.91497  4.71601  2.93053  3.83161  4.20005  2.46492  3.55263  3.57885  2.58870  2.59223  2.74552  2.71928  2.69862  3.03649  3.29150  5.80804  4.39872    139 e - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01022  4.98481  5.70716  0.61958  0.77255  0.57090  0.83245
+     82   2.41993  3.72287  3.12898  2.52046  4.44831  1.89639  3.49536  3.62837  2.38762  3.31281  3.53904  2.44913  3.42379  3.00780  3.12891  2.87912  2.75467  3.52812  5.65946  3.81984    140 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.10979  4.98481  2.33166  0.61958  0.77255  0.57090  0.83245
+     83   2.77350  5.21303  2.43943  2.62703  4.54309  1.39496  3.89257  3.99092  2.60267  3.54085  4.32260  3.04209  4.06226  3.01831  3.03242  2.64588  2.77651  3.04353  5.73236  4.35922    141 g - - -
+          2.68631  4.42210  2.77537  2.73126  3.46359  2.40522  3.72475  3.29338  2.67758  2.69250  4.24657  2.90377  2.73770  3.18148  2.89796  2.37902  2.77541  2.98478  4.58507  3.61496
+          0.21230  1.67338  5.60865  1.39945  0.28334  0.33567  1.25476
+     84   2.88583  5.39491  1.79381  2.06461  4.73890  3.24821  3.51210  4.22442  2.47141  3.51906  4.43601  2.95989  3.57031  2.79057  2.82788  2.02605  2.91163  3.62254  5.82805  4.41723    147 d - - B
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     85   2.90986  4.42406  3.70533  3.66371  3.02577  3.99009  3.90749  2.78363  3.56278  1.02082  3.24022  3.34355  4.36146  3.52102  3.78389  3.13807  2.95820  2.60948  5.03043  3.48016    148 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     86   2.57337  4.10245  3.03950  2.95233  2.09915  3.53742  3.15959  3.37138  2.66519  2.44003  3.54971  3.10382  3.71714  3.06888  3.13958  2.44794  3.05039  3.11306  4.61969  3.07664    149 f - - H
+          2.68621  4.42228  2.77522  2.73126  3.46357  2.40516  3.72497  3.29357  2.67744  2.69358  4.24693  2.90350  2.73735  3.18149  2.89804  2.37890  2.77502  2.98521  4.58480  3.61458
+          0.24291  3.02648  1.78874  0.58545  0.81386  0.48576  0.95510
+     87   2.29362  5.30873  1.92419  2.32474  4.26868  3.43187  3.46788  4.12970  2.37357  3.61146  4.35100  2.92576  3.98105  2.69850  2.60197  2.47206  2.54407  3.69618  5.74493  3.96335    152 d - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01185  4.83724  5.55959  0.61958  0.77255  0.87096  0.54223
+     88   2.24696  4.24235  4.56569  3.96478  2.41045  3.97137  3.29432  2.27668  3.43922  2.04964  3.19406  4.05576  4.33743  3.97000  3.38896  3.27217  3.06100  2.23241  4.18296  2.20711    153 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01185  4.83724  5.55959  0.61958  0.77255  0.87096  0.54223
+     89   3.80243  5.01974  5.81731  5.27268  3.70245  5.26548  5.70409  1.30084  5.15744  0.98923  3.04180  5.42350  5.39649  5.16571  5.17462  4.65797  4.03105  1.75151  5.84473  4.80632    154 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01185  4.83724  5.55959  0.61958  0.77255  0.62617  0.76494
+     90   2.48275  5.13868  3.18520  2.44727  3.97540  3.65122  3.65677  3.36126  2.16734  3.30486  3.19390  3.06911  4.04233  2.41632  2.75033  2.25069  2.41675  3.09873  5.62108  3.69504    155 k - - H
+          2.68595  4.42186  2.77509  2.73103  3.46377  2.40517  3.72421  3.29387  2.67761  2.69368  4.24695  2.90368  2.73754  3.18175  2.89730  2.37895  2.77536  2.98514  4.58473  3.61505
+          0.63487  1.25520  1.68753  1.39580  0.28453  0.74756  0.64154
+     91   2.43987  5.24839  2.75312  2.20207  4.11839  3.54175  3.72993  4.06095  2.07671  3.34429  3.98666  2.75679  3.72576  2.60349  2.28520  2.46158  2.79062  3.43756  5.68761  3.81452    172 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01352  4.70589  5.42824  0.61958  0.77255  0.34096  1.24163
+     92   2.56414  5.37054  2.84426  2.11401  4.71382  2.70860  2.88683  4.19851  2.19051  3.14492  4.41168  2.55924  4.03189  2.79129  2.63798  2.35762  3.09530  3.76022  5.80426  3.44686    173 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01035  4.97167  5.69402  0.61958  0.77255  0.60365  0.79145
+     93   2.86527  4.78061  2.95620  2.59670  4.27271  1.99448  3.84571  3.48713  2.08881  3.39073  4.33266  2.46895  3.64059  2.84730  2.63768  2.84659  2.50396  3.24160  5.73764  4.35043    174 g - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01035  4.97167  5.69402  0.61958  0.77255  0.46145  0.99524
+     94   2.57883  3.45102  3.31966  2.70191  4.02973  3.16828  3.76144  3.12068  2.26544  3.06289  4.10647  2.81108  2.70638  2.99156  2.57108  2.49596  2.68439  2.78596  4.48656  4.03099    175 k - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01001  5.00539  5.72774  0.61958  0.77255  0.51640  0.90799
+     95   3.00846  4.37852  4.95050  4.33851  2.03946  4.18408  4.51243  2.07907  4.13124  1.32746  2.71567  3.86888  4.53472  4.25457  4.14405  3.49709  3.23945  2.15194  4.98866  3.35208    176 l - - T
+          2.68632  4.42239  2.77534  2.73137  3.46298  2.40515  3.72509  3.29351  2.67755  2.69332  4.24641  2.90361  2.73734  3.18136  2.89815  2.37881  2.77519  2.98532  4.58491  3.61446
+          0.13280  2.11108  5.72774  1.00017  0.45858  0.46707  0.98573
+     96   2.55096  5.39506  2.51329  2.14467  4.73966  3.20813  3.84532  4.22553  2.37049  3.70015  4.43599  2.79265  3.16832  2.74582  2.82569  1.88822  2.42672  3.78548  5.82793  4.41687    180 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     97   2.55253  5.00371  2.91674  1.56530  3.77486  3.74196  3.71669  3.18678  2.77868  2.90828  3.00031  3.29724  3.28640  3.12328  3.07027  2.76413  2.89289  3.32236  3.74938  3.48354    181 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     98   2.59075  4.32000  2.44335  2.15201  3.95693  3.12347  3.34011  3.83572  2.30797  3.16698  3.93315  2.75363  3.60071  2.81660  2.61665  2.70578  2.85000  3.34465  3.67935  4.00275    182 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+     99   2.65092  5.35978  2.44414  1.82235  4.69093  3.44711  3.03513  3.63938  2.46445  2.69072  4.40412  3.13476  3.87902  2.43027  2.99516  2.67955  2.75495  3.20344  5.80088  4.03299    183 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    100   1.86496  3.02665  4.71869  4.11439  3.41506  3.78632  4.39467  1.72869  3.93413  2.03606  3.25110  4.17623  4.43415  4.09606  3.42450  3.02051  2.94480  2.04904  4.94015  3.76003    184 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    101   2.66380  3.60669  3.09016  2.70629  3.97052  3.12773  3.91117  3.25572  1.80561  2.74214  4.19581  3.23071  4.10037  2.51557  2.19861  2.73033  3.11863  2.97558  5.62300  4.27841    185 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    102   2.65525  3.61780  2.73206  2.59880  2.91626  3.69337  3.17740  3.91061  2.06495  2.87551  3.62080  2.65252  4.08468  3.01717  2.55140  2.50072  2.84956  3.37543  5.67937  3.86408    186 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    103   2.78549  3.75825  4.89230  4.27707  2.37628  4.09576  4.42870  1.58181  4.06146  2.16353  2.83229  4.25917  4.45832  4.19107  4.06819  3.40747  3.15724  2.08428  3.94244  2.32199    187 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    104   1.91854  4.04767  4.83572  4.22341  2.21465  3.82491  4.41156  2.18264  4.01792  2.17430  2.43077  4.22827  4.44431  4.15736  4.04152  2.82624  2.88622  2.07903  4.25105  3.74133    188 a - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    105   2.59846  4.94560  3.39451  2.56515  3.67443  3.44440  3.79808  3.54150  2.22767  2.11264  4.02778  3.19297  4.14629  3.16161  2.51054  2.63727  2.68778  3.11215  4.13689  2.62651    189 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    106   2.71202  4.65478  2.93025  2.07602  4.70407  3.24619  3.70431  4.18261  2.58938  3.67040  4.10390  3.00747  4.05496  1.41184  3.00214  2.55548  2.74962  3.75459  5.80879  4.40428    190 q - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510  
+    107   3.40758  4.70636  5.39472  4.82751  3.82844  4.73217  5.15283  1.33187  4.66862  1.85779  2.55181  4.88258  5.03394  4.81634  4.72035  4.07618  2.46891  1.37416  5.58393  4.41648    191 i - - H 
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    108   2.29107  3.23763  4.81578  4.20571  3.00975  3.26830  4.41487  2.37700  4.00619  1.39060  3.28560  4.22330  4.44775  4.15070  4.03923  2.94875  3.15303  1.86969  4.92930  3.75166    192 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    109   2.62933  4.07203  2.95927  2.08969  4.71415  3.53463  3.84677  3.97436  2.17805  2.82549  4.41873  2.79932  4.05232  2.53645  2.16719  2.25459  2.86145  3.76301  5.81311  4.40656    193 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    110   1.17650  4.82606  4.24050  3.83461  4.98724  1.08848  4.85992  4.42183  3.81285  4.07900  4.88120  3.97861  4.34888  4.10941  3.49121  2.50205  3.18154  3.58125  6.30917  5.10145    194 g - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    111   3.90271  5.14012  5.90469  5.33230  3.41122  5.28833  5.65529  2.02087  5.19062  0.88043  2.65524  5.45552  5.42442  5.15469  5.16815  4.66249  4.12260  1.40813  5.79888  4.77647    195 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    112   2.18892  5.38966  2.64044  1.97751  4.73333  3.65659  3.60437  3.92129  2.10090  3.35469  4.43069  2.33847  4.05005  2.60616  2.77179  2.58383  3.11351  3.51057  5.82316  3.92131    196 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    113   2.81051  5.07767  4.77644  4.29954  2.03535  4.57845  2.23184  3.59557  4.18346  3.18325  4.21755  4.37554  4.93163  4.33627  4.35340  3.88728  3.48134  3.41897  4.49144  0.85916    197 y - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    114   3.22459  2.61128  5.38969  4.79509  3.58979  4.68737  5.03975  1.79213  4.61293  0.89179  2.61919  4.84389  4.96801  4.69100  4.62327  4.02095  3.66625  2.57847  5.40913  4.29402    198 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    115   4.50357  5.94639  4.67261  4.49457  2.97674  4.90740  0.35314  4.75553  4.20979  3.98991  5.25041  4.60971  5.36272  4.62490  4.36557  4.47961  4.76464  4.60938  4.62703  2.51319    199 H - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    116   2.59851  4.33805  2.68311  2.05797  4.71676  3.15802  3.59362  3.65569  2.43439  3.32473  4.42034  2.78736  4.05190  2.70193  2.69422  1.73897  3.11362  3.60918  5.81450  4.40740    200 s - - H
+          2.68619  4.42226  2.77521  2.73118  3.46355  2.40514  3.72496  3.29355  2.67742  2.69356  4.24691  2.90348  2.73741  3.18131  2.89802  2.37888  2.77521  2.98510  4.58478  3.61505
+          0.04979  3.09322  5.73865  0.35009  1.21950  0.48576  0.95510
+    117   2.54785  4.42029  2.89622  2.25922  4.34868  3.66610  3.21799  3.71995  2.27125  3.07920  3.10189  2.25867  4.05899  2.34775  2.56766  2.85682  3.11428  3.36249  5.78309  3.93739    202 n - - T
+          2.68634  4.42198  2.77524  2.73138  3.46344  2.40486  3.72467  3.29300  2.67713  2.69352  4.24714  2.90371  2.73709  3.18145  2.89825  2.37911  2.77527  2.98532  4.58501  3.61528
+          0.09842  2.40225  5.73865  1.69597  0.20263  0.48576  0.95510
+    118   2.53322  5.34393  2.95935  2.59629  3.94657  1.84501  3.63806  4.14023  2.18335  3.64044  4.39013  2.55576  3.77395  2.95784  2.71428  2.21077  3.11477  3.72355  4.68564  4.39095    213 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    119   3.55513  4.24694  5.60079  5.05662  2.60993  4.96071  5.43324  0.97900  4.91818  2.09234  3.80321  5.12225  5.23216  5.06726  4.97588  4.32549  3.79818  1.34455  5.81250  4.64107    214 i - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    120   2.50668  3.55624  4.84676  4.23404  3.24346  4.08398  4.41611  1.66920  4.02684  2.01982  2.79518  4.23489  4.44784  4.16480  3.83668  2.91482  2.83994  1.71230  4.92013  3.32611    215 i - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    121   4.62495  5.94818  4.90176  4.71687  2.80207  5.05504  0.41821  4.71670  4.43975  3.92227  5.21366  4.70109  5.45885  4.74110  4.56151  4.57303  4.86606  4.59605  4.46431  2.05738    216 H - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    122   3.04282  4.16968  3.83274  3.22981  4.12386  3.92565  4.16387  3.45658  2.89535  2.79946  3.52962  3.63885  4.33284  3.40809  0.88367  3.03173  2.91452  3.20781  5.47983  4.24517    217 r - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    123   4.66549  6.56867  0.13049  3.73947  6.20930  4.51726  5.46620  6.15252  4.80154  5.54790  6.65359  4.31602  5.22661  4.77980  5.35908  4.57977  5.04873  5.69924  7.06132  6.05090    218 D - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    124   4.11594  5.30633  6.13178  5.57769  3.73657  5.59693  5.98457  1.50133  5.46568  0.67788  3.07617  5.76705  5.63423  5.36401  5.43088  5.00606  4.33175  2.14525  5.97972  5.01356    219 L - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    125   4.55719  6.24846  4.80697  4.34215  5.94023  4.73956  5.02576  5.52333  0.17090  4.86848  5.87217  4.66859  5.24422  4.23030  3.41495  4.59394  4.77093  5.20290  6.57866  5.65826    220 K - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    126   2.73388  4.87911  3.10327  2.90832  4.07991  3.41413  4.01646  3.20246  2.89177  2.38683  3.78561  3.39051  1.51394  3.22647  3.32105  2.31617  2.81372  3.19214  5.43675  4.15025    221 p - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    127   2.30763  5.39894  2.49711  1.50775  4.74209  3.53358  3.60730  4.22742  2.31483  3.70337  3.89187  2.94609  4.05547  2.65035  3.08133  2.29509  3.12215  3.78862  5.83238  4.42137    222 e - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    128   4.24075  6.04161  4.10323  4.13804  5.66046  4.44992  5.52992  5.77835  4.64675  5.26705  6.30900  0.15130  5.17553  4.91694  4.94682  4.32646  4.70552  5.25421  6.75952  5.61338    223 N - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    129   3.55135  3.88080  5.58388  5.03211  3.05772  4.93598  5.38843  0.81084  4.88763  1.84199  3.74557  5.09695  5.20522  5.02095  4.93631  4.29650  3.79207  1.75582  5.75938  4.60163    224 i - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510 
+    130   4.26500  5.46000  6.21355  5.61602  2.31085  5.64571  5.88455  2.68862  5.47780  0.52109  2.36789  5.81754  5.61663  5.25881  5.37169  5.03996  4.45544  2.72136  5.81387  4.88779    225 L - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    131   3.14725  3.80930  5.11896  4.51210  2.75868  4.35237  4.69393  1.48763  4.30885  1.32733  3.15909  4.51494  4.68518  4.42027  4.31553  3.67228  3.37834  1.91515  5.14039  3.60311    226 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    132   2.83132  4.28838  2.02200  2.41335  4.70055  3.12743  3.31546  4.17875  2.06503  3.46693  4.09733  2.49326  4.05387  2.94892  3.07895  2.27711  2.89161  2.90947  5.80603  4.40188    227 d - - E
+          2.68620  4.42227  2.77521  2.73125  3.46356  2.40515  3.72496  3.29356  2.67743  2.69346  4.24692  2.90348  2.73741  3.18137  2.89794  2.37889  2.77521  2.98513  4.58479  3.61505
+          0.19179  2.92011  2.11536  0.31959  1.29625  0.48576  0.95510
+    133   2.77846  5.34345  2.73873  2.12504  4.68884  3.31622  3.10327  4.17503  2.07146  3.64885  4.14408  2.35495  3.55373  2.53396  2.68259  2.30312  2.80970  3.73426  5.77591  4.07717    229 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01122  4.89146  5.61380  0.61958  0.77255  0.46383  0.99121
+    134   2.76231  5.37627  2.40965  2.30431  4.72103  3.48860  3.46007  4.20680  2.15285  3.68141  4.41704  2.22463  3.54282  2.67483  2.66394  2.48608  2.48229  3.08661  5.80911  4.39830    230 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01030  4.97674  5.69908  0.61958  0.77255  0.42506  1.06054
+    135   2.88621  4.31580  2.58861  2.52891  4.00821  1.80604  3.64216  3.70404  2.55848  3.01103  3.82083  2.93142  3.28125  2.96153  3.15140  2.63196  2.92652  3.07947  5.65563  3.39061    231 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    136   2.74326  5.32913  2.62872  2.23118  4.20190  3.27586  3.01088  3.39612  2.54602  3.28443  3.99578  2.66732  4.06020  2.45230  2.82728  2.54748  2.65672  2.69485  5.77785  3.49295    232 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    137   2.61635  3.28525  4.16732  3.71739  2.98202  4.03403  4.34546  2.03775  3.50788  1.92540  3.47356  3.38535  3.11745  3.96570  3.52677  3.32696  2.46663  1.86856  4.97202  3.78516    233 v - - E
+          2.68644  4.42087  2.77438  2.73125  3.46306  2.40580  3.72561  3.29182  2.67742  2.69394  4.24637  2.90298  2.73752  3.18209  2.89809  2.37898  2.77573  2.98487  4.58490  3.61464
+          0.36530  1.19470  5.73865  2.01193  0.14356  0.48576  0.95510
+    138   2.92386  3.12674  3.30583  2.50241  4.41812  3.73989  3.57074  3.83865  1.16713  3.42239  3.64701  3.25964  4.12762  3.06518  2.74407  2.94589  3.15200  3.22587  5.64420  3.71460    253 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    139   3.61791  5.07369  5.93139  5.42030  4.01697  5.40165  5.95563  1.06228  5.32659  1.08905  3.76387  5.56598  5.56723  5.42106  5.39317  4.81179  4.11011  1.74432  6.14737  5.05160    254 i - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    140   1.74479  2.79152  4.35686  3.77898  3.55350  2.78668  4.34505  2.08763  3.66685  2.55990  3.56128  3.98833  4.38080  3.89329  3.86150  2.46831  2.34811  2.41028  5.05799  3.86488    255 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    141   4.66549  6.56867  0.13049  3.73947  6.20930  4.51726  5.46620  6.15252  4.80154  5.54790  6.65359  4.31602  5.22661  4.77980  5.35908  4.57977  5.04873  5.69924  7.06132  6.05090    256 D - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    142   4.66482  5.73614  5.86239  5.64956  0.40883  5.43325  4.33355  3.86711  5.42110  2.16428  4.36705  5.11239  5.65371  5.16689  5.27011  4.83252  4.86624  3.98432  3.74189  2.69037    257 F - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    143   3.93699  5.85566  3.02816  3.87037  6.00993  0.21285  5.44927  5.75466  4.70121  5.27717  6.21696  4.31536  4.99427  4.76567  5.12629  4.02226  4.42780  5.10478  6.99282  5.96830    258 G - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    144   2.54355  3.67295  4.73868  4.13265  2.71576  4.06852  3.76754  2.37455  3.94756  1.20416  3.31174  3.74431  4.43365  4.10500  4.00521  2.64259  3.14725  2.29206  4.93223  3.75278    259 l - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03189  5.01631  3.69859  0.61958  0.77255  0.48576  0.95510
+    145   0.77520  2.86695  4.81978  4.47036  4.95592  3.15951  5.20700  4.36343  4.38394  4.08078  4.90451  4.25075  4.38058  4.58838  4.56864  1.45304  3.34905  3.34992  6.35477  5.22001    260 a - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.07711  4.99453  2.69658  0.61958  0.77255  0.54567  0.86620
+    146   2.54977  3.79925  3.16699  2.32606  4.48674  3.31547  3.84408  3.47518  1.75582  3.12339  4.25869  2.84258  4.04145  2.96441  2.18943  2.71334  2.91887  2.95414  5.67187  4.30075    261 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.13472  4.92824  2.13029  0.61958  0.77255  0.70219  0.68419
+    147   2.64685  3.78445  3.06669  2.61534  3.32325  3.67596  3.69246  3.14713  2.01231  2.43193  3.43451  3.16113  4.06301  2.47274  3.08778  2.74475  2.84906  2.65428  5.37440  4.07606    262 k - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.29473  4.80574  1.39802  0.61958  0.77255  0.92027  0.50818
+    148   2.40579  4.15423  4.25399  3.66267  2.39272  3.57693  3.44550  2.33935  3.03827  1.86796  2.97007  3.83355  4.18573  3.73297  3.67827  3.10851  2.93289  2.21754  4.76738  2.96352    263 l - - .
+          2.68635  4.42219  2.77489  2.73146  3.46367  2.40530  3.72500  3.29419  2.67720  2.69370  4.24630  2.90362  2.73727  3.18122  2.89818  2.37860  2.77478  2.98583  4.58472  3.61420
+          0.77167  0.63015  5.24955  1.50734  0.25038  0.20147  1.70117
+    149   2.68930  5.13793  2.99383  2.28756  4.12167  3.02811  3.40047  2.83046  2.53990  2.67433  3.91490  2.66806  3.85841  2.95123  3.08518  2.54554  2.43488  3.14237  4.83794  3.11842    281 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    150   2.67960  3.59834  3.08049  2.32634  4.00400  2.85568  3.65072  3.26198  2.22635  2.80014  3.89773  3.03566  3.90780  2.84131  2.77630  2.59674  2.54121  2.94346  4.66327  3.60288    282 k - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    151   2.78407  5.36932  2.80723  2.38070  3.95652  3.28210  3.31902  3.80407  2.38757  3.23090  4.41260  2.26860  2.87333  2.94776  2.55475  2.11230  2.82314  3.31603  5.80800  4.40313    283 s - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    152   2.55066  5.37311  2.42428  2.58328  4.70981  2.65546  3.66067  3.46611  2.34139  3.39818  4.41598  2.44705  3.58966  2.48003  2.87086  2.30355  2.92043  3.58081  5.81084  3.32103    284 s - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    153   2.61131  5.36709  3.05324  2.00706  4.70125  2.53001  3.45141  4.17959  2.46541  3.54879  4.41063  3.13225  3.27183  2.67970  2.77280  2.14590  2.62504  2.97295  5.80635  3.79465    285 e - - S
+          2.68619  4.42226  2.77520  2.73124  3.46320  2.40514  3.72495  3.29355  2.67742  2.69356  4.24691  2.90348  2.73740  3.18147  2.89802  2.37888  2.77520  2.98519  4.58478  3.61504
+          0.03620  3.43177  5.73865  0.40966  1.09027  0.48576  0.95510
+    154   2.80870  4.76397  2.82688  2.35901  3.53520  3.31737  3.24083  3.53679  2.31494  2.98485  3.52265  2.69923  3.15465  2.90046  2.56489  2.40361  2.83318  3.04461  5.74293  3.76697    287 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    155   2.38638  3.55872  3.84850  3.27925  3.07902  3.89951  3.49984  3.07191  2.87600  2.13071  2.79826  2.94436  3.28103  3.24012  3.30127  2.77627  2.52689  2.66914  5.17020  2.84075    288 l - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    156   2.88266  4.79637  2.63708  2.49106  4.69372  3.27452  3.02757  4.17056  2.31920  3.54781  3.84892  2.54158  3.83133  2.71114  2.83815  2.34796  2.07167  3.53868  4.84477  3.26548    289 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    157   2.55115  5.22307  2.81541  2.42597  3.84088  2.72211  3.45978  3.70939  2.65113  3.50012  4.03956  3.00145  3.26637  2.92366  3.12986  2.10982  2.09681  2.95270  4.42058  4.32902    290 t - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    158   2.79383  4.53543  3.94528  3.00256  2.28876  3.25886  4.19260  2.53698  3.31063  2.13485  3.51266  3.25465  3.61933  3.02975  2.60672  3.04108  3.13630  2.22820  5.12884  3.03437    291 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    159   2.54133  2.27249  3.62507  3.14565  3.48798  3.47382  4.20805  2.87324  3.24770  2.61629  3.62417  3.76153  4.31084  3.45605  3.15192  3.19594  2.77273  1.44309  5.11140  3.89714    292 v - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    160   2.50560  4.02210  4.21483  3.69251  4.03927  0.87585  4.50859  3.18042  3.62456  3.12203  4.01557  3.91946  4.34666  3.89680  3.91281  2.66630  3.23503  2.52015  5.49804  3.97346    293 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    161   2.98792  5.00679  3.18026  3.47531  5.09253  3.65988  4.75357  4.53531  3.72802  4.18133  4.99276  3.79669  4.37930  3.97717  4.12874  1.60941  0.74054  3.97784  6.39182  5.11951    294 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    162   2.69840  5.26022  3.00961  2.31122  4.20998  3.68086  3.63617  3.20701  2.41779  2.58849  4.31499  2.98486  2.62271  2.99075  2.13729  2.36313  3.11573  3.42385  5.72545  3.54571    295 r - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    163   2.71301  5.29924  2.59488  2.14743  3.52695  3.04037  3.28275  4.06670  2.61594  3.34508  4.35002  2.84026  3.31113  2.97459  2.36266  2.86533  2.70674  3.41928  3.03699  3.23042    296 e - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    164   4.65993  5.73598  5.69662  5.49898  1.97465  5.32012  3.31709  4.36240  5.29326  3.66395  4.92323  4.93743  5.59573  5.06816  5.17473  4.69695  3.41633  4.25844  2.61302  0.51590    297 Y - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    165   2.46851  3.63596  4.34730  3.76113  3.49978  4.00900  4.30977  2.55613  3.41377  2.11254  1.89168  3.40620  4.37893  3.86942  2.23584  3.08926  3.14320  2.13937  5.00600  3.81292    298 m - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    166   0.54633  4.79516  4.59072  4.34047  5.33605  3.22550  5.27451  4.80651  4.37656  4.47607  5.24769  4.21379  3.02084  4.58677  4.60871  1.81435  3.40538  4.05386  6.66459  5.53199    299 A - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    167   3.08666  5.02380  4.02349  3.92167  5.10908  3.73669  5.07487  4.62556  4.04836  4.33998  5.22795  3.34786  0.41351  4.38870  4.30726  3.26072  3.60085  3.40472  6.47905  5.20179    300 P - - H
+          2.68622  4.42144  2.77523  2.73111  3.46358  2.40517  3.72498  3.29358  2.67745  2.69359  4.24694  2.90351  2.73743  3.18126  2.89786  2.37891  2.77523  2.98522  4.58481  3.61507
+          0.03484  3.47295  5.73865  0.99675  0.46057  0.48576  0.95510
+    168   3.66837  6.19086  2.84947  0.45189  5.55161  3.90523  4.43200  5.09782  3.30487  4.55035  5.42336  3.03786  4.53514  3.60153  3.37398  3.54999  3.95969  4.65772  6.64024  5.17650    305 E - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510 
+    169   2.79774  4.32598  4.68241  3.67608  3.42171  4.06471  4.38713  2.01470  3.90321  1.87174  3.44282  4.15860  4.43014  3.77035  3.25202  3.36713  2.95239  1.31877  3.77234  3.76466    306 v - - H
+          2.68603  4.42242  2.77519  2.73104  3.46342  2.40506  3.72511  3.29371  2.67728  2.69322  4.24707  2.90364  2.73756  3.18163  2.89801  2.37893  2.77519  2.98535  4.58494  3.61520
+          0.12919  2.22240  4.35495  1.23224  0.34480  0.48576  0.95510
+    170   2.91184  3.76308  4.76588  4.15799  3.39752  4.06920  3.82796  1.60071  3.96642  1.39613  3.17824  3.65225  4.43387  4.11848  4.01348  2.94594  3.14403  2.21737  4.92444  3.43929    311 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00999  5.00664  5.72898  0.61958  0.77255  0.51297  0.91308
+    171   2.62785  5.06480  3.29796  2.67561  3.73804  3.41086  3.92577  3.21358  2.32640  2.15096  3.31180  2.55443  4.11004  2.47045  2.33927  2.70318  3.11578  3.12852  5.57146  4.24107    312 l - - C
+          2.68631  4.42245  2.77511  2.73076  3.46350  2.40522  3.72547  3.29356  2.67765  2.69366  4.24573  2.90389  2.73738  3.18142  2.89826  2.37846  2.77520  2.98520  4.58530  3.61473
+          0.62427  1.87899  1.16602  1.98855  0.14722  0.46912  0.98231
+    172   2.60166  5.18307  2.62016  2.19793  3.35206  2.39740  3.57105  3.96340  2.18128  3.47543  4.23188  3.01161  3.92489  2.82990  2.86326  2.33987  2.67701  3.55746  5.63416  3.78538    327 k - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01428  4.65191  5.37425  0.61958  0.77255  0.24611  1.52250
+    173   2.37600  3.94341  2.81761  2.45806  4.08381  2.38833  3.32666  4.15213  2.47322  3.64753  4.39446  2.71786  3.74359  2.39493  2.76163  2.38666  3.00597  3.01776  5.79219  3.82026    328 a - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00998  5.00778  5.73012  0.61958  0.77255  0.47101  0.97916
+    174   2.81816  4.87774  2.88922  2.34131  3.91699  3.47934  3.84840  3.76601  2.22767  3.10881  4.41163  2.46519  2.75598  2.51039  2.43545  2.43680  2.76354  3.54179  5.80719  3.84984    329 k - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    175   2.74169  5.33416  2.58184  2.32653  3.61666  2.33545  3.50275  4.12419  2.36762  3.50261  3.56274  2.91332  3.07814  2.74477  2.96926  2.79421  2.92848  3.32496  5.78164  2.72284    330 e - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    176   2.48397  4.72191  2.72922  2.94719  3.45604  3.83733  2.73484  2.72590  3.03376  2.94128  3.81928  3.51045  4.21965  3.13222  3.31573  2.84267  3.12967  2.71705  3.94971  1.76193    331 y - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    177   2.88858  5.15756  2.48812  2.69267  3.68939  2.20273  3.90720  3.84889  2.69072  3.17648  4.22648  3.21985  3.19313  3.04303  3.16266  2.07644  2.02474  3.50391  5.64990  3.69862    332 t - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    178   2.44412  5.27089  3.18567  2.52594  4.01181  3.49400  3.20300  3.68711  2.26882  2.99802  4.32458  3.16791  2.73532  2.67304  2.47574  2.35896  2.41445  3.31659  5.73361  3.79657    333 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    179   2.27957  5.36842  3.14470  2.24142  4.70283  3.35867  3.70433  4.18054  1.84880  3.00297  4.41216  3.13478  2.50109  2.87247  2.49374  2.50485  2.88881  3.75338  5.80713  4.40386    334 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    180   2.25487  3.19836  3.67383  3.53894  3.58335  3.60706  4.24810  2.30247  3.45618  2.57768  3.58074  3.40343  4.33674  3.48890  3.71724  2.25302  2.94661  1.56396  5.07167  3.86608    335 v - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    181   3.32488  6.23611  0.30142  2.99559  5.85936  3.97225  4.81661  5.46625  4.07634  5.00781  5.98660  3.57219  4.70996  4.04099  4.76451  3.76774  4.26196  4.95940  7.04785  5.56474    336 D - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    182   2.97530  4.34655  4.92112  4.31006  3.42469  4.15298  4.48903  1.67409  4.10270  2.14170  2.44181  4.30839  4.51041  4.23558  4.11915  3.46567  2.80277  1.45020  3.28394  3.56694    337 v - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    183   4.60767  5.71652  5.61554  3.89031  2.18526  5.28181  4.15190  4.32304  5.18037  3.62888  4.88484  4.90634  5.56392  5.01906  5.10312  4.65700  4.81381  4.22147  0.60655  1.74323    338 W - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    184   1.30551  4.78823  4.65738  4.37945  5.36630  2.58206  5.28849  4.84019  4.38318  4.50739  5.27020  4.22569  4.39579  4.59600  4.61395  0.79404  2.89139  4.06528  6.68751  5.56026    339 s - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    185   2.44153  3.17932  4.87400  4.25972  2.53121  4.09082  4.42403  2.33144  4.04748  1.34403  2.92510  4.24940  4.45392  4.18053  4.05982  3.14600  3.15398  2.13810  4.92017  3.14864    340 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    186   2.22507  4.96728  4.63623  4.63233  5.66307  0.33314  5.62287  5.16416  4.86793  4.86816  5.66661  4.43512  4.57282  5.01497  4.99264  3.26151  3.63533  4.32703  6.92990  5.89905    341 G - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    187   2.93009  2.13614  4.95885  4.35253  3.48038  4.19799  4.54424  1.68794  4.14925  1.93394  3.18432  4.35359  4.55452  4.28834  4.17024  3.34008  3.23972  1.60527  5.03746  3.47367    342 v - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    188   3.21831  4.55127  5.17022  4.58630  3.70338  4.47717  4.86313  1.30610  4.40976  1.92114  2.79907  4.62299  4.81179  4.56490  4.45688  3.52794  2.67243  1.42404  5.34369  4.16408    343 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    189   2.77853  3.56970  4.91220  4.29933  2.66878  4.13468  4.46922  2.12981  4.08926  1.13811  3.14072  4.29275  4.49323  4.21981  4.10205  3.44689  2.88537  2.20246  4.95897  3.46211    344 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    190   2.08301  4.31008  4.80905  4.20025  2.66597  4.08391  3.54227  2.07553  4.00182  2.35024  3.19529  4.21899  4.44796  4.14611  4.03654  3.39206  3.15615  2.32332  2.91148  1.86433    345 y - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    191   2.65126  3.79080  3.03118  1.37895  4.54227  3.68257  3.87660  3.27048  2.56225  3.53374  4.30787  2.88399  4.07460  2.72714  3.11894  2.49943  2.86114  3.20681  5.71940  3.61825    346 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    192   2.75949  4.33120  4.86063  4.24972  2.93412  4.11800  4.44873  2.58063  4.04775  1.34098  1.86205  4.26240  4.47769  4.18564  4.07448  3.30982  2.87183  2.17494  4.95131  3.25026    347 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    193   2.13730  3.34433  4.84746  4.23449  2.67079  4.08305  4.41495  2.45417  4.02682  1.45586  2.95446  4.23448  4.44687  4.16424  3.65241  3.39295  2.78239  2.10310  4.91833  3.10946    348 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    194   2.61714  3.04119  3.51958  2.50105  3.16562  3.44068  4.02999  3.26384  2.78703  2.46125  3.90521  2.94061  4.18669  3.26384  2.94427  2.45451  1.92365  2.96026  5.36672  3.77569    349 t - - H
+          2.68592  4.42131  2.77530  2.73134  3.46364  2.40523  3.72505  3.29364  2.67751  2.69365  4.24624  2.90357  2.73750  3.18157  2.89769  2.37886  2.77510  2.98507  4.58487  3.61513
+          0.08274  2.77459  4.07273  1.24373  0.34011  0.48576  0.95510
+    195   2.74009  5.38236  2.60734  2.58658  4.30151  1.49346  3.57775  4.19577  2.06867  3.68256  4.42710  2.84258  4.06100  2.86136  2.79826  2.78642  3.12785  3.76776  5.82019  4.41533    355 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02515  5.00240  4.01100  0.61958  0.77255  0.52459  0.89600
+    196   2.87057  4.15783  2.81932  2.15097  3.67482  3.00161  3.44561  3.53712  2.01194  3.52030  4.39830  2.84675  4.04172  2.62971  2.18111  2.65254  2.90372  3.46034  5.79407  3.92999    356 k - - S
+          2.68632  4.42239  2.77496  2.73101  3.46368  2.40527  3.72508  3.29339  2.67739  2.69369  4.24616  2.90361  2.73734  3.18160  2.89815  2.37901  2.77533  2.98486  4.58491  3.61430
+          0.06990  2.74572  5.70978  1.46974  0.26135  0.43968  1.03351
+    197   2.53761  4.57755  3.85895  3.05555  3.06219  3.90225  3.89097  2.90555  3.23603  1.98376  3.43681  3.14844  2.18635  3.28921  3.56902  2.76101  2.38583  2.48063  5.16546  3.65117    363 l - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    198   2.94035  4.49377  4.16973  3.59341  3.59928  3.99457  4.28692  2.57573  3.48228  2.11382  3.59630  3.88641  1.19333  3.75706  3.46613  3.08341  3.17527  2.51467  5.10025  3.66586    364 p - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    199   2.95410  3.97958  4.76382  4.16399  1.23466  4.10162  4.38107  2.78567  3.65586  2.19089  3.46719  4.20671  3.78920  4.13156  4.03525  3.25126  3.18603  2.50296  2.73984  2.86766    365 f - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    200   2.72297  4.82530  2.62940  2.35446  3.83755  3.31971  3.71413  4.17131  2.26174  3.54363  4.14174  3.02369  2.60946  2.49179  2.66057  2.21727  2.99115  3.74587  4.55596  3.10965    366 s - - S
+          2.68620  4.42227  2.77522  2.73112  3.46340  2.40515  3.72496  3.29334  2.67743  2.69357  4.24692  2.90349  2.73742  3.18148  2.89803  2.37889  2.77522  2.98520  4.58479  3.61505
+          0.07731  2.84051  4.13492  0.30572  1.33404  0.48576  0.95510
+    201   2.63880  5.38435  2.63657  2.22720  4.72697  1.94780  3.15235  3.87964  2.57669  3.68860  4.42577  2.57921  3.16991  2.74291  2.86900  2.50219  2.85278  3.77370  5.81843  4.40865    368 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.52175  0.90012
+    202   2.40102  4.71375  2.46996  2.13243  4.71840  3.11745  3.05416  3.48265  2.25142  3.16134  4.41932  2.93828  3.25177  2.60824  2.79040  2.58260  2.90071  3.38079  5.81277  4.40438    369 e - - S
+          2.68658  4.42180  2.77453  2.73123  3.46394  2.40505  3.72421  3.29333  2.67765  2.69382  4.24730  2.90354  2.73686  3.18110  2.89829  2.37906  2.77522  2.98525  4.58428  3.61543
+          0.55463  1.48984  1.60791  1.39890  0.28351  0.52175  0.90012
+    203   2.69338  5.29005  2.51143  2.25105  4.62988  2.84270  3.49832  3.48405  2.27737  3.31427  4.33211  2.56706  3.20350  2.85310  2.67590  2.21378  2.73620  3.67787  5.72586  4.31806    375 s - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01250  4.78398  5.50633  0.61958  0.77255  0.95212  0.48763
+    204   2.65942  5.19894  2.69156  2.28179  3.77027  3.19251  3.77484  3.06173  2.43464  2.99400  3.94013  2.58274  3.44930  2.63512  2.62020  2.46578  2.71730  3.35179  5.65696  3.80565    376 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01250  4.78398  5.50633  0.61958  0.77255  0.77169  0.62033
+    205   2.38920  5.21254  2.73488  2.26876  4.51445  3.29559  3.03245  3.05648  2.42252  2.83088  3.97624  2.72691  3.66747  2.74292  2.65924  2.79642  2.87825  3.04317  5.67196  3.47007    377 e - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01186  4.83615  5.55849  0.61958  0.77255  0.33713  1.25111
+    206   2.65422  4.48489  1.88534  2.29942  4.71042  3.12833  3.84224  3.67843  2.58000  3.67468  4.41498  2.22848  4.04754  2.86157  2.64414  2.33478  2.73698  3.75923  5.80928  4.40246    378 d - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.52175  0.90012
+    207   2.52228  5.35256  2.79874  2.27245  4.68298  3.15080  3.25420  3.81263  2.42135  3.03021  2.98061  3.02733  3.41549  2.09266  2.89624  2.43800  3.03420  3.22791  5.79412  4.39217    379 q - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.52175  0.90012
+    208   2.88352  5.03264  3.01718  2.64158  3.36791  3.41419  3.67586  2.82361  2.46533  2.06980  3.25199  2.87105  3.91016  2.67457  2.54739  2.67076  3.11488  3.35644  5.54543  3.24074    380 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.52175  0.90012
+    209   2.31106  5.21929  3.20358  2.11286  3.58411  3.54755  3.12693  3.74178  2.35878  3.37262  4.27754  3.18132  3.64073  2.21825  2.83044  2.63943  3.03559  2.71879  4.50670  3.34948    381 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01003  5.00344  5.72579  0.61958  0.77255  0.46390  0.99109
+    210   2.74907  4.02445  4.23598  3.43708  3.16042  3.98772  3.66116  2.29352  2.78065  1.91975  2.65717  3.64306  4.00461  3.79624  2.62994  3.12682  2.72367  2.05105  5.03295  3.83444    382 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    211   2.79706  4.32223  4.14412  4.07508  2.44930  4.05985  3.54709  1.67401  3.90204  2.17979  2.94629  3.31750  4.42562  4.07034  3.98057  2.91878  2.88141  2.01026  4.94063  2.95520    383 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    212   2.43063  3.51667  2.84063  2.33381  3.23751  3.53757  3.64461  3.61354  2.52297  3.04234  3.80037  3.02528  3.31294  2.77439  2.36706  2.71893  3.04140  2.71650  5.64517  3.47973    384 e - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    213   2.54995  5.35768  3.14585  2.23433  4.68798  3.33616  3.38625  3.80986  2.09254  2.85705  3.68757  2.56176  4.05535  2.40160  2.38927  2.57618  3.04385  3.36418  5.79929  3.79887    385 k - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    214   2.66499  5.02053  2.80345  2.59207  4.23983  3.19081  3.94632  1.95336  2.43621  2.96229  3.77095  2.92719  4.12615  3.11304  2.86357  2.94448  2.42822  2.54184  5.53679  4.21766    386 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    215   2.79062  4.84420  3.48859  2.83007  4.02200  3.06268  4.01637  2.98055  2.36084  1.68419  3.06982  3.40535  3.56488  2.89781  2.78978  2.64270  2.97298  2.81255  5.39215  4.11258    387 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    216   2.78422  4.40067  2.87114  2.38957  4.66317  2.42774  3.11725  3.68493  2.29738  3.11118  3.80555  3.07548  3.38861  2.66839  2.52841  2.36297  2.74889  3.71883  4.85618  3.72852    388 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04975  5.01631  3.17248  0.61958  0.77255  0.48576  0.95510
+    217   2.55009  3.88585  3.08502  2.47179  4.52328  2.60499  3.86026  3.74333  2.31088  3.21633  3.94275  3.03087  2.50945  2.97834  2.41589  2.69163  2.38722  3.38360  5.70135  4.11095    389 k - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01030  4.97686  5.69921  0.61958  0.77255  0.59090  0.80705
+    218   2.71816  5.35510  2.47492  2.28903  4.69101  3.44039  3.67059  2.92462  2.15295  3.03900  4.39818  2.98975  2.40750  2.74839  2.76016  2.49540  2.85213  3.74081  5.79327  4.38792    390 k - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02103  4.97686  4.27462  0.61958  0.77255  0.59090  0.80705
+    219   2.59242  4.56547  3.37418  3.24570  3.28039  3.87349  3.78552  2.80260  2.91515  1.85640  3.67045  2.94464  3.03710  3.48913  3.31352  2.93163  2.57101  2.12270  4.60315  3.29903    391 l - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01041  4.96623  5.68858  0.61958  0.77255  0.61678  0.77584
+    220   2.76552  5.35116  2.75065  2.09233  4.26840  3.39661  3.23930  3.45666  2.13782  2.93921  3.69074  2.78256  2.85112  2.85423  2.61182  2.41762  3.01864  3.73701  5.78920  4.16743    392 e - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01041  4.96623  5.68858  0.61958  0.77255  0.61678  0.77584
+    221   2.56616  4.88573  3.26662  2.33696  2.41520  3.27403  3.41068  2.92250  2.70761  2.68623  3.97070  3.19787  3.38122  3.16656  2.79903  2.47364  2.99470  2.86843  4.82552  3.32054    393 e - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01041  4.96623  5.68858  0.61958  0.77255  0.61678  0.77584
+    222   2.62567  5.35084  2.04845  2.35215  4.39252  3.47212  3.16243  3.98892  2.45186  3.65226  3.66396  2.66846  2.63580  2.82810  3.05661  2.22708  2.99549  3.54011  5.78899  3.92777    394 d - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03667  4.96623  3.53930  0.61958  0.77255  0.61678  0.77584
+    223   2.86103  4.03193  3.18635  2.38909  3.31435  3.61153  3.49109  2.70908  2.69769  2.69388  3.88535  3.22191  3.67076  3.11276  2.73635  2.49324  3.00545  2.72730  2.94731  2.96802    395 e - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01875  4.94025  4.47244  0.61958  0.77255  0.67631  0.71028
+    224   2.62521  5.12447  2.77972  2.44738  3.31605  3.53418  3.86652  3.33309  2.47319  3.03557  4.18886  3.10134  2.42408  2.60018  2.70100  2.64509  2.92363  3.22521  3.41935  3.46519    396 p - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.05901  4.93227  2.99396  0.61958  0.77255  0.69360  0.69269
+    225   2.68343  5.20702  2.88582  2.22963  3.55567  3.45903  3.65367  3.49248  2.11994  2.97314  4.26172  2.80968  3.57686  2.44336  3.06162  2.53581  2.67303  3.18308  3.73101  3.66292    397 k - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01130  4.88456  5.60691  0.61958  0.77255  0.78866  0.60597
+    226   2.68707  4.98468  2.82158  2.38405  3.75917  3.36024  3.65392  3.18317  2.38508  2.73942  3.76303  3.22111  3.36652  3.04584  2.96704  2.31757  2.64759  2.95752  3.21600  3.40268    398 s - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.11345  4.88456  2.30573  0.61958  0.77255  0.78866  0.60597
+    227   2.79298  3.84213  2.48108  2.52210  3.99851  3.60747  3.12972  3.13545  2.48018  3.05991  4.13768  2.83211  3.54520  2.93964  2.41588  2.35619  2.76810  2.97839  5.56015  3.44494    399 s - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02807  4.78362  3.94704  0.61958  0.77255  0.95264  0.48731
+    228   2.58191  4.56682  2.77742  2.37170  4.46635  3.58143  3.77449  3.50815  2.36445  2.67587  4.22448  2.86502  2.73607  2.80387  2.79297  2.07848  2.75446  3.53442  5.63386  3.74575    400 s - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.14447  4.76826  2.07130  0.61958  0.77255  0.97412  0.47405
+    229   2.74120  5.02625  2.90214  2.38037  3.96892  2.81361  3.75340  3.09744  2.29181  2.85276  3.79351  2.85134  3.31773  2.88600  2.57247  2.48932  2.74923  2.81409  5.51153  4.15648    401 k - - .
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02950  4.63827  3.94280  0.61958  0.77255  1.12880  0.39071
+    230   2.65814  5.03703  2.72915  2.44094  4.30481  3.19996  3.74471  3.73960  2.51991  2.72378  3.12113  3.05398  2.98987  2.79382  2.49179  2.12708  2.96796  3.38544  5.51898  3.52938    402 s - - .
+          2.68724  4.42293  2.77612  2.73159  3.46162  2.40699  3.72369  3.29398  2.67731  2.69321  4.24701  2.90285  2.73662  3.18257  2.89624  2.37860  2.77511  2.98573  4.58252  3.61251
+          0.60323  0.80254  5.34581  2.99471  0.05135  0.22221  1.61320
+    231   2.51767  5.38833  2.69504  1.91983  4.73145  3.65677  3.84405  3.51135  2.26714  3.33248  4.42951  2.74739  2.83044  2.63263  2.69535  2.26534  2.89575  3.77792  5.82218  4.12814    442 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    232   2.61324  5.33087  2.55247  1.71577  3.78905  3.51981  3.85697  4.11872  2.53837  3.37618  3.68276  2.71899  3.50504  2.96215  2.76975  2.43182  2.62759  3.19721  4.83386  4.08215    443 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    233   1.85315  2.66076  4.74963  4.14369  2.72361  2.76389  4.40211  2.66580  3.95769  1.60680  3.08425  4.19191  4.43908  4.11400  4.01342  3.37949  2.94810  2.35782  4.93743  3.75833    444 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    234   2.54113  3.93957  3.31567  2.58263  3.59686  3.36801  3.93655  2.84244  1.77469  2.55244  4.12290  3.02474  4.11900  2.54032  2.70090  2.93506  2.79964  3.23719  5.55956  3.97978    445 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    235   2.66035  5.38903  1.80529  2.16718  4.22724  3.25823  3.17680  4.00903  2.28647  3.50730  4.43018  2.97613  4.05032  2.48614  2.98498  2.31241  3.11375  3.77870  5.82277  3.97909    446 d - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    236   3.21254  4.56529  5.14365  4.53738  1.97526  4.40976  4.73239  2.19759  4.34073  1.04052  3.41232  4.55986  4.72994  4.44093  4.35221  3.72900  3.00622  2.20314  5.16040  3.53582    447 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    237   2.97518  4.05135  5.35921  4.78478  3.77817  4.66169  5.06989  1.35059  4.61468  1.50253  2.60400  4.82061  4.97178  4.75525  4.65269  4.00168  3.59517  1.52175  5.50364  4.33590    448 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    238   2.63113  4.16111  2.70523  2.01221  4.32546  2.89528  3.69801  3.91463  1.97992  3.37990  4.41866  2.72468  4.05215  2.88390  2.65380  2.44936  2.78930  3.52828  5.81309  3.68713    449 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    239   2.79532  4.56741  2.59886  2.26653  4.72524  2.97326  3.84498  4.20852  1.87988  3.68761  4.42563  2.67816  4.05093  2.72680  2.26697  2.39445  3.11354  3.44147  4.06926  3.70096    450 k - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    240   2.76031  1.91137  4.93344  4.32897  3.47342  4.21043  4.55379  2.12811  4.13437  1.60981  2.11111  4.35327  4.56463  4.27495  4.16872  3.52442  2.73881  2.49904  5.05160  3.88034    451 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    241   3.00579  3.43594  4.91160  4.30236  2.88535  4.17356  4.48924  2.41412  3.62991  0.97514  3.41700  4.31537  4.52645  4.23058  4.12367  3.48482  3.23680  2.59853  2.70167  3.78558    452 l - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    242   2.51291  3.86266  2.42773  2.26878  4.71076  3.65927  3.14908  3.51790  2.32370  3.67561  4.41657  2.44903  4.05255  2.58104  2.64695  2.72413  2.48024  3.27065  5.81133  3.77060    453 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    243   2.89024  4.99098  3.22187  2.69841  3.16242  3.74556  3.43367  3.45008  2.12873  2.47972  3.65551  3.30484  3.00902  2.69329  2.47140  2.87653  2.77051  2.80352  4.74384  3.09104    454 k - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    244   2.75442  4.59220  1.48443  2.55169  4.69407  3.66624  3.85987  4.16899  2.26580  3.66358  4.41104  2.16093  4.06270  2.96232  2.94350  2.69711  3.00639  3.21953  5.80766  4.40610    455 d - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    245   2.53666  4.51809  3.31495  2.50094  3.81458  3.47863  3.66745  3.68842  2.74796  3.30408  3.76297  2.90283  1.45947  2.98944  2.97069  2.84711  3.12065  2.90248  5.56105  4.23506    456 p - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    246   2.18867  5.36299  2.90524  2.21328  4.32931  3.53924  3.49382  3.97251  2.24621  3.14962  3.99253  2.78034  3.78478  2.54072  2.85251  2.30318  2.70173  3.25844  5.80328  3.47734    457 a - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    247   2.48590  4.36945  2.67418  2.22639  4.72760  3.65776  3.84501  4.21118  1.68322  3.11616  4.42723  3.02530  4.05117  2.52411  2.40832  2.54354  2.78662  3.77464  5.82017  4.41136    458 k - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    248   3.97870  5.88109  4.77637  3.79584  5.10245  4.51923  4.26030  4.68907  2.25479  4.07008  4.99673  4.06985  4.83329  3.42182  0.45649  3.98836  4.07883  4.42272  3.45519  4.78623    459 R - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    249   2.59820  4.04607  4.60274  4.00452  3.26886  3.61184  4.36773  2.06089  3.84570  1.56372  3.45362  4.11687  2.06890  4.02734  3.95003  2.68390  3.14561  2.58630  4.40258  3.38622    460 l - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04987  5.01631  3.16967  0.61958  0.77255  0.48576  0.95510
+    250   2.79362  5.19179  3.01710  2.75300  4.64905  2.77281  4.01085  4.10036  2.79939  3.46822  4.43166  2.61914  4.12461  3.14023  3.27668  1.68833  1.53351  3.69065  5.84113  4.47617    461 t - - T
+          2.68631  4.42192  2.77519  2.73130  3.46353  2.40482  3.72508  3.29367  2.67738  2.69360  4.24703  2.90360  2.73753  3.18148  2.89804  2.37867  2.77518  2.98532  4.58490  3.61498
+          0.12278  2.37863  3.77824  1.17468  0.36950  0.42506  1.06054
+    251   1.47902  3.78300  4.72209  4.11644  2.74562  3.65840  4.38345  2.02614  3.93274  2.07959  3.21794  4.17061  3.49702  4.09137  3.99270  2.98272  3.05871  2.38788  4.48487  3.74373    467 a - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01009  4.99737  5.71972  0.61958  0.77255  0.53813  0.87669
+    252   2.38222  4.71734  2.84513  2.05232  4.42547  3.25385  3.43556  4.19285  2.14440  3.09056  3.51596  2.80436  3.18490  2.67888  2.63294  2.69832  2.67125  3.58577  5.80764  4.40015    468 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01009  4.99737  5.71972  0.61958  0.77255  0.45436  1.00747
+    253   2.65797  4.06542  2.63237  1.57032  4.71890  3.65831  3.45654  4.20087  2.52289  3.68238  4.00276  3.00654  3.59507  2.12286  2.86428  2.58695  3.11362  3.45316  5.81563  3.91003    469 e - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    254   1.93383  3.77543  4.87213  4.26567  3.27005  3.35948  4.48352  1.60295  4.06924  1.71770  3.29328  4.28554  4.50523  4.21524  4.10410  3.45599  3.20183  2.00553  4.99383  3.81574    470 i - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    255   2.91099  3.35807  4.39311  3.80506  3.12662  3.41984  4.32058  2.74098  2.62412  1.35983  2.89439  3.70896  4.38574  3.55146  3.43970  3.02120  3.14303  2.30767  4.99433  3.18557    471 l - - H
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    256   2.62697  4.23577  2.94369  2.01671  4.73311  3.52212  3.12192  4.21804  1.98366  3.41966  4.43054  2.33684  4.05005  2.29561  2.80226  2.65814  2.90658  3.41449  5.82302  4.41298    472 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  5.01631  5.73865  0.61958  0.77255  0.48576  0.95510
+    257   2.67425  5.24257  2.69003  2.64384  4.09422  3.68496  1.59772  3.97575  2.52719  3.06621  3.97630  3.17995  4.07682  2.88982  2.86815  2.31165  2.78826  3.60139  5.71188  4.10399    473 h - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.18941  5.01631  1.79622  0.61958  0.77255  0.48576  0.95510
+    258   2.81850  5.23743  2.76502  2.35780  4.54654  3.39495  3.79640  3.57339  2.36240  3.35446  4.28834  3.08690  1.61434  2.83455  2.79066  2.56844  3.04975  3.37601  5.69198  4.30446    474 p - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01183  4.83873  5.56107  0.61958  0.77255  0.86853  0.54398
+    259   3.22678  4.58175  4.94227  4.40609  1.73776  4.31983  4.20406  3.01156  4.21650  2.23279  3.33965  4.33985  4.66520  3.80636  4.24184  3.63704  3.45535  2.61441  1.69956  1.91736    475 w - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01183  4.83873  5.56107  0.61958  0.77255  0.30436  1.33787
+    260   2.99670  4.36610  4.95290  4.33956  1.72799  3.71830  4.50360  1.92619  4.12878  1.69258  2.79907  4.33234  4.52551  4.25179  4.13733  3.48573  3.22794  1.98896  4.25850  3.81122    476 l - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00667  5.01308        *  0.61958  0.77255  0.00000        *
+//
diff --git a/test/jalview/io/test_fn3_hmm.txt b/test/jalview/io/test_fn3_hmm.txt
new file mode 100644 (file)
index 0000000..0dcfa22
--- /dev/null
@@ -0,0 +1,285 @@
+HMMER3/f [3.1b1 | May 2013]
+NAME  fn3
+ACC   PF00041.13
+DESC  Fibronectin type III domain
+LENG  86
+ALPH  amino
+RF    no
+MM    no
+CONS  yes
+CS    yes
+MAP   yes
+DATE  Fri Jun 20 08:22:31 2014
+NSEQ  106
+EFFN  11.415833
+CKSUM 3564431818
+GA    8.00 7.20
+TC    8.00 7.20
+NC    7.90 7.90
+STATS LOCAL MSV       -9.4043  0.71847
+STATS LOCAL VITERBI   -9.7737  0.71847
+STATS LOCAL FORWARD   -3.8341  0.71847
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   2.70330  4.91262  3.03272  2.64079  3.60307  2.84344  3.74204  3.07942  2.79841  2.65364  4.14864  2.95826  2.87120  3.02176  2.96125  2.44783  2.59757  2.57680  4.02726  3.21526
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.00000        *
+      1   3.16986  5.21447  4.52134  3.29953  4.34285  4.18764  4.30886  3.35801  3.70246  2.11675  4.32057  4.32984  0.76706  3.91880  4.22437  3.23552  3.21670  2.88223  5.80355  3.93889      1 p - - -
+          2.68629  4.42236  2.77530  2.73088  3.46365  2.40512  3.72505  3.29365  2.67737  2.69316  4.24701  2.90358  2.73734  3.18157  2.89812  2.37898  2.77517  2.98515  4.58488  3.61514
+          0.09796  2.38361  6.81068  0.10064  2.34607  0.48576  0.95510
+      2   2.70230  5.97353  2.24744  2.62947  5.31433  2.60356  4.43584  4.79731  3.17221  2.95090  5.01531  3.26630  2.09873  3.30219  3.34190  1.45782  3.14099  3.57507  6.40877  4.25623      3 s - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+      3   1.38116  5.98285  3.50784  2.54546  5.32790  3.48945  4.43311  4.81385  2.38773  3.98773  5.02352  3.27895  1.92260  2.69012  2.96119  2.64228  3.29228  3.29618  6.41555  4.20553      4 a - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+      4   3.32856  5.10403  4.47046  4.60386  4.23079  4.75438  5.09647  2.69918  4.46632  2.97102  4.23502  4.77984  0.63388  4.68581  3.76781  4.05413  3.46306  2.04533  5.75329  4.56372      5 P - - -
+          2.68616  4.42236  2.77530  2.73134  3.46365  2.40523  3.72505  3.29295  2.67751  2.69303  4.24634  2.90357  2.73739  3.18157  2.89783  2.37897  2.77530  2.98529  4.58488  3.61514
+          0.09682  2.39494  6.81068  0.10162  2.33687  0.48576  0.95510
+      5   2.95325  4.65976  3.57762  2.20709  3.14816  2.51487  3.41109  4.78902  2.65862  3.19599  4.41042  3.45032  3.44719  2.43205  2.26492  2.25400  2.23196  3.66828  4.80003  4.52485      7 e - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01246  6.08833  4.59386  0.61958  0.77255  0.48576  0.95510
+      6   2.74215  5.97618  2.19482  2.69150  4.58171  2.33553  3.83525  3.28222  2.95080  3.32698  5.01691  1.45822  3.52462  2.79670  2.90942  3.13467  3.27956  4.36668  6.40902  3.92307      8 n - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00341  6.07928  6.80162  0.61958  0.77255  0.44282  1.02784
+      7   3.32507  4.98102  3.78072  3.31043  2.85257  4.76439  5.09585  2.50332  4.69760  1.03851  3.36125  4.91001  2.73206  4.83820  4.72389  4.07376  3.83146  1.59790  5.60385  4.42704      9 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.03752  6.08833  3.36505  0.61958  0.77255  0.48576  0.95510
+      8   3.15997  4.90658  3.35204  2.72775  4.53493  3.60960  2.65074  3.69535  2.11078  4.01384  4.99896  3.14668  4.61695  2.40643  2.34723  1.92358  2.03982  3.05153  6.39127  4.98082     10 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00349  6.05430  6.77664  0.61958  0.77255  0.35749  1.20207
+      9   1.76295  3.98252  5.55061  4.93551  2.13202  3.39992  5.09294  2.14638  4.23898  2.23988  3.42109  4.92079  4.39252  3.70640  3.99349  3.50811  3.63432  1.47830  4.51148  4.41177     11 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     10   3.01387  4.98892  2.91217  2.42744  5.22342  4.00576  3.74074  2.67275  2.47618  2.92529  3.89570  3.36720  4.15809  3.26810  2.80854  1.81572  2.02040  2.77133  4.92415  4.31555     12 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     11   2.41247  5.98374  2.24093  2.04842  3.41543  2.59695  4.10033  4.81544  3.05501  4.28918  5.02429  2.22829  2.90635  3.12939  3.01921  2.37278  3.01194  3.02522  6.41619  3.66635     13 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.06784  6.08833  2.75947  0.61958  0.77255  0.48576  0.95510
+     12   2.77725  4.36386  3.23435  2.92496  4.75140  4.31852  4.53101  1.91389  3.01135  2.51491  3.47932  2.97934  3.54432  2.88257  2.68923  3.07794  2.71169  1.80880  6.06849  4.75973     14 v - - E
+          2.68623  4.42259  2.77533  2.73059  3.46323  2.40500  3.72529  3.29333  2.67757  2.69369  4.24724  2.90364  2.73744  3.18108  2.89835  2.37891  2.77531  2.98493  4.58511  3.61510
+          0.22113  1.62346  6.74643  0.44471  1.02446  0.29166  1.37446
+     13   3.17575  5.97994  3.10322  2.84738  5.32369  2.00173  4.43389  4.80873  3.01150  3.75019  4.42663  2.23751  4.64014  2.74467  2.85546  1.99984  1.67133  4.36987  5.08444  3.58488     18 t - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     14   2.14203  5.98483  2.53686  2.20884  5.33077  2.76253  3.61799  4.81737  2.91636  3.96256  5.02525  2.79307  2.44932  3.35978  3.34773  1.76758  2.51815  4.12754  4.53404  4.35768     19 s - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02274  6.08833  3.90154  0.61958  0.77255  0.48576  0.95510
+     15   3.45984  5.96297  2.32904  2.98671  5.30486  3.14893  3.59992  3.68067  2.95954  4.26690  5.00447  2.01875  4.62814  3.51950  2.89162  1.97303  1.47565  3.87160  6.39755  3.59993     20 t - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00344  6.06904  6.79138  0.61958  0.77255  0.40302  1.10352
+     16   2.82603  5.98256  2.96292  2.53617  4.39559  3.14388  4.09532  4.81336  2.59005  4.28779  5.02326  3.12547  4.63955  2.76620  3.03618  1.28432  2.14874  3.55065  4.82032  3.29405     21 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     17   2.29842  4.96481  5.56794  4.95172  2.45211  3.33868  4.31545  1.97312  4.73378  1.34355  2.88087  4.92903  5.12608  4.86259  4.73754  4.07438  3.55674  1.71344  5.58685  3.78328     22 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     18   2.99930  4.68496  3.44656  2.53427  3.92510  4.04338  3.13178  3.10095  3.07007  2.68343  3.58599  3.17010  4.15335  2.63450  2.71950  2.30083  2.03779  2.23518  4.92400  3.84991     23 t - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     19   3.61302  4.50726  5.99163  5.39935  2.74200  5.23506  5.61087  1.85210  5.20622  1.10188  3.56291  5.40353  5.56305  5.33364  5.22134  4.56441  3.85751  1.20867  6.05610  4.88933     24 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     20   2.90508  5.98486  3.37356  2.37027  5.33083  3.46854  3.40789  4.81743  2.65230  3.22177  4.17825  2.67373  4.63906  2.52648  2.39431  1.50547  2.16764  4.37598  5.01440  5.00552     25 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     21   5.54218  6.63564  6.83891  6.61797  2.57168  6.35073  5.30532  3.96643  6.37377  3.18917  5.26281  6.07009  4.35269  6.11992  6.21105  5.74680  5.75143  4.86346  0.24436  3.66807     26 W - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     22   3.10862  5.98423  2.61761  2.10221  4.46995  3.93009  3.84182  3.79843  2.19229  3.09373  4.47555  2.66452  4.06864  2.59255  2.99987  1.99073  2.05618  3.67318  6.41655  4.47364     27 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     23   2.05280  5.87614  3.77610  2.63103  5.17655  3.25143  3.55897  3.09279  2.68152  3.35866  4.92882  3.43937  1.44938  3.29932  2.98336  2.85751  2.78420  3.02863  4.43359  4.47040     28 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     24   2.65644  5.88726  3.37601  2.96202  5.19190  3.53629  4.46143  4.20046  2.87295  2.91429  4.93884  3.27084  1.07653  3.39095  3.69926  2.07705  3.29723  2.92337  6.34501  4.95985     29 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.32165  6.08833  1.29911  0.61958  0.77255  0.48576  0.95510
+     25   2.44244  5.77880  2.64467  2.21733  3.20572  3.66340  3.27861  4.60939  2.17717  2.95495  4.81953  3.17778  2.62084  2.96028  2.64039  2.55818  2.51134  3.52175  6.21164  4.20060     30 k - - S
+          2.68557  4.42278  2.77404  2.73101  3.46370  2.40555  3.72548  3.29364  2.67779  2.69337  4.24743  2.90378  2.73741  3.18051  2.89835  2.37928  2.77557  2.98526  4.58530  3.61392
+          1.12909  0.74203  1.60680  0.24883  1.51281  1.67198  0.20810
+     26   2.76497  5.64634  2.06362  2.44254  4.06108  3.91520  3.28362  3.73992  2.46695  3.95074  4.68755  2.93859  2.87257  2.71813  3.08100  2.16767  2.28242  3.00778  6.08028  4.00221     33 d - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00580  5.54941  6.27176  0.61958  0.77255  1.23854  0.34222
+     27   2.94904  4.81418  2.88265  2.99332  4.83563  1.49306  3.87927  3.68299  2.59086  2.53757  4.07313  2.89409  2.92833  3.04624  2.88166  2.82744  3.22994  3.21444  4.18838  4.04981     34 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00508  5.68074  6.40309  0.61958  0.77255  0.20085  1.70396
+     28   2.96588  5.95917  2.45759  2.65943  5.30490  1.91234  3.11781  3.44164  2.78003  4.01788  4.99965  2.12379  3.26764  2.84040  2.87160  2.26272  3.15721  3.84740  4.86821  3.64128     35 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00351  6.05095  6.77330  0.61958  0.77255  0.34864  1.22298
+     29   2.45969  5.96445  2.88574  2.61716  5.30131  1.53630  4.43818  3.46883  2.98393  3.60366  4.33332  3.25719  4.28257  3.08417  2.40821  2.08539  3.20111  2.81105  6.40207  4.25093     36 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     30   2.71563  5.98438  2.50720  1.96228  5.33013  3.47246  3.38617  3.68065  2.34086  4.03521  4.36775  3.08029  1.87041  2.90329  3.33287  2.70645  2.77558  2.97286  6.41666  4.17777     37 p - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     31   3.04837  4.96747  2.67316  4.92527  2.42064  4.14655  5.09097  1.34551  4.71334  2.18068  3.34829  4.91585  3.31178  4.15082  4.72663  3.69906  3.53551  1.88881  3.93067  3.04034     38 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     32   2.89418  4.58676  2.12720  2.65970  3.99748  4.30700  4.50689  3.12934  2.75241  2.32504  4.34805  3.25317  4.69765  2.93544  2.97945  2.48570  1.85268  2.60501  5.05387  4.40931     39 t - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     33   3.05045  5.96458  3.15002  2.79713  4.16491  1.56242  3.20986  4.07540  2.34002  3.77648  3.66616  2.65073  4.64351  3.21500  2.62101  2.34442  2.98761  3.25677  5.04578  3.36446     40 g - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     34   4.72855  5.99332  6.32397  5.91516  1.75259  5.71064  3.47111  3.64093  3.68669  3.44300  5.13564  5.60513  6.03397  5.64902  5.63703  5.05114  4.95149  3.46344  3.97019  0.49348     41 Y - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     35   3.03913  5.98101  2.74983  1.95825  3.75604  3.98121  3.78677  3.48894  2.41418  2.89991  5.02189  2.66693  4.63989  2.65222  2.20113  2.26435  2.64729  3.16844  6.41420  3.93691     42 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     36   3.75576  5.90373  6.92143  6.48083  5.27028  6.48043  7.33031  1.16190  6.46559  1.72148  4.18143  6.65320  6.63807  6.70678  6.63399  5.94811  5.00240  0.82398  7.51298  6.31097     43 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.04904  6.08833  3.08806  0.61958  0.77255  0.48576  0.95510
+     37   2.63429  4.71789  2.89387  2.04081  4.17861  4.22097  3.38514  2.92734  2.36124  3.26230  4.97143  3.69265  4.61418  2.27063  2.38238  2.70982  2.11937  3.37005  6.36711  3.73375     44 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00353  6.04282  6.76517  0.61958  0.77255  0.32899  1.27173
+     38   3.00800  2.48193  5.53105  4.91728  2.98458  4.75707  4.15268  2.83469  3.12355  2.32451  4.08737  4.91198  5.12099  4.84273  3.46182  3.58557  3.26283  2.28912  2.64554  1.29971     45 y - - E
+          2.68641  4.42053  2.77543  2.73091  3.46377  2.40536  3.72420  3.29377  2.67764  2.69378  4.24582  2.90336  2.73763  3.18113  2.89660  2.37910  2.77543  2.98542  4.58500  3.61526
+          0.12278  2.16768  6.81068  0.54422  0.86820  0.48576  0.95510
+     39   2.52553  4.02469  3.60093  2.65311  4.18239  3.63052  4.17989  3.44209  2.14970  3.12319  4.96260  3.43770  4.02454  2.12722  2.15391  2.32250  3.25888  3.40611  2.73453  3.57446     49 q - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02932  6.08833  3.62591  0.61958  0.77255  0.48576  0.95510
+     40   2.85303  5.96779  2.74660  1.91867  5.31393  3.60453  3.59476  4.33679  2.36399  3.57280  3.62613  2.83996  2.06170  2.51716  3.24767  2.43993  2.61505  3.39964  6.39984  4.98824     50 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00347  6.06248  6.78482  0.61958  0.77255  0.67944  0.70705
+     41   2.55267  3.11480  2.72137  2.31006  5.21613  3.67111  4.08491  3.18721  2.11932  2.73126  3.73119  3.72342  3.44703  2.78931  3.31632  2.60923  2.50487  2.33623  6.34948  4.95631     51 k - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.09789  6.06248  2.39773  0.61958  0.77255  0.38130  1.14877
+     42   3.41114  5.92195  2.00739  2.51445  4.35946  1.85149  4.04885  4.75310  3.02073  3.41727  4.96259  1.88803  4.57836  2.81891  2.77402  2.53317  2.79169  3.60394  6.35459  4.48731     52 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00370  5.99676  6.71911  0.61958  0.77255  0.25112  1.50474
+     43   2.89692  5.98568  2.39354  2.04021  5.33202  2.12548  3.27810  4.81889  2.36649  3.39071  4.58505  2.82477  4.63888  2.99231  3.02264  2.04514  2.45319  3.82791  6.41761  5.00590     53 e - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     44   3.10206  5.95088  3.50928  2.19657  4.70794  1.83640  4.44201  3.85177  2.81849  3.20653  3.64542  2.17673  2.82486  2.50236  3.67274  2.64718  2.71954  3.18909  6.39210  3.73228     54 g - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.08059  6.08833  2.58819  0.61958  0.77255  0.48576  0.95510
+     45   2.83017  5.93388  2.31048  1.94273  5.28007  2.46456  3.60606  4.35357  2.35448  2.95362  4.97424  3.06816  3.41033  2.69285  2.84649  2.21743  2.98206  3.67978  4.23187  4.95426     55 e - - C
+          2.68733  4.40831  2.77609  2.73043  3.46444  2.40393  3.72592  3.29278  2.67833  2.69426  4.24827  2.90407  2.73626  3.18024  2.89744  2.37952  2.77650  2.98614  4.58019  3.61570
+          1.45631  0.90375  1.01651  1.51529  0.24813  0.96821  0.47765
+     46   2.32839  5.42660  2.93163  2.28220  4.02543  3.04571  3.03894  3.22247  2.82753  3.21315  3.89864  3.49093  2.50859  3.14260  2.89921  2.69284  3.05772  2.38519  4.38264  3.20345     66 e - - G
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00571  5.56406  6.28641  0.61958  0.77255  0.18459  1.78051
+     47   2.69465  3.91083  2.42967  2.34662  4.43247  3.13708  3.62175  3.49809  2.56727  2.84127  4.03266  3.52527  3.00745  3.22895  3.52756  2.46523  2.69768  3.11906  2.19019  4.92515     67 w - - C
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00353  6.04443  6.76678  0.61958  0.77255  0.33269  1.26228
+     48   2.67627  5.86490  3.17099  3.22595  3.40616  4.27494  3.15476  3.07365  2.21494  2.78239  4.39426  2.30764  3.56160  2.19351  2.71433  2.94392  2.62330  2.55032  5.16850  4.37026     68 q - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     49   3.05871  5.98132  3.36643  1.71285  5.32569  3.90513  2.27648  4.25125  2.92072  4.28630  4.28595  2.90579  3.33969  2.79371  2.63337  2.42582  2.46361  2.81009  3.46671  3.72048     69 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     50   2.88323  4.65127  3.40086  2.73607  2.41972  3.62669  3.91044  2.52424  2.75640  2.37424  3.94274  3.64921  4.76561  2.38668  2.64750  3.03028  3.09193  2.27100  6.08057  3.03402     70 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     51   3.36533  5.95840  2.65995  2.86095  3.44584  4.25167  3.55888  2.90987  2.78397  3.33346  3.90493  2.03375  3.68901  2.80089  2.78508  2.11531  1.97302  3.15541  6.39763  4.99330     71 t - - E
+          2.68571  4.42246  2.77540  2.73144  3.46375  2.40497  3.72515  3.29375  2.67742  2.69376  4.24711  2.90367  2.73710  3.18135  2.89822  2.37876  2.77540  2.98457  4.58498  3.61524
+          0.06628  2.76409  6.81068  0.97562  0.47314  0.48576  0.95510
+     52   2.38926  5.49703  3.09615  2.80044  4.68202  3.76562  4.62096  2.19958  3.33274  2.03099  4.11871  2.97731  4.26404  3.83080  2.89542  3.19964  2.84193  1.56133  6.03936  4.75239     75 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     53   2.79501  4.92737  2.35659  2.60299  4.06511  2.98090  3.88301  4.80087  2.44593  3.57653  5.01701  3.37711  1.76863  3.53250  3.23955  1.94419  2.68524  2.95506  6.41016  5.00121     76 p - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     54   2.44542  5.98316  3.18675  2.55065  5.32835  2.12386  4.43300  4.38419  2.38413  2.93654  5.02378  2.50193  2.49139  3.27226  2.05133  2.36069  3.70337  4.12664  4.32391  4.48343     77 r - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     55   2.77179  5.98329  2.50848  2.76644  5.32854  3.30269  3.08416  4.38434  3.16759  4.28865  5.02390  2.27915  2.67265  2.61285  3.32102  2.18277  1.82444  2.83013  6.41587  4.35727     78 t - - T
+          2.68621  4.42251  2.77546  2.73149  3.46380  2.40468  3.72521  3.29380  2.67767  2.69252  4.24716  2.90344  2.73766  3.18134  2.89827  2.37913  2.77442  2.98545  4.58503  3.61529
+          0.07130  2.69245  6.81068  1.11635  0.39671  0.48576  0.95510
+     56   2.32530  5.97446  3.57954  1.89562  5.31575  4.24796  3.32554  3.73278  2.42253  2.98562  5.01611  3.71687  3.10116  2.71107  2.91871  2.60336  1.70360  3.15915  6.40941  5.00074     83 t - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.35248  6.08833  1.22151  0.61958  0.77255  0.48576  0.95510
+     57   2.95924  5.72336  2.75932  2.63159  3.96190  3.29462  3.22449  3.40062  2.77057  3.18108  3.56769  2.46117  2.98702  3.05715  2.72729  2.56520  1.96589  3.52719  6.16591  2.96938     84 t - - S
+          2.68622  4.42162  2.77539  2.73088  3.46349  2.40510  3.72514  3.29311  2.67761  2.69358  4.24710  2.90367  2.73759  3.18166  2.89753  2.37894  2.77488  2.98538  4.58497  3.61523
+          0.23969  1.55321  6.46298  0.15973  1.91309  0.51157  0.91517
+     58   2.78411  5.86726  3.65151  2.46190  3.55121  3.32277  3.71415  3.82816  2.87545  3.69015  4.27003  2.90512  3.60994  2.68731  2.37548  1.72361  1.95655  3.42914  6.30822  3.85760     87 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00384  5.96109  6.68344  0.61958  0.77255  0.21363  1.64843
+     59   2.13080  3.38910  5.56698  4.95075  2.41578  4.20194  3.39215  2.83299  4.73282  2.14660  3.19234  4.92810  5.12527  4.86169  4.18461  4.07348  3.03808  1.63705  4.66089  1.78369     88 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     60   3.12543  5.93769  2.99354  2.53954  5.26303  4.25659  3.21054  2.85212  2.62935  2.82513  3.45830  2.90110  4.19559  2.80624  3.10357  2.88568  1.50190  2.67316  6.38237  3.93622     89 t - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     61   3.61965  4.99402  3.63828  4.95783  2.26826  4.79135  5.12555  1.86647  4.74679  1.34194  3.74460  4.94944  5.15281  3.10559  4.75989  3.74420  3.85186  1.42486  5.61987  4.44474     90 l - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     62   3.10387  5.98366  3.06368  2.31445  4.05906  3.57903  3.48488  4.81531  2.43543  4.01680  3.91148  2.67498  2.99920  2.70828  2.56621  2.14984  1.90367  3.06274  4.55677  4.37671     91 t - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     63   3.34220  5.98744  1.86090  2.69096  5.33359  1.40608  3.63608  4.82043  2.48624  4.29318  4.45099  1.96888  4.06897  3.19339  3.48838  2.73868  3.70514  4.37867  6.41942  4.25681     92 g - - S
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     64   5.18530  6.36522  4.58586  6.57578  3.53131  6.59442  6.84537  2.81242  6.44067  0.21363  3.54046  6.78308  6.53953  6.19263  6.31583  5.99910  5.37457  3.39834  6.73440  5.83217     93 L - - -
+          2.68590  4.42227  2.77521  2.73125  3.46356  2.40515  3.72496  3.29356  2.67743  2.69357  4.24692  2.90349  2.73741  3.18148  2.89803  2.37889  2.77521  2.98520  4.58479  3.61505
+          0.02033  3.96193  6.81068  0.31431  1.31041  0.48576  0.95510
+     65   2.93361  5.98253  3.16620  2.07447  5.32745  3.84017  3.84639  3.52003  2.07989  2.71701  4.56317  3.05465  3.94228  2.39023  2.13660  2.81003  2.42767  2.98359  6.41531  3.87097     95 e - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     66   2.17452  5.85112  3.79178  2.44872  5.14174  4.27993  4.47388  4.59086  3.04069  4.13287  4.42378  3.36057  0.82761  3.31072  3.71538  3.13243  3.15654  3.97958  4.90439  4.38214     96 p - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     67   2.72531  5.97666  2.50761  2.79752  4.22378  1.29966  2.70734  4.80294  2.85170  4.02964  5.01805  2.59981  4.64085  3.32835  3.25617  2.51345  3.59137  3.91820  4.88800  2.97800     97 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     68   2.39925  4.86641  3.79312  2.74568  5.13568  4.27988  3.97435  3.95098  2.69562  3.65359  3.00813  3.38952  4.67180  3.12070  2.33490  2.43668  1.27713  2.90106  6.31421  4.93977     98 t - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     69   2.87821  5.98261  2.71299  2.05681  3.93787  3.36966  3.57296  3.81023  2.25993  3.03102  3.49917  2.39917  3.09415  2.62154  2.82818  2.67359  2.51409  3.67730  4.58228  5.00448     99 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     70   6.32206  7.09639  6.95855  7.01624  2.70399  6.62558  5.12529  5.84365  6.77161  4.03604  6.32463  4.32154  6.86163  6.30239  6.48747  6.05185  6.49899  5.81871  5.18730  0.14189    100 Y - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     71   3.06725  5.97020  3.56838  1.93769  4.23056  3.77167  3.39655  3.53252  2.56448  3.45405  4.47893  2.47898  4.64226  2.55695  2.74362  2.36648  2.01401  2.66399  6.40629  3.70776    101 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     72   3.19486  4.19171  5.58508  4.96942  1.44027  4.15574  5.11433  1.81297  4.75161  2.09873  3.37140  4.94615  5.14143  4.88008  4.75485  4.09123  3.83769  1.47173  5.60267  3.09551    102 f - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     73   3.18039  4.06436  3.15201  2.27681  5.32468  3.77041  3.95744  3.70705  2.23538  4.28548  4.11883  2.88564  4.63997  2.58030  1.70576  2.27707  2.39981  3.62914  4.03739  3.61343    103 r - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     74   3.76685  5.84337  6.79391  6.31497  3.84743  6.27452  6.95358  1.59565  6.25134  2.11498  4.30213  6.44619  6.46746  6.44080  6.36566  5.70153  4.90672  0.53161  7.20703  6.03175    104 V - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     75   2.31005  4.88319  3.40675  2.63903  3.58848  4.26911  3.49342  3.09105  2.39543  3.00463  4.93879  3.41670  4.66153  1.98639  2.22144  3.11070  2.61129  2.78609  6.34491  2.92043    105 q - - E
+          2.68621  4.42228  2.77522  2.73126  3.46357  2.40516  3.72497  3.29357  2.67744  2.69312  4.24693  2.90350  2.73743  3.18149  2.89804  2.37890  2.77522  2.98521  4.58480  3.61506
+          0.03738  3.58894  4.70319  0.25135  1.50394  0.48576  0.95510
+     76   0.79499  5.17226  4.80437  3.93376  4.33120  3.17569  4.96596  3.30029  4.15773  2.86829  4.32533  4.52823  5.01384  4.41886  4.42465  2.28661  2.19420  2.76335  5.81537  4.60940    107 a - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00340  6.08033  6.80268  0.61958  0.77255  0.44741  1.01966
+     77   2.84306  4.15407  4.98296  3.19564  3.23517  4.66606  3.67524  2.49603  3.23224  2.21212  3.84796  4.62630  5.03633  2.88555  2.43211  3.49088  3.22704  1.33993  5.67363  2.88123    108 v - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     78   3.27061  4.75060  2.81965  3.10410  5.31934  3.95942  3.99367  3.88107  2.62205  4.02823  5.01830  1.35628  3.08534  3.32913  3.08546  1.88067  2.21938  4.02145  6.41122  3.76470    109 n - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00992  6.08833  4.87890  0.61958  0.77255  0.48576  0.95510
+     79   2.39836  4.69624  2.57939  2.56364  4.72011  1.55094  3.74815  4.26504  2.61409  4.28517  5.02018  2.82673  4.26340  2.69971  2.87382  2.73983  2.53993  3.07568  6.41202  5.00074    110 g - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00340  6.08180  6.80415  0.61958  0.77255  0.45398  1.00812
+     80   2.22211  4.94595  2.69424  2.44928  4.84552  2.32005  3.06335  2.95704  2.33976  3.55821  4.09648  2.87488  4.09930  2.85656  2.97877  3.08279  3.07714  2.39346  6.40553  4.01548    111 a - - T
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.38398  6.08833  1.15015  0.61958  0.77255  0.48576  0.95510
+     81   2.96920  5.43248  3.46409  2.76236  3.93019  1.02513  4.29951  4.07917  3.10833  3.39330  4.50784  3.40559  4.48149  3.45547  3.56956  2.50552  2.39680  3.76237  5.94228  3.01765    112 g - - E
+          2.68652  4.42259  2.77488  2.73144  3.46170  2.40466  3.72529  3.29388  2.67703  2.69373  4.24724  2.90335  2.73774  3.18162  2.89801  2.37895  2.77529  2.98552  4.58511  3.61537
+          0.29928  1.35852  6.43163  0.48724  0.95274  1.77816  0.18506
+     82   2.84273  4.18992  3.05053  2.12883  3.36284  2.75189  4.25559  3.03507  2.66396  2.65563  4.59024  2.63081  3.84786  2.76844  3.19058  2.84597  2.82776  2.89016  3.06821  3.41272    117 e - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00494  5.70928  6.43163  0.61958  0.77255  1.56406  0.23482
+     83   3.37920  5.61493  3.74277  3.20384  4.92800  1.01625  3.96683  4.35082  2.94708  2.91738  4.72998  3.71516  3.70329  3.52945  3.00683  1.61777  3.61882  4.00263  6.13360  4.80162    118 g - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00481  5.73459  6.45693  0.61958  0.77255  0.30535  1.33510
+     84   2.58463  5.93784  3.04102  2.09605  4.57465  2.51630  3.24109  4.26208  2.60545  3.44607  3.62705  3.20484  1.89678  2.68661  2.74662  2.97880  3.02092  3.23569  6.37074  4.50367    119 p - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00361  6.02233  6.74467  0.61958  0.77255  0.28862  1.38348
+     85   2.48488  5.72055  3.87501  1.97538  3.04853  3.48010  4.51877  3.51898  2.88839  2.73568  4.42660  3.64380  2.08811  3.48814  2.70856  2.40769  2.92982  4.05679  2.77386  3.43366    120 e - - B
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00338  6.08833  6.81068  0.61958  0.77255  0.48576  0.95510
+     86   3.03720  5.94099  3.75455  2.96917  5.26587  2.91682  3.66571  4.11840  2.98472  4.23738  4.98891  3.74380  4.66031  3.40955  3.12788  0.72443  2.46104  4.32115  6.38683  4.99111    121 s - - E
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00227  6.08723        *  0.61958  0.77255  0.00000        *
+//
index 79d622c..489916e 100644 (file)
@@ -40,6 +40,7 @@ import javax.swing.JInternalFrame;
 
 import org.testng.Assert;
 import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -52,6 +53,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GeneLocus;
+import jalview.datamodel.HiddenMarkovModel;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.PDBEntry;
@@ -92,9 +94,15 @@ import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
+import junit.extensions.PA;
 @Test(singleThreaded = true)
 public class Jalview2xmlTests extends Jalview2xmlBase
 {
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  }
 
   @Override
   @BeforeClass(alwaysRun = true)
@@ -154,13 +162,14 @@ public class Jalview2xmlTests extends Jalview2xmlBase
             DataSourceType.FILE);
     assertNotNull(af, "Didn't read input file " + inFile);
     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
-    AlignViewport viewport = af.getViewport();
+    AlignViewportI viewport = af.getViewport();
     assertSame(viewport.getGlobalColourScheme().getClass(),
             TCoffeeColourScheme.class, "Didn't set T-coffee colourscheme");
     assertNotNull(
             ColourSchemeProperty.getColourScheme(viewport,
                     viewport.getAlignment(),
-                    viewport.getGlobalColourScheme().getSchemeName()),
+                    viewport.getGlobalColourScheme()
+                            .getSchemeName()),
             "Recognise T-Coffee score from string");
 
     af.saveAlignment(tfile, FileFormat.Jalview);
@@ -276,7 +285,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
-    assertTrue(Desktop.getAlignFrames().length == 1 + origCount,
+    assertEquals(Desktop.getAlignFrames().length,
+            1 + origCount,
             "Didn't gather the views in the example file.");
 
   }
@@ -410,7 +420,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" }, enabled = true)
   public void testStoreAndRecoverExpandedviews() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
@@ -438,7 +448,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -449,8 +459,9 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     Assert.assertEquals(Desktop.getAlignFrames().length,
             Desktop.getAlignmentPanels(
                     af.getViewport().getSequenceSetId()).length);
-    Assert.assertEquals(Desktop
-            .getAlignmentPanels(af.getViewport().getSequenceSetId()).length,
+    Assert.assertEquals(
+            Desktop.getAlignmentPanels(
+                    af.getViewport().getSequenceSetId()).length,
             oldviews);
   }
 
@@ -463,7 +474,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverReferenceSeqSettings() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
@@ -502,7 +513,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -596,7 +607,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverGroupRepSeqs() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
@@ -671,7 +682,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -716,7 +727,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverPDBEntry() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     String exampleFile = "examples/3W5V.pdb";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
             DataSourceType.FILE);
@@ -765,7 +776,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -803,11 +814,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
               "Mismatch PDBEntry 'Type'");
       Assert.assertNotNull(recov.getFile(),
               "Recovered PDBEntry should have a non-null file entry");
-      Assert.assertEquals(
-              recov.getFile().toLowerCase(Locale.ENGLISH)
-                      .lastIndexOf("pdb"),
-              recov.getFile().length() - 3,
-              "Recovered PDBEntry file should have PDB suffix");
+      Assert.assertEquals(recov.getFile().toLowerCase(Locale.ENGLISH).lastIndexOf("pdb"),recov.getFile().length()-3, "Recovered PDBEntry file should have PDB suffix");
     }
   }
 
@@ -821,11 +828,11 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverColourThresholds() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
 
-    AlignViewport av = af.getViewport();
+    AlignViewportI av = af.getViewport();
     AlignmentI al = av.getAlignment();
 
     /*
@@ -884,7 +891,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
             ".jvp");
     tfile.deleteOnExit();
     new Jalview2XML(false).saveState(tfile);
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
     Assert.assertNotNull(af, "Failed to reload project");
@@ -1071,6 +1078,59 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   }
 
   /**
+   * Load an HMM profile to an alignment, and confirm it is correctly restored
+   * when reloaded from project
+   * 
+   * @throws IOException
+   */
+  @Test(groups = { "Functional" })
+  public void testStoreAndRecoverHmmProfile() throws IOException
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+  
+    AlignViewportI av = af.getViewport();
+    AlignmentI al = av.getAlignment();
+
+    /*
+     * mimic drag and drop of hmm file on to alignment
+     */
+    AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.hmm", DataSourceType.FILE);
+    al.insertSequenceAt(0,
+            af2.getViewport().getAlignment().getSequenceAt(0));
+
+    /*
+     * check it loaded in
+     */
+    SequenceI hmmSeq = al.getSequenceAt(0);
+    assertTrue(hmmSeq.hasHMMProfile());
+    HiddenMarkovModel hmm = hmmSeq.getHMM();
+    assertSame(hmm.getConsensusSequence(), hmmSeq);
+
+    /*
+     * save project, close windows, reload project, verify
+     */
+    File tfile = File.createTempFile("testStoreAndRecoverHmmProfile",
+            ".jvp");
+    tfile.deleteOnExit();
+    new Jalview2XML(false).saveState(tfile);
+    Desktop.getInstance().closeAll_actionPerformed(null);
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+    Assert.assertNotNull(af, "Failed to reload project");
+
+    hmmSeq = al.getSequenceAt(0);
+    assertTrue(hmmSeq.hasHMMProfile());
+    assertSame(hmm.getConsensusSequence(), hmmSeq);
+    Mapping mapToHmmConsensus = (Mapping) PA.getValue(hmm,
+            "mapToHmmConsensus");
+    assertNotNull(mapToHmmConsensus);
+    assertSame(mapToHmmConsensus.getTo(), hmmSeq.getDatasetSequence());
+  }
+
+  /**
    * pre 2.11 - jalview 2.10 erroneously created new dataset entries for each
    * view (JAL-3171) this test ensures we can import and merge those views
    */
@@ -1093,7 +1153,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testMergeDatasetsforManyViews() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
     // complex project - one dataset, several views on several alignments
     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
@@ -1137,7 +1197,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = "Functional")
   public void testPcaViewAssociation() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     final String PCAVIEWNAME = "With PCA";
     // create a new tempfile
     File tempfile = File.createTempFile("jvPCAviewAssoc", "jvp");
@@ -1171,10 +1231,10 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     }
 
     // load again.
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             tempfile.getCanonicalPath(), DataSourceType.FILE);
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
+    JInternalFrame[] frames = Desktop.getInstance().getAllFrames();
     // PCA and the tabbed alignment view should be the only two windows on the
     // desktop
     assertEquals(frames.length, 2,
@@ -1204,12 +1264,12 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverGeneLocus() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     String seqData = ">P30419\nACDE\n>X1235\nGCCTGTGACGAA";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
             DataSourceType.PASTE);
     assertNotNull(af, "Didn't read in the example file correctly.");
-
+  
     AlignmentViewPanel ap = Desktop.getAlignmentPanels(null)[0];
     SequenceI pep = ap.getAlignment().getSequenceAt(0);
     SequenceI cds = ap.getAlignment().getSequenceAt(1);
@@ -1240,8 +1300,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
-
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  
     new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
     AlignmentViewPanel rap = Desktop.getAlignmentPanels(null)[0];
index 3931dbf..518fdef 100644 (file)
@@ -22,6 +22,7 @@ package jalview.renderer;
 
 import static org.testng.AssertJUnit.assertEquals;
 
+import jalview.api.AlignViewportI;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -56,14 +57,16 @@ public class OverviewResColourFinderTest
   {
     SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new OverviewResColourFinder();
 
     // gaps are grey, residues white
     assertEquals(Color.white, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 0, null));
-    assertEquals(Color.lightGray, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 2, null));
+            av.getResidueShading(),
+            null, seq, 0, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 2,
+                    null));
 
     // unaffected by showBoxes setting
     assertEquals(Color.white, rcf.getResidueColour(false,
@@ -78,23 +81,28 @@ public class OverviewResColourFinderTest
     SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
                                                             // gap
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new OverviewResColourFinder();
     av.setGlobalColourScheme(new ZappoColourScheme());
 
     // @see ResidueProperties.zappo
-    assertEquals(Color.pink, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 0, null)); // M
-    assertEquals(Color.green, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 2, null)); // T
-    assertEquals(Color.magenta, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 5, null)); // G
-    assertEquals(Color.orange, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 12, null)); // F
+    assertEquals(Color.pink,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 0, null)); // M
+    assertEquals(Color.green,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 2, null)); // T
+    assertEquals(Color.magenta,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 5, null)); // G
+    assertEquals(Color.orange,
+            rcf.getResidueColour(true, av.getResidueShading(),
+            null, seq, 12, null)); // F
 
     // gap colour not specified so gaps are lightGray
-    assertEquals(Color.lightGray, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 3, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
 
     // unaffected by showBoxes setting
     assertEquals(Color.pink, rcf.getResidueColour(false,
@@ -107,8 +115,9 @@ public class OverviewResColourFinderTest
             av.getResidueShading(), null, seq, 12, null)); // F
 
     // gap colour not specified so gaps are lightGray
-    assertEquals(Color.lightGray, rcf.getResidueColour(false,
-            av.getResidueShading(), null, seq, 3, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(false, av.getResidueShading(), null, seq, 3,
+                    null));
 
   }
 
@@ -118,7 +127,7 @@ public class OverviewResColourFinderTest
     SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
                                                             // gap
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new OverviewResColourFinder();
 
     Color[] newColours = new Color[24];
@@ -130,15 +139,17 @@ public class OverviewResColourFinderTest
     av.setGlobalColourScheme(new UserColourScheme(newColours));
 
     // gap colour not specified so gaps are lightGray
-    assertEquals(Color.lightGray, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 3, null));
+    assertEquals(Color.lightGray, rcf
+            .getResidueColour(true, av.getResidueShading(), null, seq, 3,
+                    null));
 
     newColours[23] = Color.pink;
     av.setGlobalColourScheme(new UserColourScheme(newColours));
 
     // gap colour specified as pink
     assertEquals(Color.pink, rcf.getResidueColour(true,
-            av.getResidueShading(), null, seq, 3, null));
+            av.getResidueShading(),
+            null, seq, 3, null));
 
     // unaffected by showBoxes setting
     // gap colour not specified so gaps are lightGray
@@ -159,7 +170,7 @@ public class OverviewResColourFinderTest
   {
     SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-
+    
     ColourSchemeI cs = new ZappoColourScheme();
     ArrayList<SequenceI> seqlist = new ArrayList<>();
     seqlist.add(seq);
@@ -168,10 +179,10 @@ public class OverviewResColourFinderTest
     al.addGroup(sg);
     SequenceGroup[] groups = new SequenceGroup[1];
     groups[0] = sg;
-
-    final AlignViewport av = new AlignViewport(al);
+    
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new OverviewResColourFinder();
-
+    
     // G in group specified as magenta in Zappo
     assertEquals(Color.magenta, rcf.getResidueColour(false,
             av.getResidueShading(), groups, seq, 7, null));
@@ -186,7 +197,7 @@ public class OverviewResColourFinderTest
 
     // use legacy colouring
     rcf = new OverviewResColourFinder(true, Color.blue, Color.red);
-
+  
     // G in group specified as magenta in Zappo
     assertEquals(Color.magenta, rcf.getResidueColour(false,
             av.getResidueShading(), groups, seq, 7, null));
@@ -220,8 +231,6 @@ public class OverviewResColourFinderTest
   {
     SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
                                                             // gap
-    AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
 
     // non-legacy colouring
     ResidueColourFinder rcf = new OverviewResColourFinder();
@@ -259,9 +268,7 @@ public class OverviewResColourFinderTest
 
     // gaps gap colour
     c = rcf.getBoxColour(shader, seq, 3);
-    assertEquals(
-            jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP,
-            c);
+    assertEquals(OverviewResColourFinder.OVERVIEW_DEFAULT_GAP, c);
 
     // non legacy colouring with colour scheme
     rcf = new OverviewResColourFinder(false, Color.blue, Color.red);
index d8efc02..9d8ecbe 100644 (file)
@@ -22,6 +22,7 @@ package jalview.renderer;
 
 import static org.testng.AssertJUnit.assertEquals;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
@@ -30,7 +31,6 @@ import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
-
 import java.awt.Color;
 
 import org.testng.annotations.BeforeClass;
@@ -51,7 +51,7 @@ public class ResidueColourFinderTest
   {
     SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new ResidueColourFinder();
     av.setGlobalColourScheme(new ZappoColourScheme());
 
@@ -81,7 +81,7 @@ public class ResidueColourFinderTest
   {
     SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new ResidueColourFinder();
 
     assertEquals(Color.white, rcf.getResidueColour(true,
@@ -102,7 +102,7 @@ public class ResidueColourFinderTest
     SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a
                                                             // gap
     AlignmentI al = new Alignment(new SequenceI[] { seq });
-    final AlignViewport av = new AlignViewport(al);
+    final AlignViewportI av = new AlignViewport(al);
     ResidueColourFinder rcf = new ResidueColourFinder();
 
     Color[] newColours = new Color[24];
index ca69d37..47d5ff1 100644 (file)
@@ -26,10 +26,10 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.renderer.ScaleRenderer.ScaleMark;
+import jalview.viewmodel.AlignmentViewport;
 
 import java.util.List;
 
@@ -43,7 +43,7 @@ public class ScaleRendererTest
     String data = ">Seq/20-45\nABCDEFGHIJKLMNOPQRSTUVWXYS\n";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(data,
             DataSourceType.PASTE);
-    AlignViewport av = af.getViewport();
+    AlignmentViewport av = af.getViewport();
 
     /*
      * scale has minor ticks at 5, 15, 25, major at 10 and 20
index 637bbf4..c22ce7d 100644 (file)
@@ -25,10 +25,10 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
-
 import java.awt.Color;
 import java.util.List;
 
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
@@ -37,13 +37,14 @@ import jalview.api.FeatureColourI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
+import jalview.api.AlignViewportI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.schemes.FeatureColour;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
+
 /**
  * Unit tests for feature colour determination, including but not limited to
  * <ul>
@@ -63,7 +64,7 @@ import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
  */
 public class FeatureColourFinderTest
 {
-  private AlignViewport av;
+  private AlignViewportI av;
 
   private SequenceI seq;
 
@@ -73,12 +74,13 @@ public class FeatureColourFinderTest
 
   private FeatureRendererModel fr;
 
-  @BeforeTest(alwaysRun = true)
+  @BeforeClass(alwaysRun = true)
   public void setUp()
   {
     // aligned column 8 is sequence position 6
     String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
-    af = new FileLoader().LoadFileWaitTillLoaded(s, DataSourceType.PASTE);
+    af = new FileLoader().LoadFileWaitTillLoaded(s,
+            DataSourceType.PASTE);
     av = af.getViewport();
     seq = av.getAlignment().getSequenceAt(0);
     fr = af.getFeatureRenderer();
@@ -153,8 +155,8 @@ public class FeatureColourFinderTest
   @Test(groups = "Functional")
   public void testFindFeatureColour_gapPosition()
   {
-    seq.addSequenceFeature(
-            new SequenceFeature("Metal", "Metal", 2, 12, 0f, null));
+    seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, 0f,
+            null));
     fr.setColour("Metal", new FeatureColour(Color.red));
     fr.featuresAdded();
     av.setShowSequenceFeatures(true);
@@ -182,7 +184,6 @@ public class FeatureColourFinderTest
     Color c = finder.findFeatureColour(null, seq, 15);
     assertEquals(c, fr.getColor(sf2, fc));
   }
-
   @Test(groups = "Functional")
   public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
   {
@@ -305,8 +306,8 @@ public class FeatureColourFinderTest
     /*
      * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
      */
-    seq.addSequenceFeature(new SequenceFeature("Disulphide Bond", "Contact",
-            2, 12, Float.NaN, "Disulphide"));
+    seq.addSequenceFeature(new SequenceFeature("Disulphide Bond",
+            "Contact", 2, 12, Float.NaN, "Disulphide"));
     fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
     fr.featuresAdded();
     av.setShowSequenceFeatures(true);
@@ -351,12 +352,12 @@ public class FeatureColourFinderTest
   @Test(groups = "Functional")
   public void testFindFeatureColour_graduatedFeatureColour()
   {
-    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, 2,
-            0f, "KdGroup"));
-    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4, 4,
-            5f, "KdGroup"));
-    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7, 7,
-            10f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2,
+            2, 0f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4,
+            4, 5f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7,
+            7, 10f, "KdGroup"));
 
     /*
      * graduated colour from 0 to 10
@@ -396,7 +397,7 @@ public class FeatureColourFinderTest
     fr.setColour("Metal", red);
     fr.featuresAdded();
     av.setShowSequenceFeatures(true);
-
+  
     /*
      * the FeatureSettings transparency slider has range 0-70 which
      * corresponds to a transparency value of 1 - 0.3
@@ -422,7 +423,7 @@ public class FeatureColourFinderTest
     fr.setColour("Domain", green);
     fr.featuresAdded();
     av.setShowSequenceFeatures(true);
-
+  
     /*
      * Domain (green) rendered above Metal (red) above background (cyan)
      * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
@@ -431,7 +432,7 @@ public class FeatureColourFinderTest
     fr.setTransparency(0.6f);
     Color c = finder.findFeatureColour(Color.cyan, seq, 10);
     assertEquals(c, new Color(61, 194, 41));
-
+  
     /*
      * now promote Metal above Domain
      * - currently no way other than mimicking reordering of
@@ -446,7 +447,7 @@ public class FeatureColourFinderTest
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.cyan, seq, 10);
     assertEquals(c, new Color(153, 102, 41));
-
+  
     /*
      * ..and turn off display of Metal
      * Domain (green) above background (pink)
@@ -518,15 +519,15 @@ public class FeatureColourFinderTest
   {
     String kdFeature = "kd";
     String metalFeature = "Metal";
-    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
-            2, 2, 0f, "KdGroup"));
-    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
-            4, 4, 5f, "KdGroup"));
-    seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4, 5f,
-            "MetalGroup"));
-    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
-            7, 7, 10f, "KdGroup"));
-
+    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 2,
+            2, 0f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 4,
+            4, 5f, "KdGroup"));
+    seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4,
+            5f, "MetalGroup"));
+    seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 7,
+            7, 10f, "KdGroup"));
+  
     /*
      * kd feature has graduated colour from 0 to 10
      * above threshold value of 5
@@ -550,7 +551,7 @@ public class FeatureColourFinderTest
     fr.setFeaturePriority(data);
 
     av.setShowSequenceFeatures(true);
-
+  
     /*
      * position 2, column 1, score 0 - below threshold - default colour
      */
@@ -563,7 +564,7 @@ public class FeatureColourFinderTest
      */
     c = finder.findFeatureColour(Color.blue, seq, 3);
     assertEquals(c, Color.green);
-
+  
     /*
      * position 7, column 9, score 10 - maximum colour in range
      */
index a3d0a7c..e1dac7d 100644 (file)
@@ -27,6 +27,16 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
 import jalview.analysis.GeneticCodes;
 import jalview.api.AlignViewportI;
 import jalview.api.FeatureColourI;
@@ -46,17 +56,13 @@ import jalview.schemes.FeatureColour;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.annotations.Test;
-
 public class FeatureRendererTest
 {
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  }
 
   @Test(groups = "Functional")
   public void testFindAllFeatures()
index 6731465..096fe53 100644 (file)
@@ -107,7 +107,7 @@ public class ClustalxColourSchemeTest
   }
 
   // @formatter:on
-
+  
   /**
    * Test for colour calculation when the consensus percentage ignores gapped
    * sequences
@@ -127,7 +127,7 @@ public class ClustalxColourSchemeTest
             DataSourceType.PASTE);
     AlignmentI al = af.getViewport().getAlignment();
     ClustalxColourScheme cs = new ClustalxColourScheme(al, null);
-
+  
     /*
      * column 1 is 66% C which is above Clustalx threshold of 60%
      */
index 58e272d..a4139cc 100644 (file)
@@ -192,7 +192,7 @@ public class ColourSchemesTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @Test(groups = "Functional")
diff --git a/test/jalview/schemes/HmmerGlobalBackgroundTest.java b/test/jalview/schemes/HmmerGlobalBackgroundTest.java
new file mode 100644 (file)
index 0000000..1cdee91
--- /dev/null
@@ -0,0 +1,90 @@
+package jalview.schemes;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.HMMFile;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.testng.annotations.Test;
+
+public class HmmerGlobalBackgroundTest {
+
+  @Test(groups = "Functional")
+  public void testFindColour() throws MalformedURLException, IOException
+  {
+    HMMFile file = new HMMFile("test/jalview/io/test_PKinase_hmm.txt",
+            DataSourceType.FILE);
+
+    SequenceI hmmSeq = file.getSeqsAsArray()[0];
+    AlignmentI al = new Alignment(new SequenceI[] { hmmSeq });
+    ColourSchemeI scheme = new HmmerGlobalBackground(al);
+
+    /*
+     * 'A' in column 1, node 2, match emission 2.77204
+     * e-2.77204 = 0.0625 
+     * background frequency is 0.0826
+     * ratio is 0.757, log is negative, colour is Orange
+     */
+    Color actual = scheme.findColour('A', 1, null, null, 0);
+    assertEquals(actual, Color.ORANGE);
+
+    // gap is white
+    actual = scheme.findColour('-', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+    actual = scheme.findColour(' ', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+    actual = scheme.findColour('.', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+
+    /*
+     * 'Y' in column 4, node 5, match emission 4.41426
+     * e-4.41426 = 0.0121 
+     * background frequency is 0.0292
+     * ratio is 0.414, log is negative, colour is Orange
+     */
+    actual = scheme.findColour('Y', 4, null, null, 0);
+    assertEquals(actual, Color.ORANGE);
+
+    /*
+     * 'M' in column 109, no matching node, colour is reddish
+     */
+    actual = scheme.findColour('M', 109, null, null, 0);
+    assertEquals(actual, new Color(230, 0, 0));
+
+    /*
+     * 'I' in column 6, node 7, match emission 1.33015
+     * e-1.33015 = 0.2644
+     * background frequency is 0.0593
+     * ratio is 4.459, log is 1.495
+     * colour is graduated 1.495/4.52 or 84/255 of the way from
+     * white(255, 255, 255) to blue(0, 0, 255)
+     */
+    actual = scheme.findColour('I', 6, null, null, 0);
+    assertEquals(actual, new Color(171, 171, 255));
+
+    /*
+     * 'V' in column 14, node 15, match emission 0.44769
+     * e-0.44769 = 0.6391
+     * background frequency is 0.0686
+     * ratio is 9.316, log is 2.232
+     * colour is graduated 2.232/4.52 or 126/255 of the way from
+     * white(255, 255, 255) to blue(0, 0, 255)
+     */
+    actual = scheme.findColour('V', 14, null, null, 0);
+    assertEquals(actual, new Color(129, 129, 255));
+
+    /*
+     * invalid symbol is White
+     */
+    actual = scheme.findColour('X', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+  }
+
+}
diff --git a/test/jalview/schemes/HmmerLocalBackgroundTest.java b/test/jalview/schemes/HmmerLocalBackgroundTest.java
new file mode 100644 (file)
index 0000000..5f2bb8f
--- /dev/null
@@ -0,0 +1,77 @@
+package jalview.schemes;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.HMMFile;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.testng.annotations.Test;
+
+public class HmmerLocalBackgroundTest {
+
+  @Test(groups = "Functional")
+  public void testFindColour() throws MalformedURLException, IOException
+  {
+    HMMFile file = new HMMFile("test/jalview/io/test_PKinase_hmm.txt",
+            DataSourceType.FILE);
+
+    /*
+     * alignment with 20 residues and background frequencies:
+     * A/a, S 3/20 = 0.15
+     * M, K 4/20 = 0.2
+     * V 2/20 = 0.1
+     * Q, R, L 1/20 = 0.05
+     * log(totalCount) = log(20) = 2.996
+     */
+    SequenceI seq1 = new Sequence("seq1", "AAMMMKKKVV");
+    SequenceI seq2 = new Sequence("seq2", "aAM-QKRSSSL");
+    SequenceI hmmSeq = file.getSeqsAsArray()[0];
+    AnnotatedCollectionI ac = new Alignment(
+            new SequenceI[]
+            { hmmSeq, seq1, seq2 });
+    ColourSchemeI scheme = new HmmerLocalBackground(ac);
+
+    /*
+     * 'A' in column 1, node 2, match emission 2.77204
+     * e-2.77204 = 0.0625 
+     * background frequency is 0.15
+     * ratio is < 1, log is negative, colour is Orange
+     */
+    Color actual = scheme.findColour('A', 1, null, null, 0);
+    assertEquals(actual, Color.ORANGE);
+
+    // gap is white
+    actual = scheme.findColour('-', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+    actual = scheme.findColour(' ', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+    actual = scheme.findColour('.', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+
+    /*
+     * 'L' in column 3, node 4, match emission 1.98342
+     * e-1.98342 = 0.1376 
+     * background frequency is 0.05
+     * ratio is 2.752, log is 1.012
+     * colour is graduated 1.012/2.996 or 86/255 of the way from
+     * white(255, 255, 255) to blue(0, 0, 255)
+     */
+    actual = scheme.findColour('L', 3, null, null, 0);
+    assertEquals(actual, new Color(169, 169, 255));
+
+    /*
+     * invalid symbol is White
+     */
+    actual = scheme.findColour('X', 2, null, null, 0);
+    assertEquals(actual, Color.WHITE);
+  }
+
+}
index dff19cd..4a62d80 100644 (file)
@@ -28,9 +28,9 @@ import org.testng.annotations.Test;
 
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.gui.AlignViewport;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.viewmodel.AlignmentViewport;
 
 public class PIDColourSchemeTest
 {
@@ -103,7 +103,7 @@ public class PIDColourSchemeTest
      */
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqs,
             DataSourceType.PASTE);
-    AlignViewport viewport = af.getViewport();
+    AlignmentViewport viewport = af.getViewport();
     viewport.setIgnoreGapsConsensus(false, af.alignPanel);
     do
     {
index f1feced..31cc155 100644 (file)
@@ -76,7 +76,8 @@ public class Mapping
               helices[] = new int[]
               { 303, 315 }, sheets[] = new int[] { 267, 268, 269, 270 };
 
-      StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+      StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(null);
       StructureFile pmap = ssm.setMapping(true, new SequenceI[] { uprot },
               new String[]
               { "A" }, "test/jalview/ext/jmol/1QCF.pdb",
@@ -144,7 +145,8 @@ public class Mapping
             "EIVKGVCSNFLCDLQPGDNVQITGPVGKEMLMPKDPNATIIMLATGTGIAPFRSFLWKMFFEKHDDYKFNGLGWLFLGVPTSSSLLYKEEFGKM");
     Sequence sq1 = new Sequence(sq);
     String inFile;
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     // Associate the 1GAQ pdb file with the subsequence 'imported' from another
     // source
     StructureFile pde = ssm.setMapping(true, new SequenceI[] { sq },
@@ -245,7 +247,8 @@ public class Mapping
             ">FER1_MAIZE/1-150 Ferredoxin-1, chloroplast precursor\nMATVLGSPRAPAFFFSSSSLRAAPAPTAVALPAAKVGIMGRSASSRRRLRAQATYNVKLITPEGEVELQVPD\nDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKE\nEELTGA",
             DataSourceType.PASTE, FileFormat.Fasta);
     SequenceI newseq = seqf.getViewport().getAlignment().getSequenceAt(0);
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
             new String[]
             { null }, "examples/3W5V.pdb", DataSourceType.FILE);
@@ -273,7 +276,8 @@ public class Mapping
     // make it harder by shifting the copy vs the reference
     newseq.setStart(refseq.getStart() + 25);
     newseq.setEnd(refseq.getLength() + 25 + refseq.getStart());
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     ssm.setProcessSecondaryStructure(true);
     ssm.setAddTempFacAnnot(true);
     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
index 66514f9..5ca2cb1 100644 (file)
@@ -80,7 +80,8 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   public void setUp()
   {
     StructureImportSettings.setShowSeqFeatures(true);
-    ssm = new StructureSelectionManager();
+    ssm = StructureSelectionManager.getStructureSelectionManager(null);
+    ssm.resetAll();
   }
 
   @Test(groups = { "Functional" })
@@ -88,12 +89,10 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
     acf1.addMap(new Sequence("s1", "ttt"), new Sequence("p1", "p"),
-            new MapList(new int[]
-            { 1, 3 }, new int[] { 1, 1 }, 1, 1));
+            new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
     AlignedCodonFrame acf2 = new AlignedCodonFrame();
     acf2.addMap(new Sequence("s2", "ttt"), new Sequence("p2", "p"),
-            new MapList(new int[]
-            { 1, 3 }, new int[] { 1, 1 }, 1, 1));
+            new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
 
     ssm.registerMapping(acf1);
     assertEquals(1, ssm.getSequenceMappings().size());
@@ -118,16 +117,13 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
     acf1.addMap(new Sequence("s1", "ttt"), new Sequence("p1", "p"),
-            new MapList(new int[]
-            { 1, 3 }, new int[] { 1, 1 }, 1, 1));
+            new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
     AlignedCodonFrame acf2 = new AlignedCodonFrame();
     acf2.addMap(new Sequence("s2", "ttt"), new Sequence("p2", "p"),
-            new MapList(new int[]
-            { 1, 3 }, new int[] { 1, 1 }, 1, 1));
+            new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
     AlignedCodonFrame acf3 = new AlignedCodonFrame();
     acf3.addMap(new Sequence("s3", "ttt"), new Sequence("p3", "p"),
-            new MapList(new int[]
-            { 1, 3 }, new int[] { 1, 1 }, 1, 1));
+            new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
 
     List<AlignedCodonFrame> set1 = new ArrayList<>();
     set1.add(acf1);
@@ -144,10 +140,11 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     ssm.registerMappings(set2);
     ssm.registerMappings(set2);
 
-    assertEquals(3, ssm.getSequenceMappings().size());
-    assertTrue(ssm.getSequenceMappings().contains(acf1));
-    assertTrue(ssm.getSequenceMappings().contains(acf2));
-    assertTrue(ssm.getSequenceMappings().contains(acf3));
+    List<AlignedCodonFrame> mappings = ssm.getSequenceMappings();
+    assertEquals(3, mappings.size());
+    assertTrue(mappings.contains(acf1));
+    assertTrue(mappings.contains(acf2));
+    assertTrue(mappings.contains(acf3));
   }
 
   /**
@@ -157,14 +154,14 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testSetMapping_seqFeatures()
   {
-    SequenceI seq = new Sequence("1GAQ|B",
+    SequenceI seq = new Sequence(
+            "1GAQ|B",
             "ATYNVKLITPEGEVELQVPDDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKEEELTGA");
-    StructureSelectionManager sm = new StructureSelectionManager();
+    StructureSelectionManager sm = StructureSelectionManager.getStructureSelectionManager(null);
     sm.setProcessSecondaryStructure(true);
     sm.setAddTempFacAnnot(true);
     StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
-            new String[]
-            { null }, "examples/1gaq.txt", DataSourceType.FILE);
+            new String[] { null }, "examples/1gaq.txt", DataSourceType.FILE);
     assertTrue(pmap != null);
 
     assertEquals(3, pmap.getSeqs().size());
@@ -204,23 +201,20 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   {
     // for some reason 'BeforeMethod' (which should be inherited from
     // Jalview2XmlBase isn't always called)...
-    Desktop.instance.closeAll_actionPerformed(null);
-    try
-    {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+    try { 
       Thread.sleep(200);
-    } catch (Exception foo)
-    {
-    }
-    ;
+    } catch (Exception foo) {}; 
     SequenceI seq = new Sequence("4IM2|A",
             "LDFCIRNIEKTVMGEISDIHTKLLRLSSSQGTIE");
     String P4IM2_MISSING = "examples/testdata/4IM2_missing.pdb";
-    StructureSelectionManager sm = new StructureSelectionManager();
+    StructureSelectionManager sm = StructureSelectionManager.getStructureSelectionManager(null);
     sm.setProcessSecondaryStructure(true);
     sm.setAddTempFacAnnot(true);
     StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
             new String[]
-            { null }, P4IM2_MISSING, DataSourceType.FILE);
+            { null }, P4IM2_MISSING,
+            DataSourceType.FILE);
     assertTrue(pmap != null);
 
     assertEquals(1, pmap.getSeqs().size());
@@ -268,6 +262,7 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     // alignment
     verifySeqFeats(pdb_viewseq, offset, fdesc);
 
+
     JalviewStructureDisplayI viewr = openStructureViaChooser(alf,
             pdb_viewseq, "4IM2");
 
@@ -278,7 +273,8 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     PDBEntry pdbe = seq.getPDBEntry("4IM2");
     StructureSelectionManager apssm = alf.alignPanel
             .getStructureSelectionManager();
-    StructureMapping[] smap = apssm.getMapping(pdbe.getFile());
+    StructureMapping[] smap = apssm
+            .getMapping(pdbe.getFile());
     assertNotNull(smap);
     assertNotNull(smap[0]);
     // find the last position in the alignment sequence - this is not
@@ -324,8 +320,8 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
             { pdbe.getFile() },
             new SequenceI[][]
             { new SequenceI[] { pdb_viewseq } },
-            new SequenceRenderer(alf.alignPanel.getAlignViewport()),
-            alf.alignPanel);
+                    new SequenceRenderer(alf.alignPanel.getAlignViewport()),
+                    alf.alignPanel);
     // Expected - all residues are white
     for (String c : smcr)
     {
@@ -357,42 +353,43 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     // currently this test fails if trimming is enabled
     Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
             Boolean.FALSE.toString());
-    String TEMP_FACTOR_AA = "Temperature Factor";
+    String TEMP_FACTOR_AA="Temperature Factor";
     String PDBID = "4IM2";
-    String FullLengthSeq = ">TBK1_HUMAN Serine/threonine-protein kinase TBK1\n"
-            + "MQSTSNHLWLLSDILGQGATANVFRGRHKKTGDLFAIKVFNNISFLRPVDVQMREFEVLKKLNHKNIVKLFA\n"
-            + "IEEETTTRHKVLIMEFCPCGSLYTVLEEPSNAYGLPESEFLIVLRDVVGGMNHLRENGIVHRDIKPGNIMRV\n"
-            + "IGEDGQSVYKLTDFGAARELEDDEQFVSLYGTEEYLHPDMYERAVLRKDHQKKYGATVDLWSIGVTFYHAAT\n"
-            + "GSLPFRPFEGPRRNKEVMYKIITGKPSGAISGVQKAENGPIDWSGDMPVSCSLSRGLQVLLTPVLANILEAD\n"
-            + "QEKCWGFDQFFAETSDILHRMVIHVFSLQQMTAHKIYIHSYNTATIFHELVYKQTKIISSNQELIYEGRRLV\n"
-            + "LEPGRLAQHFPKTTEENPIFVVSREPLNTIGLIYEKISLPKVHPRYDLDGDASMAKAITGVVCYACRIASTL\n"
-            + "LLYQELMRKGIRWLIELIKDDYNETVHKKTEVVITLDFCIRNIEKTVKVYEKLMKINLEAAELGEISDIHTK\n"
-            + "LLRLSSSQGTIETSLQDIDSRLSPGGSLADAWAHQEGTHPKDRNVEKLQVLLNCMTEIYYQFKKDKAERRLA\n"
-            + "YNEEQIHKFDKQKLYYHATKAMTHFTDECVKKYEAFLNKSEEWIRKMLHLRKQLLSLTNQCFDIEEEVSKYQ\n"
-            + "EYTNELQETLPQKMFTASSGIKHTMTPIYPSSNTLVEMTLGMKKLKEEMEGVVKELAENNHILERFGSLTMD\n"
-            + "GGLRNVDCL";
+    String FullLengthSeq = ">TBK1_HUMAN Serine/threonine-protein kinase TBK1\n" + 
+            "MQSTSNHLWLLSDILGQGATANVFRGRHKKTGDLFAIKVFNNISFLRPVDVQMREFEVLKKLNHKNIVKLFA\n" + 
+            "IEEETTTRHKVLIMEFCPCGSLYTVLEEPSNAYGLPESEFLIVLRDVVGGMNHLRENGIVHRDIKPGNIMRV\n" + 
+            "IGEDGQSVYKLTDFGAARELEDDEQFVSLYGTEEYLHPDMYERAVLRKDHQKKYGATVDLWSIGVTFYHAAT\n" + 
+            "GSLPFRPFEGPRRNKEVMYKIITGKPSGAISGVQKAENGPIDWSGDMPVSCSLSRGLQVLLTPVLANILEAD\n" + 
+            "QEKCWGFDQFFAETSDILHRMVIHVFSLQQMTAHKIYIHSYNTATIFHELVYKQTKIISSNQELIYEGRRLV\n" + 
+            "LEPGRLAQHFPKTTEENPIFVVSREPLNTIGLIYEKISLPKVHPRYDLDGDASMAKAITGVVCYACRIASTL\n" + 
+            "LLYQELMRKGIRWLIELIKDDYNETVHKKTEVVITLDFCIRNIEKTVKVYEKLMKINLEAAELGEISDIHTK\n" + 
+            "LLRLSSSQGTIETSLQDIDSRLSPGGSLADAWAHQEGTHPKDRNVEKLQVLLNCMTEIYYQFKKDKAERRLA\n" + 
+            "YNEEQIHKFDKQKLYYHATKAMTHFTDECVKKYEAFLNKSEEWIRKMLHLRKQLLSLTNQCFDIEEEVSKYQ\n" + 
+            "EYTNELQETLPQKMFTASSGIKHTMTPIYPSSNTLVEMTLGMKKLKEEMEGVVKELAENNHILERFGSLTMD\n" + 
+            "GGLRNVDCL";
     /*
      * annotation exported after importing full length sequence to desktop, opening 4IM2 and selecting 'Add Reference Annotation'.
      * 
      * Note - tabs must be replaced with \t - Eclipse expands them to spaces otherwise.
      */
-    String FullLengthAnnot = "JALVIEW_ANNOTATION\n"
-            + "# Created: Mon Feb 05 15:30:20 GMT 2018\n"
-            + "# Updated: Fri Feb 09 17:05:17 GMT 2018\n" + "\n" + "\n"
-            + "SEQUENCE_REF\tTBK1_HUMAN\n"
+    String FullLengthAnnot = "JALVIEW_ANNOTATION\n" + 
+            "# Created: Mon Feb 05 15:30:20 GMT 2018\n" + 
+            "# Updated: Fri Feb 09 17:05:17 GMT 2018\n" + 
+            "\n" + 
+            "\n" + 
+            "SEQUENCE_REF\tTBK1_HUMAN\n"
             + "LINE_GRAPH\tTemperature Factor\tTemperature Factor for 4im2A\t125.22|128.51|120.35|113.12|122.6|114.44|91.49|102.53|98.22|111.41|111.32|116.64|103.55|100.53|95.07|105.55|114.76|128.29|133.55|142.14|121.12|110.36|95.79|95.39|87.14|99.56|93.55|94.21|100.33|110.68|97.85|82.37|75.87|76.53|77.85|82.49|80.92|96.88|122.58|133.31|160.15|180.51|||||242.88|258.97|247.01|227.12|223.24|211.62|184.65|183.51|168.96|160.04|150.88|131.68|130.43|139.87|148.59|136.57|125.7|96.51|74.49|74.08|85.87|70.93|86.47|101.59|97.51|97.39|117.19|114.27|129.5|112.98|147.52|170.26|154.98|168.18|157.51|131.95|105.85|97.78|97.35|76.51|76.31|72.55|71.43|78.82|79.94|75.04|79.54|77.95|83.56|88.5|71.51|71.73|75.96|82.36|81.75|66.51|67.23|69.35|67.92|54.75|71.19|61.85|65.34|67.97|64.51|67.41|62.28|72.85|72.76|70.64|65.23|71.07|67.73|87.72|64.93|75.92|94.02|99.35|93.71|103.59|106.29|115.46|118.69|147.18|130.62|171.64|158.95|164.11||107.42|88.53|83.52|88.06|94.06|80.82|59.01|59.73|78.89|69.21|70.34|81.95|74.53|60.92|64.65|55.79|75.71|68.86|70.95|75.08|87.76|85.43|105.84|||||||||||||||||137.46|151.33|145.17|122.79|111.56|126.72|124.06|161.75|176.84|180.51|198.49|196.75|187.41||195.23|202.27|203.16|226.55|221.75|193.83||||||172.33|177.97|151.47|132.65|99.22|93.7|91.15|88.24|72.35|70.05|70.0|74.92|66.51|68.37|65.76|70.12|74.97|76.89|80.83|70.21|69.48|79.54|82.65|96.54|114.31|140.46|168.51|176.99|205.08|209.27|155.83|139.41|151.3|129.33|111.31|119.62|121.37|102.26|115.39|129.97|128.65|110.38|110.66|116.1|82.53|84.02|82.17|87.63|86.42|77.23|91.23|95.53|102.21|120.73|133.26|109.67|108.49|93.25|92.85|86.39|95.66|94.92|85.82|80.13|76.17|86.61|78.9|77.97|105.6|70.66|69.35|78.94|66.68|63.03|69.91|79.05|75.43|70.73|70.02|80.57|81.74|77.99|84.1|91.66|92.42|94.03|116.47|132.01|154.55|163.99|161.37|155.23|132.78|109.3|90.38|101.83|99.61|91.68|82.77|86.12|82.73|90.13|85.14|79.54|74.27|74.06|72.88|86.34|72.0|69.32|60.9|68.15|52.99|63.53|61.3|66.01|68.28|77.41|71.52|67.18|66.17|71.51|65.47|52.63|65.08|66.37|73.76|77.79|67.58|79.53|84.75|87.42|78.9|79.19|85.57|73.67|80.56|86.19|72.17|66.27|72.8|86.28|78.89|74.5|90.6|80.42|92.5|92.84|96.18|92.08|88.5|87.25|64.6|68.95|65.56|67.55|71.62|78.24|84.95|71.35|86.41|84.73|94.41|95.09|84.74|87.64|88.85|75.1|86.42|79.28|73.14|78.54|80.81|60.66|67.93|71.64|59.85|64.7|61.22|63.84|65.9|62.18|74.95|72.92|93.37|90.47|96.0|93.8|88.46|79.78|83.4|66.55|68.7|73.2|78.76|85.67|84.8|89.59|96.52|79.53|103.51|134.72|126.7|145.31|156.17|149.35|128.48|117.29|118.98|131.59|109.36|90.39|87.68|91.81|78.77|80.11|91.39|75.57|78.98|71.53|76.85|70.9|64.71|73.55|73.45|60.0|69.92|57.89|69.07|66.45|62.85|57.83|57.89|66.4|61.61|60.85|66.47|63.53|63.84|65.96|73.06|70.82|64.51|63.66|73.37|73.59|68.09|78.93|76.99|75.05|71.32|88.4|78.88|93.08|110.61|94.32|99.24|128.99|129.49|132.74|124.21|120.32|142.06|166.41|149.87|153.29|172.19|165.89|181.6|223.11|237.73|176.41|171.09|189.65|188.61|154.84|142.72|154.25|170.99|175.65|||||||110.61||||||||||158.07|170.73|167.93|198.47|212.36|181.71|157.69|163.31|138.96|120.29|131.63|152.26|125.06|136.66|148.97|129.68|120.52|135.31|136.05|119.39|124.18|128.94|123.02|103.37|128.44|134.12|118.88|120.94|130.38|124.67|112.21|113.69|123.65|132.06|114.97|110.75|92.38|101.2|103.25|94.84|85.3|82.19|89.81|98.81|83.03|68.91|65.24|70.31|63.49|86.38|71.07|62.65|63.95|66.98|58.06|68.28|62.11|63.86|67.4|68.69|69.57|68.03|74.23|75.66|70.67|81.08|81.31|82.49|88.15|95.99|92.97|100.01|113.18|122.37|110.99|122.19|159.27|147.74|133.96|111.2|115.64|126.55|107.15|102.85|117.06|116.56|109.55|96.82|98.92|96.53|86.0|88.11|92.76|85.77|79.41|93.06|86.96|76.35|72.37|74.19|68.6|67.46|74.47|76.25|66.73|73.18|75.2|88.21|84.93|75.04|71.09|82.6|80.03|76.22|75.76|83.72|75.85|79.36|90.35|86.9|78.24|95.64|97.38|86.41|85.02|91.87|87.36|77.56|81.25|91.66|83.65|77.67|85.07|89.21|92.66|92.46|89.0|100.83|96.71|94.81|101.37|111.28|124.48|119.73|127.81|134.41|132.4|140.32|140.86|166.52|160.16|168.39|176.74|174.63|172.86|168.55|155.9|132.71|113.44|113.49|123.9|151.11|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"
-            + "\n" + "";
-    AlignFrame alf_full = new FileLoader(false)
-            .LoadFileWaitTillLoaded(FullLengthSeq, DataSourceType.PASTE);
-    alf_full.loadJalviewDataFile(FullLengthAnnot, DataSourceType.PASTE,
-            null, null);
+            + 
+            "\n" + 
+            "";
+    AlignFrame alf_full=new
+     FileLoader(false).LoadFileWaitTillLoaded(FullLengthSeq,DataSourceType.PASTE);
+    alf_full.loadJalviewDataFile(FullLengthAnnot, DataSourceType.PASTE, null, null);
     AlignmentI al_full = alf_full.getViewport().getAlignment();
-    AlignmentAnnotation fullseq_tf = al_full
-            .findAnnotations(al_full.getSequences().get(0), null,
-                    TEMP_FACTOR_AA)
-            .iterator().next();
+    AlignmentAnnotation fullseq_tf = al_full.findAnnotations(al_full.getSequences().get(0), null, TEMP_FACTOR_AA).iterator()
+            .next();
     assertNotNull(fullseq_tf);
-
+    
     // getMappingFor
     // AlignmentI al_full=alf_full.getViewport().getAlignment();
     //
@@ -419,9 +416,9 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     JalviewStructureDisplayI sview = openStructureViaChooser(alf, seq,
             PDBID);
 
-    AlignmentAnnotation subseq_tf = null;
+    AlignmentAnnotation subseq_tf=null;
     assertTrue(seq.getDBRefs() != null && seq.getDBRefs().size() > 0);
-
+    
     if (!al.findAnnotations(seq, null, TEMP_FACTOR_AA).iterator().hasNext())
     {
       // FIXME JAL-2321 - don't see reference annotation on alignment the first
@@ -448,12 +445,12 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     // verify location of secondary structure annotation
     // Specific positions: LYS477 (h),THR478 (no helix), ... GLY496(no helix),
     // GLU497 (helix),
-
+    
     // check there is or is not a tempfactor for each mapped position, and that
     // values are equal for those positions.
-    for (int p = seq.getStart(); p <= seq.getEnd(); p++)
+    for (int p=seq.getStart();p<=seq.getEnd();p++)
     {
-      Annotation orig, subseq;
+      Annotation orig,subseq;
       orig = fullseq_tf.getAnnotationForPosition(p);
       subseq = subseq_tf.getAnnotationForPosition(p);
       if (orig == null)
@@ -474,7 +471,8 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   }
 
   private JalviewStructureDisplayI openStructureViaChooser(AlignFrame alf,
-          SequenceI seq, String pDBID)
+          SequenceI seq,
+          String pDBID)
   {
 
     SequenceI[] selectedSeqs = new SequenceI[] { seq };
index 1a2cb84..8818bf1 100644 (file)
@@ -138,7 +138,7 @@ public class AAStructureBindingModelTest
     // see JAL-623 - pasting is still not correctly handled...
     PDBEntry importedPDB = new PDBEntry("3A6S", "", Type.PDB, "Paste");
     AAStructureBindingModel binder = new AAStructureBindingModel(
-            new StructureSelectionManager(), new PDBEntry[]
+            StructureSelectionManager.getStructureSelectionManager(null), new PDBEntry[]
             { importedPDB },
             new SequenceI[][]
             { importedAl.getSequencesArray() }, null)
@@ -227,7 +227,7 @@ public class AAStructureBindingModelTest
     seqs[0] = new SequenceI[] { seq1a, seq1b };
     seqs[1] = new SequenceI[] { seq2 };
     seqs[2] = new SequenceI[] { seq3 };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
 
     ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
             DataSourceType.PASTE, null);
diff --git a/test/jalview/util/Binned.csv b/test/jalview/util/Binned.csv
new file mode 100644 (file)
index 0000000..f140646
--- /dev/null
@@ -0,0 +1,4 @@
+1.8, 4.53
+3.4, 2.65
+0, 5.4
+6.4, 10.8
\ No newline at end of file
index 9c9437e..1ee0392 100644 (file)
@@ -24,13 +24,13 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 
-import jalview.gui.JvOptionPane;
 
 import java.awt.Color;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import jalview.gui.JvOptionPane;
 public class ColorUtilsTest
 {
 
@@ -102,8 +102,8 @@ public class ColorUtilsTest
     /*
      * value two-thirds of the way between min and max
      */
-    col = ColorUtils.getGraduatedColour(30f, 10f, minColour, 40f,
-            maxColour);
+    col = ColorUtils
+            .getGraduatedColour(30f, 10f, minColour, 40f, maxColour);
     assertEquals(153, col.getRed());
     // Color constructor rounds float value to nearest int
     assertEquals(167, col.getGreen());
@@ -112,15 +112,15 @@ public class ColorUtilsTest
     /*
      * value = min
      */
-    col = ColorUtils.getGraduatedColour(10f, 10f, minColour, 30f,
-            maxColour);
+    col = ColorUtils
+            .getGraduatedColour(10f, 10f, minColour, 30f, maxColour);
     assertEquals(minColour, col);
 
     /*
      * value = max
      */
-    col = ColorUtils.getGraduatedColour(30f, 10f, minColour, 30f,
-            maxColour);
+    col = ColorUtils
+            .getGraduatedColour(30f, 10f, minColour, 30f, maxColour);
     assertEquals(maxColour, col);
 
     /*
@@ -132,15 +132,15 @@ public class ColorUtilsTest
     /*
      * value > max
      */
-    col = ColorUtils.getGraduatedColour(40f, 10f, minColour, 30f,
-            maxColour);
+    col = ColorUtils
+            .getGraduatedColour(40f, 10f, minColour, 30f, maxColour);
     assertEquals(maxColour, col);
 
     /*
      * min = max
      */
-    col = ColorUtils.getGraduatedColour(40f, 10f, minColour, 10f,
-            maxColour);
+    col = ColorUtils
+            .getGraduatedColour(40f, 10f, minColour, 10f, maxColour);
     assertEquals(minColour, col);
   }
 
@@ -206,6 +206,7 @@ public class ColorUtilsTest
      */
     assertNull(ColorUtils.parseColourString(null));
     assertNull(ColorUtils.parseColourString("rubbish"));
+    // from 2.11.2
     assertEquals(Color.WHITE, ColorUtils.parseColourString("-1"));
     assertNull(ColorUtils
             .parseColourString(String.valueOf(Integer.MAX_VALUE)));
@@ -215,8 +216,7 @@ public class ColorUtilsTest
   }
 
   @Test(groups = "Functional")
-  public void testGetAWTColorFromName()
-  {
+  public void testGetAWTColorFromName() {
     assertEquals(Color.white, ColorUtils.getAWTColorFromName("white"));
     assertEquals(Color.white, ColorUtils.getAWTColorFromName("White"));
     assertEquals(Color.white, ColorUtils.getAWTColorFromName("WHITE"));
@@ -230,8 +230,7 @@ public class ColorUtilsTest
   public void testCreateColourFromName()
   {
     assertEquals(Color.white, ColorUtils.createColourFromName(null));
-    assertEquals(new Color(20, 20, 20),
-            ColorUtils.createColourFromName(""));
+    assertEquals(new Color(20, 20, 20), ColorUtils.createColourFromName(""));
     assertEquals(new Color(98, 131, 171),
             ColorUtils.createColourFromName("None")); // no special treatment!
     assertEquals(new Color(123, 211, 122),
diff --git a/test/jalview/util/FileUtilsTest.java b/test/jalview/util/FileUtilsTest.java
new file mode 100644 (file)
index 0000000..fd64bcd
--- /dev/null
@@ -0,0 +1,79 @@
+package jalview.util;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class FileUtilsTest
+{
+       /*
+        * FIXME fails on bamboo since it the working directory named according to the build number
+        */
+  @Test(groups = {"Functional","Not-bamboo"})
+  public void testFindMatchingPaths() throws IOException
+  {
+    String expect1 = Paths.get("..", "jalview", "examples", "plantfdx.fa")
+            .toString();
+    String expect2 = Paths.get("../jalview/examples/plantfdx.features")
+            .toString();
+    String expect3 = Paths
+            .get("../jalview/examples/testdata/plantfdx.features")
+            .toString();
+
+    List<String> matches = FileUtils
+            .findMatchingPaths(Paths.get(".."),
+                    ".*[\\\\/]plant.*\\.f.*");
+    System.out.println(matches);
+    assertTrue(matches.contains(expect1));
+    assertTrue(matches.contains(expect2));
+    assertTrue(matches.contains(expect3));
+  }
+
+  @Test(groups = "External")
+  public void testWindowsPath() throws IOException
+  {
+    if (System.getProperty("os.name").startsWith("Windows"))
+    {
+      /*
+       * should pass provided Eclipse is installed
+       */
+      List<String> matches = FileUtils.findMatches("C:\\",
+              "Program Files*/eclips*/eclips?.exe");
+      assertFalse(matches.isEmpty());
+
+      /*
+       * should pass provided Chimera is installed
+       */
+      matches = FileUtils.findMatches("C:\\",
+              "Program Files*/Chimera*/bin/{chimera,chimera.exe}");
+      assertFalse(matches.isEmpty());
+    }
+  }
+
+  /*
+   * FIXME fails on bamboo since it the working directory named according to the build number
+   */
+  @Test(groups = {"Functional","Not-bamboo"})
+  public void testFindMatches() throws IOException
+  {
+    String expect1 = Paths.get("..", "jalview", "examples", "plantfdx.fa")
+            .toString();
+    String expect2 = Paths.get("../jalview/examples/plantfdx.features")
+            .toString();
+    String expect3 = Paths
+            .get("../jalview/examples/testdata/plantfdx.features")
+            .toString();
+  
+    List<String> matches = FileUtils
+            .findMatches("..", "jalview/ex*/plant*.f*");
+    System.out.println(matches);
+    assertTrue(matches.contains(expect1));
+    assertTrue(matches.contains(expect2));
+    assertFalse(matches.contains(expect3));
+  }
+}
diff --git a/test/jalview/util/HMMProbabilityDistributionAnalyserTest.java b/test/jalview/util/HMMProbabilityDistributionAnalyserTest.java
new file mode 100644 (file)
index 0000000..ec59c14
--- /dev/null
@@ -0,0 +1,119 @@
+package jalview.util;
+
+import jalview.datamodel.HMMNode;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.testng.annotations.Test;
+
+import junit.extensions.PA;
+
+public class HMMProbabilityDistributionAnalyserTest {
+
+  HMMProbabilityDistributionAnalyser analyser = new HMMProbabilityDistributionAnalyser();
+
+  @Test
+  public void testMoveToFile() throws IOException
+  {
+
+    BufferedReader br = new BufferedReader(new FileReader(
+            "test/jalview/util/test_Fams_for_probability_analysis"));
+    analyser.moveLocationBy(1, br);
+
+    String line = br.readLine();
+    assertEquals(line, "# STOCKHOLM 1.0");
+    line = br.readLine();
+    assertEquals(line, "seq1 ATW");
+    line = br.readLine();
+    assertEquals(line, "seq2 ATI");
+
+  }
+
+  @Test
+  public void testCountValidResidues()
+  {
+    analyser.sequences = new Vector<>();
+    analyser.hmm = new HiddenMarkovModel();
+    analyser.hmm.setProperty("LENG", "8");
+
+    List<HMMNode> nodes = new ArrayList<>();
+    nodes.add(new HMMNode());
+    for (int i = 1; i < 9; i++)
+    {
+      HMMNode node = new HMMNode();
+      node.setResidueNumber(i - 1);
+      nodes.add(node);
+
+    }
+    PA.setValue(analyser.hmm, "nodes", nodes);
+
+    SequenceI[] sequence = new Sequence[] {
+        new Sequence("seq1", "ATGWWSCF"), new Sequence("seq2", "GGMKI"),
+        new Sequence("seq3", "--.ATccGc") };
+    analyser.sequences.add(sequence[0]);
+    analyser.sequences.add(sequence[1]);
+    analyser.sequences.add(sequence[2]);
+
+    int count = analyser.countValidResidues();
+    assertEquals(count, 16);
+  }
+
+  @Test(priority = 0)
+  public void testReadBinned() throws IOException
+  {
+    analyser.readBinned("test/jalview/util/");
+    Map<String, Double> map = analyser.binned;
+    assertEquals((double) map.get("1.8"), 4.53);
+    assertEquals((double) map.get("3.4"), 2.65);
+    assertEquals((double) map.get("6.4"), 10.8);
+    assertEquals((double) map.get("0"), 5.4);
+  }
+
+  @Test
+  public void testReadRaw() throws IOException
+  {
+    analyser.readRaw("test/jalview/util/");
+    List<ArrayList<Double>> list = analyser.raw;
+
+    assertEquals((double) list.get(0).get(0), 1.43);
+    assertNull(list.get(0).get(2));
+    assertEquals((double) list.get(1).get(1), 1.2);
+    assertEquals((double) list.get(2).get(0), 5.6);
+    assertEquals((double) list.get(2).get(2), 6.8);
+
+  }
+
+  @Test(priority = 1)
+  public void testProcessData() throws IOException
+  {
+    analyser.keepRaw = true;
+    BufferedReader brFam = new BufferedReader(new FileReader(
+            "test/jalview/util/test_Fams_for_probability_analysis"));
+    BufferedReader brHMM = new BufferedReader(new FileReader(
+            "test/jalview/util/test_HMMs_for_probability_analysis"));
+    analyser.readStockholm(brFam);
+    analyser.readHMM(brHMM);
+    analyser.processData(6);
+    Map<String, Double> map = analyser.binned;
+    List<ArrayList<Double>> list = analyser.raw;
+    assertEquals((double) map.get("1.8"), 4.863d, 0.001d);
+    assertEquals((double) map.get("3.4"), 2.65);
+    assertEquals((double) map.get("0"), 5.4);
+    assertEquals((double) map.get("6.4"), 10.8);
+    assertEquals((double) map.get("1.4"), 0.166667, 0.00001d);
+    assertEquals((double) map.get("4.4"), 0.5);
+
+  }
+}
index 03b3c99..5650586 100644 (file)
@@ -744,60 +744,6 @@ public class MapListTest
     assertEquals("[ [11, 16] ] 1:3 to [ [72, 53] ]", ml.toString());
   }
 
-  @Test(groups = "Functional")
-  public void testAddRange()
-  {
-    int[] range = { 1, 5 };
-    List<int[]> ranges = new ArrayList<>();
-
-    // add to empty list:
-    MapList.addRange(range, ranges);
-    assertEquals(1, ranges.size());
-    assertSame(range, ranges.get(0));
-
-    // extend contiguous (same position):
-    MapList.addRange(new int[] { 5, 10 }, ranges);
-    assertEquals(1, ranges.size());
-    assertEquals(1, ranges.get(0)[0]);
-    assertEquals(10, ranges.get(0)[1]);
-
-    // extend contiguous (next position):
-    MapList.addRange(new int[] { 11, 15 }, ranges);
-    assertEquals(1, ranges.size());
-    assertEquals(1, ranges.get(0)[0]);
-    assertEquals(15, ranges.get(0)[1]);
-
-    // change direction: range is not merged:
-    MapList.addRange(new int[] { 16, 10 }, ranges);
-    assertEquals(2, ranges.size());
-    assertEquals(16, ranges.get(1)[0]);
-    assertEquals(10, ranges.get(1)[1]);
-
-    // extend reverse contiguous (same position):
-    MapList.addRange(new int[] { 10, 8 }, ranges);
-    assertEquals(2, ranges.size());
-    assertEquals(16, ranges.get(1)[0]);
-    assertEquals(8, ranges.get(1)[1]);
-
-    // extend reverse contiguous (next position):
-    MapList.addRange(new int[] { 7, 6 }, ranges);
-    assertEquals(2, ranges.size());
-    assertEquals(16, ranges.get(1)[0]);
-    assertEquals(6, ranges.get(1)[1]);
-
-    // change direction: range is not merged:
-    MapList.addRange(new int[] { 6, 9 }, ranges);
-    assertEquals(3, ranges.size());
-    assertEquals(6, ranges.get(2)[0]);
-    assertEquals(9, ranges.get(2)[1]);
-
-    // not contiguous: not merged
-    MapList.addRange(new int[] { 11, 12 }, ranges);
-    assertEquals(4, ranges.size());
-    assertEquals(11, ranges.get(3)[0]);
-    assertEquals(12, ranges.get(3)[1]);
-  }
-
   /**
    * Check state after construction
    */
index 8cb1208..9239ba5 100644 (file)
@@ -59,6 +59,7 @@ import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FormatAdapter;
 
+
 public class MappingUtilsTest
 {
   @BeforeClass(alwaysRun = true)
@@ -1450,4 +1451,59 @@ public class MappingUtilsTest
     assertEquals(0, mappedGroup.getStartRes());
     assertEquals(1, mappedGroup.getEndRes()); // two columns
   }
+
+  // new for 2.12
+  @Test(groups = "Functional")
+  public void testAddRange()
+  {
+    int[] range = { 1, 5 };
+    List<int[]> ranges = new ArrayList<>();
+  
+    // add to empty list:
+    MappingUtils.addRange(range, ranges);
+    assertEquals(1, ranges.size());
+    assertSame(range, ranges.get(0));
+  
+    // extend contiguous (same position):
+    MappingUtils.addRange(new int[] { 5, 10 }, ranges);
+    assertEquals(1, ranges.size());
+    assertEquals(1, ranges.get(0)[0]);
+    assertEquals(10, ranges.get(0)[1]);
+  
+    // extend contiguous (next position):
+    MappingUtils.addRange(new int[] { 11, 15 }, ranges);
+    assertEquals(1, ranges.size());
+    assertEquals(1, ranges.get(0)[0]);
+    assertEquals(15, ranges.get(0)[1]);
+  
+    // change direction: range is not merged:
+    MappingUtils.addRange(new int[] { 16, 10 }, ranges);
+    assertEquals(2, ranges.size());
+    assertEquals(16, ranges.get(1)[0]);
+    assertEquals(10, ranges.get(1)[1]);
+  
+    // extend reverse contiguous (same position):
+    MappingUtils.addRange(new int[] { 10, 8 }, ranges);
+    assertEquals(2, ranges.size());
+    assertEquals(16, ranges.get(1)[0]);
+    assertEquals(8, ranges.get(1)[1]);
+  
+    // extend reverse contiguous (next position):
+    MappingUtils.addRange(new int[] { 7, 6 }, ranges);
+    assertEquals(2, ranges.size());
+    assertEquals(16, ranges.get(1)[0]);
+    assertEquals(6, ranges.get(1)[1]);
+  
+    // change direction: range is not merged:
+    MappingUtils.addRange(new int[] { 6, 9 }, ranges);
+    assertEquals(3, ranges.size());
+    assertEquals(6, ranges.get(2)[0]);
+    assertEquals(9, ranges.get(2)[1]);
+  
+    // not contiguous: not merged
+    MappingUtils.addRange(new int[] { 11, 12 }, ranges);
+    assertEquals(4, ranges.size());
+    assertEquals(11, ranges.get(3)[0]);
+    assertEquals(12, ranges.get(3)[1]);
+  }
 }
index 2f583a5..8794b12 100644 (file)
@@ -32,6 +32,7 @@ import java.awt.Toolkit;
 import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -142,4 +143,17 @@ public class PlatformTest
     assertEquals(Platform.escapeBackslashes("hello\\world"),
             "hello\\\\world");
   }
+
+  @Test(groups = { "Functional" })
+  public void getLeadingIntegerFromString()
+  {
+    Assert.assertEquals(Platform.getLeadingIntegerValue("1234abcd", -1),
+            1234);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("1234", -1), 1234);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("abcd", -1), -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("abcd1234", -1),
+            -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("None", -1), -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("Null", -1), -1);
+  }
 }
diff --git a/test/jalview/util/Raw.csv b/test/jalview/util/Raw.csv
new file mode 100644 (file)
index 0000000..83d3452
--- /dev/null
@@ -0,0 +1,4 @@
+Seq1, Seq2, Seq3
+1.43, 2.34, 5.6,
+EMPTY, 1.2, 0.05,
+EMPTY, 5.4, 6.8,
\ No newline at end of file
index 22324fc..b425116 100644 (file)
@@ -90,19 +90,6 @@ public class StringUtilsTest
   }
 
   @Test(groups = { "Functional" })
-  public void testGetLastToken()
-  {
-    assertNull(StringUtils.getLastToken(null, null));
-    assertNull(StringUtils.getLastToken(null, "/"));
-    assertEquals("a", StringUtils.getLastToken("a", null));
-
-    assertEquals("abc", StringUtils.getLastToken("abc", "/"));
-    assertEquals("c", StringUtils.getLastToken("abc", "b"));
-    assertEquals("file1.dat", StringUtils.getLastToken(
-            "file://localhost:8080/data/examples/file1.dat", "/"));
-  }
-
-  @Test(groups = { "Functional" })
   public void testSeparatorListToArray()
   {
     String[] result = StringUtils.separatorListToArray(
diff --git a/test/jalview/util/test_Fams_for_probability_analysis b/test/jalview/util/test_Fams_for_probability_analysis
new file mode 100644 (file)
index 0000000..74fbab0
--- /dev/null
@@ -0,0 +1,13 @@
+# STOCKHOLM 1.0
+seq1 AW
+seq2 GW
+seq3 AW
+//
+# STOCKHOLM 1.0
+seq1 ATW
+seq2 ATI
+//
+# STOCKHOLM 1.0
+seq1 R-WW
+seq2 RAWW
+//
\ No newline at end of file
diff --git a/test/jalview/util/test_hmms_for_probability_analysis b/test/jalview/util/test_hmms_for_probability_analysis
new file mode 100644 (file)
index 0000000..b70d42b
--- /dev/null
@@ -0,0 +1,99 @@
+HMMER3/f [3.1b2 | February 2015]
+NAME  test1
+LENG  4
+ALPH  amino
+RF    no
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Fri Jul 21 06:35:06 2017
+NSEQ  3
+EFFN  3.000000
+CKSUM 657102310
+STATS LOCAL MSV       -5.0223  0.82341
+STATS LOCAL VITERBI   -4.9569  0.82341
+STATS LOCAL FORWARD   -2.0282  0.82341
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   1.94296  5.20658  4.78882  4.52987  4.66525  2.54188  5.19553  2.25221  4.08549  3.87133  5.00250  4.55734  4.73106  4.65220  2.77287  3.67799  1.53923  3.75384  1.48526  4.84023
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01083  4.92694  5.64929  0.61958  0.77255  0.00000        *
+      1   0.67027  4.79120  4.60974  4.51892  5.43734  1.28080  5.43621  4.90028  4.64310  4.61059  5.39837  4.28688  4.41468  4.80804  4.79047  3.08135  3.44468  4.10506  6.74908  5.67939      1 A - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01083  4.92694  5.64929  0.61958  0.77255  0.48576  0.95510
+      2   5.43405  6.34487  5.85697  5.84679  4.39288  5.10370  5.65594  5.70664  5.69821  4.87848  6.25251  5.89664  5.70261  6.00722  5.60902  5.64576  5.80049  5.59366  0.08910  4.36301      2 W - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01083  4.92694  5.64929  0.61958  0.77255  0.48576  0.95510
+      3   3.42384  5.23617  4.84601  4.77568  5.39247  4.01365  5.64153  4.72950  4.75585  4.52264  5.55782  4.69647  4.81267  5.08969  4.87301  3.64217  0.20934  4.25217  6.65833  5.63018      3 T - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01083  4.92694  5.64929  0.61958  0.77255  0.48576  0.95510
+      4   3.53954  5.09963  4.71395  4.06038  4.20429  4.44020  4.65482  0.90152  3.16536  2.96072  4.13056  4.31689  4.83725  3.95143  1.46549  3.86829  3.78084  2.85654  5.74911  4.54685      4 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00730  4.92341        *  0.61958  0.77255  0.00000        *
+//
+HMMER3/f [3.1b2 | February 2015]
+NAME  test2
+LENG  3
+ALPH  amino
+RF    no
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Fri Jul 21 06:36:50 2017
+NSEQ  2
+EFFN  2.000000
+CKSUM 777554360
+STATS LOCAL MSV       -5.2452  0.95763
+STATS LOCAL VITERBI   -5.2886  0.95763
+STATS LOCAL FORWARD   -1.5134  0.95763
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   1.33364  4.79082  4.44132  4.24796  3.91456  3.75718  4.85762  2.23342  4.16821  3.09137  4.29744  4.25088  4.41741  4.46803  4.31080  3.33397  1.39376  3.21010  2.54819  4.14914
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01467  4.62483  5.34718  0.61958  0.77255  0.00000        *
+      1   0.32372  4.76536  4.42980  4.32857  5.00499  3.55951  5.22620  4.27004  4.37081  4.10495  5.08789  4.22499  4.36948  4.63911  4.51684  3.12947  3.46009  3.76842  6.33337  5.25783      1 A - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01467  4.62483  5.34718  0.61958  0.77255  0.48576  0.95510
+      2   3.04414  4.87155  4.35068  4.21532  4.89213  3.66881  5.13994  4.14202  4.17893  3.96810  5.00600  4.23490  4.44590  4.53729  4.35178  3.25814  0.35496  3.73038  6.23308  5.12388      2 T - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01467  4.62483  5.34718  0.61958  0.77255  0.48576  0.95510
+      3   3.41901  4.77187  4.98694  4.49106  3.10447  4.49364  4.52024  1.21391  4.22709  2.30875  3.57865  4.53952  4.83367  4.43564  4.31127  3.88439  3.66452  2.61326  1.44329  3.34125      3 i - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  4.62006        *  0.61958  0.77255  0.00000        *
+//
+HMMER3/f [3.1b2 | February 2015]
+NAME  test3
+LENG  4
+ALPH  amino
+RF    no
+MM    no
+CONS  yes
+CS    no
+MAP   yes
+DATE  Fri Jul 21 06:37:01 2017
+NSEQ  2
+EFFN  2.000000
+CKSUM 986955970
+STATS LOCAL MSV       -5.4578  0.80004
+STATS LOCAL VITERBI   -5.2499  0.80004
+STATS LOCAL FORWARD   -2.1856  0.80004
+HMM          A        C        D        E        F        G        H        I        K        L        M        N        P        Q        R        S        T        V        W        Y   
+            m->m     m->i     m->d     i->m     i->i     d->m     d->d
+  COMPO   2.16700  5.33023  4.57707  4.32329  4.15728  4.03215  4.83242  4.49728  3.73978  4.00181  5.18156  4.45520  4.63709  4.41734  1.53081  3.84281  4.10073  4.20172  0.82417  4.15750
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01467  4.62483  5.34718  0.61958  0.77255  0.00000        *
+      1   4.09153  5.75008  4.67229  4.09411  5.31638  4.34678  4.67762  4.97541  2.93879  4.35023  5.37643  4.37234  4.85576  3.90366  0.27042  4.17197  4.33362  4.67489  6.11461  5.14175      1 R - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.39762  4.62483  1.14482  0.61958  0.77255  0.48576  0.95510
+      2   0.58385  4.42717  3.89187  3.72118  4.47383  3.23815  4.69159  3.65968  3.74862  3.52210  4.52478  3.76053  4.01438  4.05655  3.96007  2.78076  3.08518  3.24526  5.86063  4.71068      2 A - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.02145  4.24866  4.97100  0.61958  0.77255  0.27360  1.42978
+      3   4.82318  5.87391  5.36844  5.28961  3.81117  4.70308  5.10523  4.98084  5.06729  4.21822  5.56037  5.33222  5.28501  5.40387  5.04111  5.02415  5.17266  4.88017  0.16601  3.78547      3 W - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.01467  4.62483  5.34718  0.61958  0.77255  0.48576  0.95510
+      4   4.82318  5.87391  5.36844  5.28961  3.81117  4.70308  5.10523  4.98084  5.06729  4.21822  5.56037  5.33222  5.28501  5.40387  5.04111  5.02415  5.17266  4.88017  0.16601  3.78547      4 W - - -
+          2.68618  4.42225  2.77519  2.73123  3.46354  2.40513  3.72494  3.29354  2.67741  2.69355  4.24690  2.90347  2.73739  3.18146  2.89801  2.37887  2.77519  2.98518  4.58477  3.61503
+          0.00990  4.62006        *  0.61958  0.77255  0.00000        *
+//
\ No newline at end of file
diff --git a/test/jalview/workers/AlignCaclManager2Test.java b/test/jalview/workers/AlignCaclManager2Test.java
new file mode 100644 (file)
index 0000000..343ad14
--- /dev/null
@@ -0,0 +1,377 @@
+package jalview.workers;
+
+import static org.testng.Assert.*;
+
+import java.util.ArrayList;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jalview.api.AlignCalcManagerI2;
+import jalview.api.AlignCalcWorkerI;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+
+@Test(singleThreaded = true)
+public class AlignCaclManager2Test
+{
+  AlignFrame alignFrame;
+
+  AlignCalcManagerI2 calcManager;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpClass()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    AlignmentI al = new Alignment(
+        new SequenceI[] { new Sequence("Seq1", "ABC") });
+    al.setDataset(null);
+    alignFrame = new AlignFrame(al, 3, 1);
+    calcManager = alignFrame.getViewport().getCalcManager();
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    calcManager.shutdown();
+  }
+
+  // Running workers
+
+  @Test(groups = "Functional")
+  public void testStartRegisteredWorker() throws InterruptedException
+  {
+    var sentinel = new Object();
+    var job = CompletableFuture.completedFuture(sentinel);
+    var worker = new AlignCalcWorkerMock(job);
+    calcManager.registerWorker(worker);
+    Thread.sleep(10);
+    assertSame(worker.getLastResult(), sentinel);
+  }
+
+  @Test(groups = "Functional")
+  public void testIsWorking() throws InterruptedException
+  {
+    var job = new CompletableFuture<Object>();
+    var worker = new AlignCalcWorkerMock(job);
+    calcManager.registerWorker(worker);
+    assertTrue(calcManager.isWorking(worker));
+    assertTrue(calcManager.isWorking());
+    job.complete(null);
+    Thread.sleep(10);
+    assertFalse(calcManager.isWorking(worker));
+    assertFalse(calcManager.isWorking());
+  }
+
+  @Test(groups = "Functional")
+  public void testIsWorkingWithAnnotation() throws InterruptedException
+  {
+    var job = new CompletableFuture<Void>();
+    var worker1 = new AlignCalcWorkerMock(job);
+    var annot = worker1.annotation = newAlignmentAnnotation();
+    var otherAnnot = newAlignmentAnnotation();
+    calcManager.registerWorker(worker1);
+    assertTrue(calcManager.isWorkingWithAnnotation(annot));
+    assertFalse(calcManager.isWorkingWithAnnotation(otherAnnot));
+    job.complete(null);
+    Thread.sleep(10);
+    assertFalse(calcManager.isWorkingWithAnnotation(annot));
+  }
+
+  @Test(groups = "Functional")
+  public void testRestartCompletedWorkers() throws Throwable
+  {
+    var sentinel1 = new Object();
+    var sentinel2 = new Object();
+    var job = CompletableFuture.completedFuture(sentinel1);
+    var worker = new AlignCalcWorkerMock(job);
+    calcManager.registerWorker(worker);
+    Thread.sleep(10);
+    assertSame(worker.getLastResult(), sentinel1);
+    job.obtrudeValue(sentinel2);
+    calcManager.restartWorkers();
+    Thread.sleep(10);
+    assertSame(worker.getLastResult(), sentinel2);
+  }
+
+  @Test(groups = "Functional")
+  public void testRestartCancelsWorkers() throws Throwable
+  {
+    var job = new CompletableFuture<Object>();
+    var worker = new AlignCalcWorkerMock(job);
+    var sentinel = new Object();
+    calcManager.registerWorker(worker);
+    Thread.sleep(10);
+    calcManager.restartWorkers();
+    Thread.sleep(10);
+    assertTrue(worker.wasCancelled());
+    job.obtrudeValue(sentinel);
+    Thread.sleep(10);
+    assertSame(worker.getLastResult(), sentinel);
+  }
+
+  // Disabling workers
+
+  @Test(groups = "Functional")
+  public void testDisableWorker()
+  {
+    var worker = new AlignCalcWorkerMock(null);
+    calcManager.registerWorker(worker);
+    calcManager.disableWorker(worker);
+    assertTrue(calcManager.isDisabled(worker));
+    calcManager.enableWorker(worker);
+    assertFalse(calcManager.isDisabled(worker));
+  }
+
+  @Test(groups = "Functional")
+  public void testRestartDisabledWorker() throws InterruptedException
+  {
+    var worker = new AlignCalcWorkerMock(null);
+    calcManager.registerWorker(worker);
+    Thread.sleep(10);
+    assertEquals(worker.getCallCount(), 1);
+    calcManager.disableWorker(worker);
+    calcManager.restartWorkers();
+    Thread.sleep(10);
+    assertEquals(worker.getCallCount(), 1);
+    calcManager.enableWorker(worker);
+    calcManager.restartWorkers();
+    Thread.sleep(10);
+    assertEquals(worker.getCallCount(), 2);
+  }
+
+  // Canceling workers
+
+  @Test(groups = "Functional")
+  public void testCancelWorker() throws InterruptedException
+  {
+    var worker = new AlignCalcWorkerMock(new CompletableFuture<>());
+    calcManager.registerWorker(worker);
+    Thread.sleep(10);
+    calcManager.cancelWorker(worker);
+    Thread.sleep(10);
+    assertTrue(worker.wasCancelled());
+  }
+  
+  // One-shot workers
+  
+  @Test(groups = "Functional")
+  public void testStartOneShotWorker() throws InterruptedException
+  {
+    var job = CompletableFuture.completedFuture("result");
+    var worker = new AlignCalcWorkerMock(job);
+    calcManager.startWorker(worker);
+    Thread.sleep(10);
+    assertEquals(worker.getLastResult(), "result");
+  }
+  
+  @Test(groups = "Functional")
+  public void testCancelOneShotWorker() throws InterruptedException
+  {
+    var worker = new AlignCalcWorkerMock(new CompletableFuture<>());
+    calcManager.startWorker(worker);
+    Thread.sleep(10);
+    calcManager.cancelWorker(worker);
+    Thread.sleep(10);
+    assertTrue(worker.wasCancelled());
+  }
+  
+  @Test(groups = "Functional")
+  public void restartOneShotWorker() throws InterruptedException
+  {
+    var job = CompletableFuture.completedFuture("result1");
+    var worker = new AlignCalcWorkerMock(job);
+    calcManager.startWorker(worker);
+    Thread.sleep(10);
+    job.obtrudeValue("result2");
+    calcManager.restartWorkers();
+    Thread.sleep(10);
+    
+  }
+  
+
+  // Retrieving workers
+
+  @Test(groups = "Functional")
+  public void testGetWorkersOfClass() throws InterruptedException
+  {
+    var worker1 = new AlignCalcWorkerMock(null);
+    var worker2 = new AlignCalcWorkerMock(null);
+    var worker3 = new AlignCalcWorkerMock(null) {};
+    calcManager.registerWorker(worker1);
+    calcManager.registerWorker(worker2);
+    calcManager.registerWorker(worker3);
+    final var workers = calcManager
+        .getWorkersOfClass(AlignCalcWorkerMock.class);
+    assertTrue(workers.contains(worker1) && workers.contains(worker2));
+    assertFalse(workers.contains(worker3));
+  }
+
+  // Removing workers
+
+  @Test(groups = "Functional")
+  public void testRemoveWorker()
+  {
+    var worker = new AlignCalcWorkerMock(null);
+    calcManager.registerWorker(worker);
+    calcManager.removeWorker(worker);
+    assertFalse(calcManager.getWorkers().contains(worker));
+  }
+
+  @Test(groups = "Functional")
+  public void testRemoveWorkersOfClass()
+  {
+    var worker1 = new AlignCalcWorkerMock(null);
+    var worker2 = new AlignCalcWorkerMock(null);
+    var worker3 = new AlignCalcWorkerMock(null) {};
+    calcManager.registerWorker(worker1);
+    calcManager.registerWorker(worker2);
+    calcManager.registerWorker(worker3);
+    calcManager.removeWorkersOfClass(worker1.getClass());
+    assertFalse(calcManager.getWorkers().contains(worker1)
+        || calcManager.getWorkers().contains(worker2));
+    assertTrue(calcManager.getWorkers().contains(worker3));
+  }
+
+  @Test(groups = "Functional")
+  public void testRemoveWorkersForAnnotation()
+  {
+    var worker1 = new AlignCalcWorkerMock(null);
+    var worker2 = new AlignCalcWorkerMock(null);
+    var annot = worker1.annotation = newAlignmentAnnotation();
+    calcManager.registerWorker(worker1);
+    calcManager.registerWorker(worker2);
+    calcManager.removeWorkerForAnnotation(annot);
+    var workers = calcManager.getWorkers();
+    assertFalse(workers.contains(worker1));
+    assertTrue(workers.contains(worker2));
+  }
+
+  @Test(groups = "Functional")
+  public void testRemoveNonRemovableWorker()
+  {
+    var worker = new AlignCalcWorkerMock(null);
+    worker.deletable = false;
+    calcManager.registerWorker(worker);
+    calcManager.removeWorker(worker);
+    assertTrue(calcManager.getWorkers().contains(worker));
+  }
+
+  @Test(groups = "Functional")
+  public void testRemoveNonRemovableWorkersOfClass()
+  {
+    var worker1 = new AlignCalcWorkerMock(null);
+    var worker2 = new AlignCalcWorkerMock(null);
+    worker2.deletable = false;
+    calcManager.registerWorker(worker1);
+    calcManager.registerWorker(worker2);
+    calcManager.removeWorkersOfClass(worker1.getClass());
+    var workers = calcManager.getWorkers();
+    assertFalse(workers.contains(worker1));
+    assertTrue(workers.contains(worker2));
+  }
+
+  private int annotationCount = 0;
+
+  private AlignmentAnnotation newAlignmentAnnotation()
+  {
+    return new AlignmentAnnotation("Ann" + annotationCount++, "description",
+        new Annotation[] {});
+  }
+}
+
+class AlignCalcWorkerMock implements AlignCalcWorkerI
+{
+  AlignmentAnnotation annotation = null;
+  Future<?> job;
+  ArrayList<Object> values = new ArrayList<>();
+  int callCount = 0;
+  boolean deletable = true;
+
+  AlignCalcWorkerMock(Future<?> job)
+  {
+    this.job = job;
+  }
+
+  public Object getLastResult()
+  {
+    return values.isEmpty() ? null : values.get(values.size() - 1);
+  }
+
+  public Throwable getException()
+  {
+    var result = getLastResult();
+    return (result instanceof Throwable) ? (Throwable) result : null;
+  }
+  
+  public int getCallCount() {
+    return callCount;
+  }
+
+  public boolean wasCancelled()
+  {
+    return getException() instanceof InterruptedException;
+  }
+
+  @Override
+  public boolean involves(AlignmentAnnotation annot)
+  {
+    if (annotation == null)
+      return false;
+    else
+      return annot == annotation;
+  }
+
+  @Override
+  public void updateAnnotation()
+  {
+  }
+
+  @Override
+  public void removeAnnotation()
+  {
+  }
+
+  @Override
+  public void run() throws Throwable
+  {
+    callCount++;
+    if (job != null)
+    {
+      try
+      {
+        values.add(job.get());
+      } 
+      catch (InterruptedException e) { values.add(e); }
+      catch (CancellationException e) {
+        values.add(new InterruptedException());
+      } catch (ExecutionException e)
+      {
+        values.add(e.getCause());
+      }
+    }
+  }
+
+  @Override
+  public boolean isDeletable()
+  {
+    return deletable;
+  }
+}
index a523f38..fca61d0 100644 (file)
@@ -24,7 +24,7 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.api.AlignCalcManagerI;
+import jalview.api.AlignCalcManagerI2;
 import jalview.api.AlignCalcWorkerI;
 import jalview.api.FeatureRenderer;
 import jalview.datamodel.Alignment;
@@ -63,11 +63,11 @@ public class AlignCalcManagerTest
   @Test(groups = "Functional")
   public void testRemoveWorkerForAnnotation()
   {
-    AlignCalcManagerI acm = alignFrame.getViewport().getCalcManager();
-    final AlignmentAnnotation ann1 = new AlignmentAnnotation("Ann1", "desc",
-            new Annotation[] {});
-    final AlignmentAnnotation ann2 = new AlignmentAnnotation("Ann2", "desc",
-            new Annotation[] {});
+    AlignCalcManagerI2 acm = alignFrame.getViewport().getCalcManager();
+    final AlignmentAnnotation ann1 = new AlignmentAnnotation("Ann1",
+            "desc", new Annotation[] {});
+    final AlignmentAnnotation ann2 = new AlignmentAnnotation("Ann2",
+            "desc", new Annotation[] {});
 
     /*
      * make two workers for ann1, one deletable, one not
@@ -96,8 +96,7 @@ public class AlignCalcManagerTest
       }
     }
 
-    List<AlignCalcWorkerI> workers = acm
-            .getRegisteredWorkersOfClass(worker1.getClass());
+    List<AlignCalcWorkerI> workers = acm.getWorkersOfClass(worker1.getClass());
     assertEquals(2, workers.size());
     assertTrue(workers.contains(worker1));
     assertTrue(workers.contains(worker2));
@@ -108,10 +107,8 @@ public class AlignCalcManagerTest
      * remove workers for ann2 (there aren't any)
      */
     acm.removeWorkerForAnnotation(ann2);
-    assertTrue(acm.getRegisteredWorkersOfClass(worker1.getClass())
-            .contains(worker1));
-    assertTrue(acm.getRegisteredWorkersOfClass(worker1.getClass())
-            .contains(worker2));
+    assertTrue(acm.getWorkersOfClass(worker1.getClass()).contains(worker1));
+    assertTrue(acm.getWorkersOfClass(worker1.getClass()).contains(worker2));
     assertFalse(acm.isDisabled(worker1));
     assertFalse(acm.isDisabled(worker2));
 
@@ -120,10 +117,8 @@ public class AlignCalcManagerTest
      * - should delete worker1 but not worker2
      */
     acm.removeWorkerForAnnotation(ann1);
-    assertEquals(1,
-            acm.getRegisteredWorkersOfClass(worker1.getClass()).size());
-    assertTrue(acm.getRegisteredWorkersOfClass(worker1.getClass())
-            .contains(worker2));
+    assertEquals(1, acm.getWorkersOfClass(worker1.getClass()).size());
+    assertTrue(acm.getWorkersOfClass(worker1.getClass()).contains(worker2));
     assertFalse(acm.isDisabled(worker1));
     assertFalse(acm.isDisabled(worker2));
   }
@@ -167,9 +162,8 @@ public class AlignCalcManagerTest
   @BeforeMethod(alwaysRun = true)
   public void setUp()
   {
-    AlignmentI al = new Alignment(
-            new SequenceI[]
-            { new Sequence("Seq1", "ABC") });
+    AlignmentI al = new Alignment(new SequenceI[] { new Sequence("Seq1",
+            "ABC") });
     al.setDataset(null);
     alignFrame = new AlignFrame(al, 3, 1);
   }
index e7f9ff1..3f9b9d8 100644 (file)
@@ -57,9 +57,9 @@ public class PDBSequenceFetcherTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // ensure 'add annotation from structure' is selected
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
 
     sf = new SequenceFetcher();
@@ -76,7 +76,7 @@ public class PDBSequenceFetcherTest
   @Test(groups = { "Network" }, enabled = true)
   public void testRnaSeqRetrieve() throws Exception
   {
-    Cache.applicationProperties.setProperty("PDB_DOWNLOAD_FORMAT", "PDB");
+    Cache.setPropertyNoSave("PDB_DOWNLOAD_FORMAT", "PDB");
     List<DbSourceProxy> sps = sf.getSourceProxy("PDB");
     AlignmentI response = sps.get(0).getSequenceRecords("2GIS");
     assertTrue(response != null);
@@ -149,7 +149,7 @@ public class PDBSequenceFetcherTest
         // chains in structure have a mapping to data in the structure
         List<SequenceFeature> prev = null;
         int lastp = -1;
-        for (int col = 1; col <= sq.getLength(); col++)
+        for (int col = 1, ns = sq.getLength(); col <= ns; col++)
         {
           List<SequenceFeature> sf = sq.findFeatures(col, col, "RESNUM");
           if (sf.size() != 1)
index d48ff91..4cacf01 100644 (file)
@@ -66,9 +66,9 @@ public class RemoteFormatTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // ensure 'add annotation from structure' is selected
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
 
     sf = new SequenceFetcher();
diff --git a/test/jalview/ws/ebi/HmmerJSONProcessTest.java b/test/jalview/ws/ebi/HmmerJSONProcessTest.java
new file mode 100644 (file)
index 0000000..bf68906
--- /dev/null
@@ -0,0 +1,122 @@
+package jalview.ws.ebi;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileParse;
+import jalview.io.FormatAdapter;
+
+import java.io.File;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class HmmerJSONProcessTest {
+  public static File alignmentFragFile = new File(
+          "examples/testdata/hmmer3/alignment_frag.fa.gz");
+
+  private AlignmentI getSearchResultFragmentAlignment() throws Exception
+  {
+    AlignmentI alf = new FormatAdapter().readFile(
+            alignmentFragFile.getAbsolutePath(), DataSourceType.FILE,
+            FileFormat.Fasta);
+
+    return alf;
+  }
+
+  public static File alignmentResultFile = new File(
+          "examples/testdata/hmmer3/alignment_res.fa.gz");
+
+  private AlignmentI getSearchResultAlignment() throws Exception
+  {
+    AlignmentI alf = new FormatAdapter().readFile(
+            alignmentResultFile.getAbsolutePath(), DataSourceType.FILE,
+            FileFormat.Fasta);
+
+    return alf;
+  }
+
+  public static String hitTestFile = "examples/testdata/hmmer3/hit_fragment.json.gz",
+          hmmerResultFile = "examples/testdata/hmmer3/hmmeresult.json.gz";
+
+
+  @Test(groups = { "Functional" })
+  public void parseHitTest() throws Exception
+  {
+
+    Assert.assertTrue(new File(hitTestFile).exists(),
+            "Can't find test data.\n"
+            + hitTestFile);
+    JSONParser jp = new JSONParser();
+    // read JSON in same way - via fileparse
+    Object hitfragment = jp.parse(new FileParse(hitTestFile,
+            DataSourceType.FILE).getReader());
+    Assert.assertTrue((hitfragment instanceof JSONObject),
+            "Didn't find a JSON object map in " + hitTestFile);
+    AlignmentI searchResult = getSearchResultFragmentAlignment();
+
+    Assert.assertTrue(searchResult != null && searchResult.getHeight() > 0,
+            "Didn't read search result alignment from " + alignmentFragFile);
+
+    HmmerJSONProcessor hjsp = new HmmerJSONProcessor(searchResult);
+    hjsp.addHit((JSONObject) hitfragment, 1);
+    // check that
+    // scores, posterior probabilities and stuff exist.
+  }
+
+  @Test(groups = { "Functional" })
+  public void parseJsonResultTest() throws Exception
+  {
+
+    Assert.assertTrue(new File(hmmerResultFile).exists(),
+            "Can't find test data.\n" + hmmerResultFile);
+
+    AlignmentI searchResult = getSearchResultAlignment();
+
+    Assert.assertTrue(searchResult != null && searchResult.getHeight() > 0,
+            "Didn't read search result alignment from " + alignmentFragFile);
+
+    HmmerJSONProcessor hjsp = new HmmerJSONProcessor(searchResult);
+    hjsp.parseFrom(new FileParse(hmmerResultFile, DataSourceType.FILE));
+    AlignmentAnnotation[] aa = searchResult.getSequenceAt(5)
+            .getAnnotation();
+    Assert.assertNotNull(aa);
+    Assert.assertEquals(aa.length, 3,
+            "didn't get expected set of annotation.\n");
+    // DPTSERWFHGHLSGKEAEKLLTeKGKHGSFLVRESQSHPGDFVLSVRTgddkgesndgKSKVTHVMIR-CQELKYDVGGGERFDSLTDLVEHYKKNPmvet
+    // LGTVLQLKQP
+    // 5789*****************9799***********************999998888888********.99**************************9999
+    // 899999*999
+    // AlignmentAnnotation
+    // 101 == 8
+    String seq = "tLGT";
+    SequenceI s5 = searchResult.getSequenceAt(5);
+    Assert.assertEquals(
+            s5.getSubSequence(s5.findIndex(225), s5.findIndex(229))
+                    .getSequenceAsString(),
+            seq);
+    int pos = s5.findIndex(226);
+    for (AlignmentAnnotation an : aa)
+    {
+      if (an.label.startsWith("Posterior"))
+      {
+        Assert.assertEquals(an.annotations[pos].value, 8f);
+
+      }
+    }
+    ;
+    // check that
+    // scores, posterior probabilities and stuff exist.
+  }
+  // Groovy test
+  // def al = Jalview.getAlignFrames()[0].getViewport().getAlignment()
+  // def jproc = new jalview.ws.ebi.HmmerJSONProcessor(al)
+  // jproc.parseFrom(new
+  // jalview.io.FileParse("examples/testdata/hmmer3/hmmeresult.json.gz","File"))
+  // jproc.updateView(Jalview.getAlignFrames()[0].getViewport())
+
+}
index e18e9fb..8dbb1fe 100644 (file)
@@ -27,10 +27,13 @@ import jalview.bin.Console;
 import jalview.gui.JvOptionPane;
 import jalview.gui.WsJobParameters;
 import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jabaws.JalviewJabawsTestUtils;
 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.ParamDatastoreI;
 
 import java.awt.BorderLayout;
 import java.awt.event.WindowAdapter;
@@ -61,12 +64,12 @@ public class Jws2ParamView
   /**
    * which services to test
    */
-  public static List<String> serviceTests = new ArrayList<String>();
+  public static List<String> serviceTests = new ArrayList<>();
 
   /**
    * which presets to test for services
    */
-  public static List<String> presetTests = new ArrayList<String>();
+  public static List<String> presetTests = new ArrayList<>();
   static
   {
     serviceTests.add("AAConWS".toLowerCase(Locale.ROOT));
@@ -78,7 +81,6 @@ public class Jws2ParamView
   public static void setUpBeforeClass() throws Exception
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Console.initLogger();
     disc = JalviewJabawsTestUtils.getJabawsDiscoverer();
   }
 
@@ -91,10 +93,12 @@ public class Jws2ParamView
   public void testJws2Gui()
   {
     Iterator<String> presetEnum = presetTests.iterator();
-    for (Jws2Instance service : disc.getServices())
+    for (ServiceWithParameters _service : disc.getServices())
     {
-      if (serviceTests.size() == 0 || serviceTests
-              .contains(service.serviceType.toLowerCase(Locale.ROOT)))
+      // This will fail for non-jabaws services
+      Jws2Instance service = (Jws2Instance) _service;
+      if (serviceTests.size() == 0
+         || serviceTests.contains(service.getName().toLowerCase(Locale.ROOT)))
       {
         List<Preset> prl = null;
         Preset pr = null;
@@ -130,8 +134,9 @@ public class Jws2ParamView
             }
             pr = en.next();
           }
-          WsJobParameters pgui = new WsJobParameters(service,
-                  new JabaPreset(service, pr));
+          WsJobParameters pgui = new WsJobParameters((ParamDatastoreI) null,
+                  service, new JabaPreset(service, pr),
+                  (List<ArgumentI>) null);
           JFrame jf = new JFrame(MessageManager
                   .formatMessage("label.ws_parameters_for", new String[]
                   { service.getActionText() }));
diff --git a/test/jalview/ws/jabaws/AAConAnnotAndSettingsIO.java b/test/jalview/ws/jabaws/AAConAnnotAndSettingsIO.java
new file mode 100644 (file)
index 0000000..5428acd
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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.jabaws;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.SeqAnnotationServiceCalcWorker;
+import jalview.ws.slivkaws.SlivkaWSDiscoverer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/*
+ * All methods in this class are set to the Network group because setUpBeforeClass will fail
+ * if there is no network.
+ */
+@Test(singleThreaded = true)
+public class AAConAnnotAndSettingsIO
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  public static String testseqs = "examples/uniref50.fa";
+
+  public static Jws2Discoverer disc;
+
+  public static List<ServiceWithParameters[]> aacon;
+
+  jalview.ws.jws2.SeqAnnotationServiceCalcWorker aaconClient;
+
+  public static jalview.gui.AlignFrame af = null;
+
+  @BeforeClass(alwaysRun = true)
+  public static void setUpBeforeClass() throws Exception
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+    Cache.initLogger();
+    disc = JalviewJabawsTestUtils.getJabawsDiscoverer();
+
+    while (disc.isRunning())
+    {
+      // don't get services until discoverer has finished
+      Thread.sleep(100);
+    }
+
+
+    aacon = new ArrayList<>();
+    for (ServiceWithParameters svc : disc.getServices())
+    {
+      if (svc.getNameURI().toLowerCase().contains("aacon"))
+      {
+        aacon.add(new ServiceWithParameters[] { svc });
+      }
+    }
+
+    for (ServiceWithParameters svc : SlivkaWSDiscoverer.getInstance()
+            .getServices())
+    {
+      if (svc.getNameURI().toLowerCase().contains("aacon"))
+      {
+        aacon.add(new ServiceWithParameters[] { svc });
+      }
+    }
+    assertTrue("Couldn't discover any AACon services to use to test.",
+            aacon.size() > 0);
+  }
+
+  @AfterClass(alwaysRun = true)
+  public static void tearDownAfterClass() throws Exception
+  {
+    if (af != null)
+    {
+      af.setVisible(false);
+      af.dispose();
+      af = null;
+    }
+  }
+
+  @DataProvider(name = "aacons")
+  public Object[][] getAaconArray()
+  {
+
+    Object[][] rtn = new Object[aacon.size()][1];
+    int i = 0;
+    for (ServiceWithParameters[] aa : aacon)
+    {
+      rtn[i++] = aa;
+    }
+    return rtn;
+  }
+  /**
+   * Run AACon on an alignment with defaults and verify Just Shenkin annotation
+   * appears
+   */
+  @Test(groups = { "External", "Network" }, dataProvider = "aacons")
+  public void testAAConAnnotAndRecovery(ServiceWithParameters service)
+  {
+    jalview.io.FileLoader fl = new jalview.io.FileLoader(false);
+    AlignFrame _af = fl.LoadFileWaitTillLoaded(testseqs,
+            jalview.io.DataSourceType.FILE);
+    assertNotNull("Couldn't load test data ('" + testseqs + "')", _af);
+    af = _af;
+    try
+    {
+    testAAConClient(_af, service);
+    } finally
+    {
+      af = null;
+      _af.setVisible(false);
+      _af.dispose();
+      _af = null;
+
+    }
+  }
+
+  /**
+   * triggers the given aacon worker on the alignment, waits for 5s and gives up
+   * or verifies SHENKIN annotation is produced.
+   * 
+   * @param af
+   *          - test data in an alignment frame
+   * @param aacon
+   *          - the service to test
+   */
+  static void testAAConClient(AlignFrame af, ServiceWithParameters aacon)
+  {
+    SeqAnnotationServiceCalcWorker aaconClient = new SeqAnnotationServiceCalcWorker(
+            aacon, af, null,
+            null);
+    long current = System.currentTimeMillis(), limit = 15;
+    af.getViewport().getCalcManager().startWorker(aaconClient);
+    do
+    {
+      try
+      {
+        Thread.sleep(50);
+      } catch (InterruptedException x)
+      {
+      }
+      ;
+      assertTrue(
+              "Waited " + limit + "s for " + aacon.getHostURL()
+                      + " - giving up.",
+              (System.currentTimeMillis() - current) < limit * 1000);
+    } while (af.getViewport().getCalcManager().isWorking());
+    AlignmentI orig_alig = af.getViewport().getAlignment();
+    boolean foundShenkin = false;
+    Iterable<AlignmentAnnotation> _aa=orig_alig
+            .findAnnotation(aacon.getAlignAnalysisUI().getCalcId());
+    assertTrue("No annotation from service",
+            _aa != null && _aa.iterator().hasNext());
+
+    for (AlignmentAnnotation aa : _aa)
+    {
+      assertTrue("AACon annotation not marked as autocalculated!",
+              aa.autoCalculated);
+      if ("shenkin".equals(aa.label.toLowerCase()))
+      {
+        foundShenkin = true;
+        break;
+      }
+    }
+    assertTrue("Failed to locate 'SHENKIN' annotation row.", foundShenkin);
+  }
+}
index 629b6c3..6bcc1da 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.ws.jabaws;
 
 import java.util.Locale;
-
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
@@ -35,9 +34,10 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 import jalview.io.StockholmFileTest;
-import jalview.ws.jws2.AADisorderClient;
+import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.jws2.SeqAnnotationServiceCalcWorker;
+import jalview.ws.slivkaws.SlivkaWSDiscoverer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,6 +45,7 @@ import java.util.List;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 /*
@@ -66,9 +67,9 @@ public class DisorderAnnotExportImport
 
   public static Jws2Discoverer disc;
 
-  public static List<Jws2Instance> iupreds;
+  public static List<ServiceWithParameters> iupreds;
 
-  jalview.ws.jws2.AADisorderClient disorderClient;
+  jalview.ws.jws2.SeqAnnotationServiceCalcWorker disorderClient;
 
   public static jalview.gui.AlignFrame af = null;
 
@@ -85,8 +86,9 @@ public class DisorderAnnotExportImport
       Thread.sleep(100);
     }
 
-    iupreds = new ArrayList<Jws2Instance>();
-    for (Jws2Instance svc : disc.getServices())
+    SlivkaWSDiscoverer disc2 = SlivkaWSDiscoverer.getInstance();
+    disc2.startDiscoverer();
+    while (disc2.isRunning())
     {
       if (svc.getServiceTypeURI().toLowerCase(Locale.ROOT)
               .contains("iupredws"))
@@ -112,14 +114,29 @@ public class DisorderAnnotExportImport
       af = null;
     }
   }
+  
+  @DataProvider(name="getIuPreds",parallel = false)
+  public static ServiceWithParameters[][] getIuPreds()
+  {
+    ServiceWithParameters[][] services = new ServiceWithParameters[iupreds
+            .size()][1];
+
+    int i = 0;
+    for (ServiceWithParameters iupred : iupreds)
+    {
+      services[i++][0] = iupred;
+    }
+    return services;
+  }
 
   /**
    * test for patches to JAL-1294
    */
-  @Test(groups = { "External", "Network" })
-  public void testDisorderAnnotExport()
+  @Test(groups = { "External", "Network" },dataProvider = "getIuPreds")
+  public void testDisorderAnnotExport(ServiceWithParameters iuPred)
   {
-    disorderClient = new AADisorderClient(iupreds.get(0), af, null, null);
+    disorderClient = new SeqAnnotationServiceCalcWorker(iuPred, af, null,
+            null);
     af.getViewport().getCalcManager().startWorker(disorderClient);
     do
     {
@@ -134,7 +151,7 @@ public class DisorderAnnotExportImport
     AlignmentI orig_alig = af.getViewport().getAlignment();
     // NOTE: Consensus annotation row cannot be exported and reimported
     // faithfully - so we remove them
-    List<AlignmentAnnotation> toremove = new ArrayList<AlignmentAnnotation>();
+    List<AlignmentAnnotation> toremove = new ArrayList<>();
     for (AlignmentAnnotation aa : orig_alig.getAlignmentAnnotation())
     {
       if (aa.autoCalculated)
index 8f003cb..3c115b2 100644 (file)
@@ -85,7 +85,7 @@ public class JalviewJabawsTestUtils
   public static Jws2Discoverer getJabawsDiscoverer(boolean localhost)
   {
     jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
-            .getDiscoverer();
+            .getInstance();
     String svcurls = "";
     if (localhost)
     {
@@ -97,7 +97,7 @@ public class JalviewJabawsTestUtils
         services.add(url);
       }
       ;
-      Jws2Discoverer.getDiscoverer().setServiceUrls(services);
+      Jws2Discoverer.getInstance().setServiceUrls(services);
     }
     try
     {
index bc11d2f..0a9cb19 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.ws.jabaws;
 
 import java.util.Locale;
-
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
@@ -36,8 +35,10 @@ import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 import jalview.io.StockholmFileTest;
 import jalview.project.Jalview2XML;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.JabaParamStore;
 import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.jws2.RNAalifoldClient;
+import jalview.ws.jws2.SeqAnnotationServiceCalcWorker;
 import jalview.ws.jws2.SequenceAnnotationWSClient;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.AutoCalcSetting;
@@ -81,7 +82,7 @@ public class RNAStructExportImport
 
   public static Jws2Instance rnaalifoldws;
 
-  jalview.ws.jws2.RNAalifoldClient alifoldClient;
+  SeqAnnotationServiceCalcWorker alifoldClient;
 
   public static jalview.gui.AlignFrame af = null;
 
@@ -98,13 +99,13 @@ public class RNAStructExportImport
       Thread.sleep(100);
     }
 
-    for (Jws2Instance svc : disc.getServices())
+    for (ServiceWithParameters svc : disc.getServices())
     {
 
       if (svc.getServiceTypeURI().toLowerCase(Locale.ROOT)
               .contains("rnaalifoldws"))
       {
-        rnaalifoldws = svc;
+        rnaalifoldws = (Jws2Instance) svc;
       }
     }
 
@@ -159,7 +160,8 @@ public class RNAStructExportImport
   public void testRNAAliFoldValidStructure()
   {
 
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null, null);
+    alifoldClient = new SeqAnnotationServiceCalcWorker(rnaalifoldws, af, null,
+            null);
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);
 
@@ -192,7 +194,8 @@ public class RNAStructExportImport
   public void testRNAStructExport()
   {
 
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null, null);
+    alifoldClient = new SeqAnnotationServiceCalcWorker(rnaalifoldws, af, null,
+            null);
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);
 
@@ -209,7 +212,7 @@ public class RNAStructExportImport
     AlignmentI orig_alig = af.getViewport().getAlignment();
     // JBPNote: this assert fails (2.10.2) because the 'Reference Positions'
     // annotation is mistakenly recognised as an RNA annotation row when read in
-    // as an annotation file.
+    // as an annotation file. bug is JAL-3122
     verifyAnnotationFileIO("Testing RNAalifold Annotation IO", orig_alig);
 
   }
@@ -278,7 +281,8 @@ public class RNAStructExportImport
         opts.add(rg);
       }
     }
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null, opts);
+    alifoldClient = new SeqAnnotationServiceCalcWorker(rnaalifoldws, af, null,
+            JabaParamStore.getJwsArgsfromJaba(opts));
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);
 
index 0802b8a..1d3c191 100644 (file)
@@ -21,7 +21,6 @@
 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;
@@ -29,6 +28,8 @@ import static org.testng.AssertJUnit.assertTrue;
 import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.gui.JvOptionPane;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.api.UIinfo;
 import jalview.ws.jabaws.JalviewJabawsTestUtils;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 
@@ -64,7 +65,7 @@ public class ParameterUtilsTest
    * To limit tests to specify services, add them to this list; leave list empty
    * to test all
    */
-  private static List<String> serviceTests = new ArrayList<String>();
+  private static List<String> serviceTests = new ArrayList<>();
 
   private static Jws2Discoverer disc = null;
 
@@ -80,10 +81,11 @@ public class ParameterUtilsTest
   @Test(groups = { "Network" })
   public void testWriteParameterSet() throws WrongParameterException
   {
-    for (Jws2Instance service : disc.getServices())
+    for (ServiceWithParameters _service : disc.getServices())
     {
-      if (isForTesting(service))
+      if (isForTesting(_service))
       {
+        Jws2Instance service = (Jws2Instance) _service;
 
         List<Preset> prl = null;
         PresetManager prman = service.getPresets();
@@ -131,7 +133,7 @@ public class ParameterUtilsTest
    * @param service
    * @return
    */
-  public boolean isForTesting(Jws2Instance service)
+  public boolean isForTesting(UIinfo service)
   {
     return serviceTests.size() == 0 || serviceTests
             .contains(service.serviceType.toLowerCase(Locale.ROOT));
@@ -140,10 +142,11 @@ public class ParameterUtilsTest
   @Test(groups = { "Network" })
   public void testCopyOption()
   {
-    for (Jws2Instance service : disc.getServices())
+    for (ServiceWithParameters _service : disc.getServices())
     {
-      if (isForTesting(service))
+      if (isForTesting(_service))
       {
+        Jws2Instance service = (Jws2Instance) _service;
         List<Option<?>> options = service.getRunnerConfig().getOptions();
         for (Option<?> o : options)
         {
@@ -164,10 +167,11 @@ public class ParameterUtilsTest
   @Test(groups = { "Network" })
   public void testCopyParameter()
   {
-    for (Jws2Instance service : disc.getServices())
+    for (ServiceWithParameters _service : disc.getServices())
     {
-      if (isForTesting(service))
+      if (isForTesting(_service))
       {
+        Jws2Instance service = (Jws2Instance) _service;
         List<Parameter> parameters = service.getRunnerConfig()
                 .getParameters();
         for (Parameter o : parameters)
index cd17a63..dded0ee 100644 (file)
@@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.gui.AlignFrame;
 import jalview.gui.JvOptionPane;
+import jalview.ws.rest.clientdefs.ShmrRestClient;
 
 import java.util.Map;
 
@@ -52,13 +53,13 @@ public class ShmmrRSBSService
     assertTrue(
             "Test Rsd Exchange using using default Shmmr service failed.",
             testRsdExchange("Test using default Shmmr service",
-                    RestClient.makeShmmrRestClient().service));
+                    ShmrRestClient.makeShmmrRestClient().service));
   }
 
   @Test(groups = { "Functional" })
   public void testShmmrServiceDataprep() throws Exception
   {
-    RestClient _rc = RestClient.makeShmmrRestClient();
+    RestClient _rc = ShmrRestClient.makeShmmrRestClient();
     assertNotNull(_rc);
     AlignFrame alf = new jalview.io.FileLoader(false)
             .LoadFileWaitTillLoaded("examples/testdata/smad.fa",
@@ -127,6 +128,12 @@ public class ShmmrRSBSService
       if (!service.equals(newService))
       {
         System.err.println("Failed for service (" + desc + ").");
+        if (fromservicetostring.equals(newService.toString()))
+        {
+          System.err.println(
+                  "Description strings are equivalent: fault during RestServiceDescription.equals()");
+          return false;
+        }
         System.err.println("Original service and parsed service differ.");
         System.err.println("Original: " + fromservicetostring);
         System.err.println("Parsed  : " + newService.toString());
index 44a6a02..ebe7f25 100644 (file)
@@ -23,16 +23,6 @@ package jalview.ws.sifts;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
-import jalview.api.DBRefEntryI;
-import jalview.bin.Cache;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.gui.JvOptionPane;
-import jalview.io.DataSourceType;
-import jalview.structure.StructureMapping;
-import jalview.xml.binding.sifts.Entry.Entity;
 
 import java.io.File;
 import java.io.IOException;
@@ -48,6 +38,16 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
+import jalview.api.DBRefEntryI;
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.structure.StructureMapping;
+import jalview.xml.binding.sifts.Entry.Entity;
 import mc_view.Atom;
 import mc_view.PDBfile;
 
@@ -62,18 +62,19 @@ public class SiftsClientTest
   }
 
   public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = System
-          .getProperty("user.home") + File.separatorChar
+          .getProperty("user.home")
+          + File.separatorChar
           + ".sifts_downloads" + File.separatorChar;
 
   private String testPDBId = "1a70";
 
   private SiftsClient siftsClient = null;
 
-  SequenceI testSeq = new Sequence("P00221",
+  SequenceI testSeq = new Sequence(
+          "P00221",
           "MAAT..TTTMMG..MATTFVPKPQAPPMMAALPSNTGR..SLFGLKT.GSR..GGRMTMA"
                   + "AYKVTLVTPTGNVEFQCPDDVYILDAAEEEGIDLPYSCRAGSCSSCAGKLKTGSLNQDD"
-                  + "QSFLDDDQIDEGWVLTCAAYPVSDVTIETHKEEELTA.",
-          1, 147);
+                  + "QSFLDDDQIDEGWVLTCAAYPVSDVTIETHKEEELTA.", 1, 147);
 
   int u = SiftsClient.UNASSIGNED;
 
@@ -188,15 +189,14 @@ public class SiftsClientTest
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // SIFTs entries are updated weekly - so use saved SIFTs file to enforce
     // test reproducibility
-    new SiftsSettings();
-    SiftsSettings.setSiftDownloadDirectory(jalview.bin.Cache
-            .getDefault("sifts_download_dir", DEFAULT_SIFTS_DOWNLOAD_DIR));
+    SiftsSettings.setSiftDownloadDirectory(jalview.bin.Cache.getDefault(
+            "sifts_download_dir", DEFAULT_SIFTS_DOWNLOAD_DIR));
     SiftsSettings.setMapWithSifts(true);
     SiftsSettings.setCacheThresholdInDays("2");
     SiftsSettings.setFailSafePIDThreshold("70");
     PDBfile pdbFile;
-    pdbFile = new PDBfile(false, false, false,
-            "test/jalview/io/" + testPDBId + ".pdb", DataSourceType.FILE);
+    pdbFile = new PDBfile(false, false, false, "test/jalview/io/"
+            + testPDBId + ".pdb", DataSourceType.FILE);
     siftsClient = new SiftsClient(pdbFile);
   }
 
@@ -277,8 +277,8 @@ public class SiftsClientTest
 
     try
     {
-      HashMap<Integer, int[]> actualMapping = siftsClient
-              .getGreedyMapping("A", testSeq, null);
+      HashMap<Integer, int[]> actualMapping = siftsClient.getGreedyMapping(
+              "A", testSeq, null);
       Assert.assertEquals(testSeq.getStart(), 1);
       Assert.assertEquals(testSeq.getEnd(), 147);
       // Can't do Assert.assertEquals(actualMapping, expectedMapping);
@@ -310,14 +310,13 @@ public class SiftsClientTest
     atom.atomIndex = 7;
     atoms.add(atom);
     int actualAtomIndex = siftsClient.getAtomIndex(1, atoms);
-    Assert.assertEquals(actualAtomIndex, siftsClient.UNASSIGNED);
+    Assert.assertEquals(actualAtomIndex, SiftsClient.UNASSIGNED);
     actualAtomIndex = siftsClient.getAtomIndex(43, atoms);
     Assert.assertEquals(actualAtomIndex, 7);
   }
 
   @Test(
-    groups =
-    { "Network" },
+    groups = { "Network" },
     expectedExceptions = IllegalArgumentException.class)
   private void getAtomIndexNullTest()
   {
@@ -330,14 +329,18 @@ public class SiftsClientTest
 
   }
 
-  @Test(groups = { "Network" }, expectedExceptions = SiftsException.class)
+  @Test(
+groups = { "Network" },
+    expectedExceptions = SiftsException.class)
   private void populateAtomPositionsNullTest1()
           throws IllegalArgumentException, SiftsException
   {
     siftsClient.populateAtomPositions(null, null);
   }
 
-  @Test(groups = { "Network" }, expectedExceptions = SiftsException.class)
+  @Test(
+groups = { "Network" },
+    expectedExceptions = SiftsException.class)
   private void populateAtomPositionsNullTest2()
           throws IllegalArgumentException, SiftsException
   {
@@ -356,14 +359,18 @@ public class SiftsClientTest
     Assert.assertEquals(actualValidSrcDBRef, expectedDBRef);
   }
 
-  @Test(groups = { "Network" }, expectedExceptions = SiftsException.class)
+  @Test(
+groups = { "Network" },
+    expectedExceptions = SiftsException.class)
   public void getValidSourceDBRefExceptionTest() throws SiftsException
   {
     SequenceI invalidTestSeq = new Sequence("testSeq", "ABCDEFGH");
     siftsClient.getValidSourceDBRef(invalidTestSeq);
   }
 
-  @Test(groups = { "Network" }, expectedExceptions = SiftsException.class)
+  @Test(
+groups = { "Network" },
+    expectedExceptions = SiftsException.class)
   public void getValidSourceDBRefExceptionXTest() throws SiftsException
   {
     SequenceI invalidTestSeq = new Sequence("testSeq", "ABCDEFGH");
@@ -387,10 +394,11 @@ public class SiftsClientTest
   public void getSiftsStructureMappingTest() throws SiftsException
   {
     Assert.assertTrue(SiftsSettings.isMapWithSifts());
-    StructureMapping strucMapping = siftsClient
-            .getSiftsStructureMapping(testSeq, testPDBId, "A");
+    StructureMapping strucMapping = siftsClient.getSiftsStructureMapping(
+            testSeq, testPDBId, "A");
     String expectedMappingOutput = "\nSequence âŸ· Structure mapping details\n"
-            + "Method: SIFTS\n\n" + "P00221 :  51 - 147 Maps to \n"
+            + "Method: SIFTS\n\n"
+            + "P00221 :  51 - 147 Maps to \n"
             + "1A70|A :  1 - 97\n\n"
             + "P00221 AAYKVTLVTPTGNVEFQCPDDVYILDAAEEEGIDLPYSCRAGSCSSCAGKLKTGSLNQDDQSFLD\n"
             + "       |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"
@@ -414,8 +422,8 @@ public class SiftsClientTest
     while (it.hasNext())
     {
       Map.Entry<Integer, int[]> pair = it.next();
-      Assert.assertTrue(
-              strucMapping.getMapping().containsKey(pair.getKey()));
+      Assert.assertTrue(strucMapping.getMapping()
+              .containsKey(pair.getKey()));
       Assert.assertEquals(strucMapping.getMapping().get(pair.getKey()),
               pair.getValue());
     }
@@ -462,13 +470,13 @@ public class SiftsClientTest
   }
 
   @Test(groups = { "Network" })
-  public void getEntityByMostOptimalMatchedIdTest1()
-          throws IOException, SiftsException
+  public void getEntityByMostOptimalMatchedIdTest1() throws IOException,
+          SiftsException
   {
     SiftsClient siftsClientX = null;
     PDBfile pdbFile;
-    pdbFile = new PDBfile(false, false, false,
-            "test/jalview/io/2nq2" + ".pdb", DataSourceType.FILE);
+    pdbFile = new PDBfile(false, false, false, "test/jalview/io/2nq2"
+            + ".pdb", DataSourceType.FILE);
     siftsClientX = new SiftsClient(pdbFile);
     Entity entityA = siftsClientX.getEntityByMostOptimalMatchedId("A");
     Assert.assertEquals(entityA.getEntityId(), "A");
@@ -482,8 +490,8 @@ public class SiftsClientTest
   }
 
   @Test(groups = { "Network" })
-  public void getEntityByMostOptimalMatchedIdTest2()
-          throws IOException, SiftsException
+  public void getEntityByMostOptimalMatchedIdTest2() throws IOException,
+          SiftsException
   {
     // This test is for a SIFTS file in which entity A should map to chain P for
     // the given PDB Id. All the other chains shouldn't be mapped as there are
@@ -505,18 +513,4 @@ public class SiftsClientTest
     Assert.assertNull(entityP);
 
   }
-
-  @Test(groups = { "Network" })
-  public void getLeadingIntegerFromString()
-  {
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("1234abcd", -1),
-            1234);
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("1234", -1),
-            1234);
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("abcd", -1), -1);
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("abcd1234", -1),
-            -1);
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("None", -1), -1);
-    Assert.assertEquals(SiftsClient.getLeadingIntegerValue("Null", -1), -1);
-  }
 }
index 3a24661..be8cee4 100644 (file)
@@ -36,7 +36,8 @@ public class UrlDownloadClientTest
   public void UrlDownloadTest()
   {
     UrlDownloadClient client = new UrlDownloadClient();
-    String urlstring = "http://identifiers.org/rest/collections/";
+    String urlstring = "http://www.jalview.org/services/identifiers";
+    // was "http://identifiers.org/rest/collections/";
     String outfile = "testfile.tmp";
 
     try
@@ -59,12 +60,14 @@ public class UrlDownloadClientTest
 
     // download file has a believable size
     // identifiers.org file typically at least 250K
-    Assert.assertTrue(f.length() > 250000);
-
+    long n = f.length();
     if (f.exists())
     {
       f.delete();
     }
+    // 74589
+    Assert.assertTrue(n > 70000);
+
 
   }
 
index b636669..0e9037c 100644 (file)
@@ -326,11 +326,11 @@ public class PDBfileTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN",
+    Cache.setPropertyNoSave("ADD_TEMPFACT_ANN",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     StructureImportSettings.setDefaultStructureFileFormat("PDB");
   }
diff --git a/unused/JalviewAppLoader.java b/unused/JalviewAppLoader.java
new file mode 100644 (file)
index 0000000..0f4833c
--- /dev/null
@@ -0,0 +1,27 @@
+package jalview.bin;
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+import jalview.gui.AlignFrame;
+import jalview.io.BioJsHTMLOutput;
+import jalview.io.FileFormatI;
+import jalview.io.HtmlSvgOutput;
+import jalview.util.Platform;
+
+/**
+ * Abandoned -- see JalviewApp
+ * 
+ * A class to load parameters for either JalviewLite or Jalview
+ * 
+ * @author hansonr
+ *
+ */
+public class JalviewAppLoader
+{
+  
+  public JalviewAppLoader()
+  {
+  }
+
+}
\ No newline at end of file
diff --git a/unused/JalviewJSApp.java b/unused/JalviewJSApp.java
new file mode 100644 (file)
index 0000000..654569d
--- /dev/null
@@ -0,0 +1,12 @@
+package jalview.bin;
+
+import java.util.List;
+
+import jalview.appletgui.js.JSFunctionExec;
+
+public interface JalviewJSApp
+{
+
+
+
+}
index 360a700..2cbfeb9 100755 (executable)
@@ -40,7 +40,7 @@
                <param name="file2" value="${file2}" />
        </antcall>
        <echo message=" "/>
-       <echo message="Missing message labels in Messages.properties compare to ${file2}"/>
+       <echo message="Missing message labels in Messages.properties compared to ${file2}"/>
        <antcall target="compareProperties">
                <param name="file2" value="resources/lang/Messages.properties"/>
                <param name="file1" value="${file2}" />
diff --git a/utils/jalviewjs/_j2sclasslist.txt b/utils/jalviewjs/_j2sclasslist.txt
deleted file mode 100644 (file)
index e35111e..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-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/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/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/SB.js
-javax/net/ssl/HttpsUrlConnection.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/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/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/font/FontDesignMetrics.js
-sun/swing/DefaultLookup.js
-sun/swing/SwingLazyValue.js
-sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
-sun/util/resources/LocaleData.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/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
-
-
index 5d60006..739f070 100644 (file)
@@ -1,49 +1,66 @@
+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/AlignmentSorter.js
 jalview/analysis/AlignmentUtils.js
 jalview/analysis/AnnotationSorter.js
 jalview/analysis/Conservation.js
 jalview/analysis/CrossRef.js
-jalview/analysis/NJTree.js
+jalview/analysis/GeneticCodeI.js
+jalview/analysis/GeneticCodes.js
 jalview/analysis/SeqsetUtils.js
-jalview/analysis/TreeBuilder.js
+jalview/analysis/SequenceIdMatcher.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/AlignCalcManagerI2.js
 jalview/api/AlignCalcWorkerI.js
 jalview/api/AlignViewControllerGuiI.js
 jalview/api/AlignViewControllerI.js
 jalview/api/AlignViewportI.js
 jalview/api/AlignmentViewPanel.js
 jalview/api/BuildDetailsI.js
+jalview/api/DBRefEntryI.js
 jalview/api/FeatureColourI.js
 jalview/api/FeatureRenderer.js
+jalview/api/FeatureSettingsModelI.js
 jalview/api/FeaturesDisplayedI.js
+jalview/api/FeaturesSourceI.js
+jalview/api/JalviewJSApi.js
 jalview/api/OOMHandlerI.js
+jalview/api/PollableAlignCalcWorkerI.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/AppletParams.js
+jalview/bin/ApplicationSingletonProvider.js
 jalview/bin/ArgsParser.js
 jalview/bin/BuildDetails.js
 jalview/bin/Cache.js
 jalview/bin/Jalview.js
-jalview/bin/JalviewJS2.js
+jalview/bin/JalviewJSApp.js
+jalview/bin/JalviewTaskbar.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/AlignmentOrder.js
 jalview/datamodel/AlignmentView.js
 jalview/datamodel/AnnotatedCollectionI.js
 jalview/datamodel/Annotation.js
@@ -53,11 +70,14 @@ 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
@@ -66,6 +86,7 @@ jalview/datamodel/ProfilesI.js
 jalview/datamodel/Range.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
@@ -76,31 +97,49 @@ jalview/datamodel/SequenceFeature.js
 jalview/datamodel/SequenceGroup.js
 jalview/datamodel/SequenceI.js
 jalview/datamodel/SequenceNode.js
+jalview/datamodel/StructureViewerModel.js
+jalview/datamodel/features/FeatureAttributes.js
 jalview/datamodel/features/FeatureLocationI.js
-jalview/datamodel/features/FeatureMatcher.js
-jalview/datamodel/features/FeatureMatcherI.js
-jalview/datamodel/features/FeatureMatcherSet.js
-jalview/datamodel/features/FeatureMatcherSetI.js
 jalview/datamodel/features/FeatureStore.js
-jalview/datamodel/features/RangeComparator.js
 jalview/datamodel/features/SequenceFeatures.js
 jalview/datamodel/features/SequenceFeaturesI.js
+jalview/ext/ensembl/EnsemblData.js
+jalview/ext/ensembl/EnsemblGene.js
+jalview/ext/ensembl/EnsemblRestClient.js
+jalview/ext/ensembl/EnsemblSeqProxy.js
+jalview/ext/ensembl/EnsemblSequenceFetcher.js
+jalview/fts/api/FTSData.js
+jalview/fts/api/FTSDataColumnI.js
+jalview/fts/api/FTSRestClientI.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/service/pdb/PDBFTSRestClient.js
+jalview/gui/APQHandlers.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/CalculationChooser.js
+jalview/gui/AnnotationRowFilter.js
 jalview/gui/ColourMenuHelper.js
 jalview/gui/ComboBoxTooltipRenderer.js
 jalview/gui/Desktop.js
 jalview/gui/FeatureRenderer.js
 jalview/gui/IProgressIndicator.js
+jalview/gui/IProgressIndicatorHandler.js
 jalview/gui/IdCanvas.js
 jalview/gui/IdPanel.js
 jalview/gui/IdwidthAdjuster.js
+jalview/gui/JalviewBooleanRadioButtons.js
 jalview/gui/JalviewChangeSupport.js
+jalview/gui/JvOptionPane.js
 jalview/gui/JvSwingUtils.js
+jalview/gui/OptsAndParamsPage.js
+jalview/gui/OptsParametersContainerI.js
 jalview/gui/PaintRefresher.js
 jalview/gui/PopupMenu.js
 jalview/gui/Preferences.js
@@ -108,24 +147,48 @@ jalview/gui/ProgressBar.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/SlivkaPreferences.js
+jalview/gui/StructureChooser.js
+jalview/gui/StructureViewer.js
 jalview/gui/TreeCanvas.js
 jalview/gui/TreePanel.js
 jalview/gui/ViewSelectionMenu.js
+jalview/gui/WebserviceInfo.js
+jalview/gui/WsJobParameters.js
+jalview/gui/WsParamSetManager.js
+jalview/hmmer/HmmerCommand.js
 jalview/io/AlignFile.js
 jalview/io/AlignmentFileReaderI.js
 jalview/io/AlignmentFileWriterI.js
+jalview/io/AnnotationFile.js
 jalview/io/AppletFormatAdapter.js
+jalview/io/BackupFilenameParts.js
+jalview/io/BackupFiles.js
+jalview/io/BackupFilesPresetEntry.js
 jalview/io/DataSourceType.js
+jalview/io/FastaFile.js
+jalview/io/FeaturesFile.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/IntKeyStringValueEntry.js
+jalview/io/JalviewFileChooser.js
+jalview/io/JalviewFileFilter.js
+jalview/io/JalviewFileView.js
+jalview/io/NewickFile.js
 jalview/io/PIRFile.js
 jalview/io/ScoreMatrixFile.js
 jalview/io/SequenceAnnotationReport.js
+jalview/io/StockholmFile.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
@@ -133,16 +196,21 @@ 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/GAlignFrame.js
 jalview/jbgui/GAlignmentPanel.js
 jalview/jbgui/GDesktop.js
 jalview/jbgui/GPreferences.js
+jalview/jbgui/GSliderPanel.js
+jalview/jbgui/GStructureChooser.js
 jalview/jbgui/GTreePanel.js
-jalview/math/Matrix.js
-jalview/math/MatrixI.js
+jalview/jbgui/GWebserviceInfo.js
 jalview/project/Jalview2XML.js
 jalview/renderer/AnnotationRenderer.js
 jalview/renderer/AwtRenderPanelI.js
+jalview/renderer/OverviewResColourFinder.js
 jalview/renderer/ResidueColourFinder.js
 jalview/renderer/ResidueShader.js
 jalview/renderer/ResidueShaderI.js
@@ -156,8 +224,14 @@ jalview/schemes/ColourSchemeProperty.js
 jalview/schemes/ColourSchemes.js
 jalview/schemes/Consensus.js
 jalview/schemes/FeatureColour.js
+jalview/schemes/FeatureSettingsAdapter.js
+jalview/schemes/HMMMatchScoreColourScheme.js
 jalview/schemes/HelixColourScheme.js
+jalview/schemes/HmmerColourScheme.js
+jalview/schemes/HmmerGlobalBackground.js
+jalview/schemes/HmmerLocalBackground.js
 jalview/schemes/HydrophobicColourScheme.js
+jalview/schemes/IdColourScheme.js
 jalview/schemes/JalviewColourScheme.js
 jalview/schemes/NucleotideColourScheme.js
 jalview/schemes/PIDColourScheme.js
@@ -171,6 +245,7 @@ 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
@@ -188,40 +263,113 @@ jalview/urls/UrlProviderImpl.js
 jalview/urls/api/UrlProviderFactoryI.js
 jalview/urls/api/UrlProviderI.js
 jalview/urls/desktop/DesktopUrlProviderFactory.js
+jalview/util/BrowserLauncher.js
 jalview/util/ColorUtils.js
 jalview/util/Comparison.js
 jalview/util/DBRefUtils.js
+jalview/util/FileUtils.js
 jalview/util/Format.js
+jalview/util/LinkedIdentityHashSet.js
+jalview/util/MapList.js
 jalview/util/MessageManager.js
+jalview/util/ParseHtmlBodyAndLinks.js
 jalview/util/Platform.js
 jalview/util/QuickSort.js
 jalview/util/StringUtils.js
 jalview/util/UrlLink.js
+jalview/util/dialogrunner/DialogRunnerI.js
 jalview/util/jarInputStreamProvider.js
-jalview/util/matcher/Condition.js
-jalview/util/matcher/Matcher.js
-jalview/util/matcher/MatcherI.js
 jalview/viewmodel/AlignmentViewport.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/AlignCalcManager2.js
 jalview/workers/AlignCalcWorker.js
+jalview/workers/ComplementConsensusThread.js
 jalview/workers/ConsensusThread.js
 jalview/workers/ConservationThread.js
+jalview/workers/InformationThread.js
+jalview/workers/StrucConsensusThread.js
+jalview/ws/AWSThread.js
+jalview/ws/AWsJob.js
+jalview/ws/JobStateSummary.js
+jalview/ws/SequenceFetcher.js
+jalview/ws/ServiceChangeListener.js
+jalview/ws/WSClient.js
+jalview/ws/WSClientI.js
+jalview/ws/WSDiscovererI.js
+jalview/ws/WSMenuEntryProviderI.js
+jalview/ws/api/CancellableI.js
+jalview/ws/api/JalviewServiceEndpointProviderI.js
+jalview/ws/api/JalviewWebServiceI.js
+jalview/ws/api/JobId.js
+jalview/ws/api/MsaI.js
+jalview/ws/api/MsaResultI.js
+jalview/ws/api/MultipleSequenceAlignmentI.js
+jalview/ws/api/SequenceAnnotationServiceI.js
+jalview/ws/api/ServiceWithParameters.js
+jalview/ws/api/UIinfo.js
+jalview/ws/api/WSAnnotationCalcManagerI.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/gui/AnnotationWsJob.js
+jalview/ws/gui/MsaWSJob.js
+jalview/ws/gui/MsaWSThread.js
+jalview/ws/gui/WsJob.js
+jalview/ws/jws2/Jws2Client.js
+jalview/ws/jws2/Jws2ClientFactory.js
+jalview/ws/jws2/Jws2Discoverer.js
+jalview/ws/jws2/MsaWSClient.js
+jalview/ws/jws2/PreferredServiceChangeListener.js
+jalview/ws/jws2/PreferredServiceRegistry.js
+jalview/ws/jws2/SeqAnnotationServiceCalcWorker.js
+jalview/ws/jws2/SequenceAnnotationWSClient.js
+jalview/ws/jws2/dm/AAConSettings.js
+jalview/ws/params/ArgumentI.js
+jalview/ws/params/AutoCalcSetting.js
+jalview/ws/params/OptionI.js
+jalview/ws/params/ParamDatastoreI.js
+jalview/ws/params/ParamManager.js
+jalview/ws/params/ParameterI.js
+jalview/ws/params/ValueConstrainI.js
+jalview/ws/params/WsParamSetI.js
+jalview/ws/params/simple/BooleanOption.js
+jalview/ws/params/simple/DoubleParameter.js
+jalview/ws/params/simple/IntegerParameter.js
+jalview/ws/params/simple/Option.js
+jalview/ws/params/simple/StringParameter.js
+jalview/ws/seqfetcher/ASequenceFetcher.js
+jalview/ws/seqfetcher/DbSourceProxy.js
+jalview/ws/seqfetcher/DbSourceProxyImpl.js
 jalview/ws/sifts/SiftsSettings.js
+jalview/ws/slivkaws/SlivkaAnnotationServiceInstance.js
+jalview/ws/slivkaws/SlivkaDatastore.js
+jalview/ws/slivkaws/SlivkaMsaServiceInstance.js
+jalview/ws/slivkaws/SlivkaParamSet.js
+jalview/ws/slivkaws/SlivkaWSDiscoverer.js
+jalview/ws/slivkaws/SlivkaWSInstance.js
+jalview/ws/uimodel/AlignAnalysisUIText.js
+jalview/ws/utils/UrlDownloadClient.js
 jalview/xml/binding/jalview/Annotation.js
 jalview/xml/binding/jalview/AnnotationElement.js
 jalview/xml/binding/jalview/Feature.js
-jalview/xml/binding/jalview/FeatureMatcher.js
-jalview/xml/binding/jalview/FeatureMatcherSet.js
-jalview/xml/binding/jalview/FilterBy.js
 jalview/xml/binding/jalview/JalviewModel.js
-jalview/xml/binding/jalview/NoValueColour.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
@@ -239,6 +387,7 @@ 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
@@ -248,6 +397,7 @@ 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
@@ -262,8 +412,12 @@ 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/ImageMediaEntry.js
 java/awt/Insets.js
 java/awt/ItemSelectable.js
 java/awt/JSComponent.js
@@ -276,6 +430,8 @@ java/awt/KeyboardFocusManager.js
 java/awt/LayoutManager.js
 java/awt/LayoutManager2.js
 java/awt/LightweightDispatcher.js
+java/awt/MediaEntry.js
+java/awt/MediaTracker.js
 java/awt/Paint.js
 java/awt/Point.js
 java/awt/Queue.js
@@ -284,6 +440,8 @@ 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
@@ -311,6 +469,7 @@ 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
@@ -325,6 +484,7 @@ 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
@@ -333,6 +493,7 @@ 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
@@ -346,8 +507,11 @@ 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
 java/awt/image/Raster.js
 java/awt/image/RenderedImage.js
 java/awt/image/SampleModel.js
@@ -355,6 +519,7 @@ 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
@@ -366,18 +531,32 @@ 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
+java/io/FileOutputStream.js
 java/io/FileReader.js
 java/io/FileSystem.js
+java/io/FilenameFilter.js
 java/io/FilterInputStream.js
+java/io/FilterOutputStream.js
 java/io/InputStream.js
 java/io/InputStreamReader.js
-java/io/PushbackInputStream.js
+java/io/ObjectStreamField.js
+java/io/OutputStream.js
+java/io/OutputStreamWriter.js
+java/io/PrintStream.js
+java/io/PrintWriter.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
@@ -385,36 +564,35 @@ java/lang/Enum.js
 java/lang/Iterable.js
 java/lang/Readable.js
 java/lang/Runtime.js
+java/lang/RuntimePermission.js
 java/lang/StringBuffer.js
 java/lang/StringBuilder.js
 java/lang/Thread.js
 java/lang/ThreadGroup.js
+java/lang/ThreadLocal.js
+java/lang/ref/Reference.js
+java/lang/ref/ReferenceQueue.js
+java/lang/ref/WeakReference.js
+java/lang/reflect/AccessibleObject.js
+java/lang/reflect/AnnotatedElement.js
 java/lang/reflect/Constructor.js
+java/lang/reflect/Field.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/MalformedURLException.js
+java/net/URI.js
 java/net/URL.js
 java/net/URLConnection.js
-java/net/URLDecoder.js
 java/net/URLStreamHandler.js
 java/net/URLStreamHandlerFactory.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/security/AccessController.js
+java/security/BasicPermission.js
+java/security/Guard.js
+java/security/Permission.js
 java/security/PrivilegedAction.js
 java/security/PrivilegedExceptionAction.js
 java/text/AttributedCharacterIterator.js
@@ -428,7 +606,14 @@ java/text/FieldPosition.js
 java/text/Format.js
 java/text/MessageFormat.js
 java/text/NumberFormat.js
+java/text/ParsePosition.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
@@ -442,58 +627,83 @@ 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/EnumMap.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/ImmutableCollections.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
 java/util/Objects.js
+java/util/PriorityQueue.js
 java/util/Properties.js
 java/util/PropertyResourceBundle.js
 java/util/Queue.js
+java/util/Random.js
 java/util/RandomAccess.js
 java/util/ResourceBundle.js
+java/util/ServiceLoader.js
 java/util/Set.js
 java/util/SortedMap.js
 java/util/SortedSet.js
+java/util/Spliterator.js
 java/util/StringTokenizer.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
+java/util/concurrent/Callable.js
+java/util/concurrent/CompletableFuture.js
+java/util/concurrent/CompletionStage.js
 java/util/concurrent/ConcurrentHashMap.js
 java/util/concurrent/ConcurrentMap.js
+java/util/concurrent/CopyOnWriteArrayList.js
+java/util/concurrent/CopyOnWriteArraySet.js
+java/util/concurrent/DelayQueue.js
+java/util/concurrent/Delayed.js
 java/util/concurrent/Executor.js
 java/util/concurrent/ExecutorService.js
 java/util/concurrent/Executors.js
+java/util/concurrent/ForkJoinPool.js
+java/util/concurrent/ForkJoinTask.js
+java/util/concurrent/Future.js
+java/util/concurrent/FutureTask.js
 java/util/concurrent/LinkedBlockingQueue.js
 java/util/concurrent/RejectedExecutionHandler.js
+java/util/concurrent/RunnableFuture.js
+java/util/concurrent/RunnableScheduledFuture.js
+java/util/concurrent/ScheduledExecutorService.js
+java/util/concurrent/ScheduledFuture.js
+java/util/concurrent/ScheduledThreadPoolExecutor.js
 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/atomic/AtomicLong.js
 java/util/concurrent/locks/AbstractOwnableSynchronizer.js
 java/util/concurrent/locks/AbstractQueuedSynchronizer.js
 java/util/concurrent/locks/Condition.js
@@ -501,45 +711,94 @@ 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/Consumer.js
+java/util/function/Function.js
+java/util/function/Predicate.js
+java/util/function/Supplier.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/stream/AbstractPipeline.js
+java/util/stream/BaseStream.js
+java/util/stream/MatchOps.js
+java/util/stream/PipelineHelper.js
+java/util/stream/ReferencePipeline.js
+java/util/stream/Sink.js
+java/util/stream/Stream.js
+java/util/stream/StreamOpFlag.js
+java/util/stream/StreamShape.js
+java/util/stream/StreamSupport.js
+java/util/stream/TerminalOp.js
 java/util/zip/CRC32.js
 java/util/zip/Inflater.js
 java/util/zip/InflaterInputStream.js
 java/util/zip/ZipConstants.js
 java/util/zip/ZipEntry.js
 java/util/zip/ZipInputStream.js
+javajs/api/BytePoster.js
 javajs/api/GenericLineReader.js
-javajs/api/JSFunction.js
+javajs/api/GenericOutputChannel.js
 javajs/api/JSONEncodable.js
+javajs/async/SwingJSUtils.js
+javajs/http/ClientProtocolException.js
+javajs/http/HttpClient.js
+javajs/http/HttpClientFactory.js
+javajs/http/SimpleHttpClient.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/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
+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/AbstractButton.js
+javax/swing/AbstractCellEditor.js
 javax/swing/AbstractListModel.js
+javax/swing/AbstractSpinnerModel.js
 javax/swing/Action.js
 javax/swing/ActionMap.js
 javax/swing/AncestorNotifier.js
 javax/swing/ArrayTable.js
+javax/swing/Autoscroller.js
 javax/swing/BorderFactory.js
 javax/swing/BoundedRangeModel.js
+javax/swing/Box.js
 javax/swing/BoxLayout.js
 javax/swing/ButtonGroup.js
 javax/swing/ButtonModel.js
+javax/swing/CellEditor.js
+javax/swing/CellRendererPane.js
 javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxEditor.js
 javax/swing/ComboBoxModel.js
 javax/swing/ComponentInputMap.js
 javax/swing/DefaultBoundedRangeModel.js
@@ -547,9 +806,16 @@ 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/InternalFrameFocusTraversalPolicy.js
 javax/swing/JApplet.js
 javax/swing/JButton.js
 javax/swing/JCheckBox.js
@@ -558,13 +824,17 @@ 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
@@ -574,15 +844,23 @@ javax/swing/JRootPane.js
 javax/swing/JScrollBar.js
 javax/swing/JScrollPane.js
 javax/swing/JSeparator.js
+javax/swing/JSlider.js
+javax/swing/JSpinner.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
@@ -591,16 +869,27 @@ javax/swing/Popup.js
 javax/swing/PopupFactory.js
 javax/swing/RepaintManager.js
 javax/swing/RootPaneContainer.js
+javax/swing/RowFilter.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/SortingFocusTraversalPolicy.js
+javax/swing/SpinnerModel.js
+javax/swing/SpinnerNumberModel.js
+javax/swing/Spring.js
+javax/swing/SpringLayout.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
@@ -608,22 +897,44 @@ 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/TitledBorder.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
+javax/swing/event/RowSorterEvent.js
+javax/swing/event/RowSorterListener.js
+javax/swing/event/SwingPropertyChangeSupport.js
+javax/swing/event/TableColumnModelEvent.js
+javax/swing/event/TableColumnModelListener.js
+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
@@ -631,11 +942,59 @@ 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
 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
+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/text/WrappedPlainView.js
+javax/swing/tree/TreeNode.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
 javax/xml/bind/ContextFinder.js
 javax/xml/bind/GetPropertyAction.js
 javax/xml/bind/JAXBContext.js
@@ -649,14 +1008,247 @@ 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/FactoryFinder.js
+javax/xml/datatype/SecuritySupport.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
+net/miginfocom/layout/AC.js
+net/miginfocom/layout/AnimSpec.js
+net/miginfocom/layout/BoundSize.js
+net/miginfocom/layout/CC.js
+net/miginfocom/layout/ComponentWrapper.js
+net/miginfocom/layout/ConstraintParser.js
+net/miginfocom/layout/ContainerWrapper.js
+net/miginfocom/layout/DimConstraint.js
+net/miginfocom/layout/Grid.js
+net/miginfocom/layout/LC.js
+net/miginfocom/layout/LayoutUtil.js
+net/miginfocom/layout/LinkHandler.js
+net/miginfocom/layout/PlatformDefaults.js
+net/miginfocom/layout/ResizeConstraint.js
+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/json/simple/parser/JSONParser.js
-org/json/simple/parser/ParseException.js
-org/json/simple/parser/Yylex.js
+org/jmol/adapter/readers/pdb/PdbReader.js
+org/jmol/adapter/smarter/Atom.js
+org/jmol/adapter/smarter/AtomIterator.js
+org/jmol/adapter/smarter/AtomSetCollection.js
+org/jmol/adapter/smarter/AtomSetCollectionReader.js
+org/jmol/adapter/smarter/AtomSetObject.js
+org/jmol/adapter/smarter/Bond.js
+org/jmol/adapter/smarter/BondIterator.js
+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/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
+org/jmol/api/Interface.js
+org/jmol/api/JmolAdapter.js
+org/jmol/api/JmolAdapterAtomIterator.js
+org/jmol/api/JmolAdapterBondIterator.js
+org/jmol/api/JmolAdapterStructureIterator.js
+org/jmol/api/JmolCallbackListener.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
+org/jmol/api/JmolScriptFunction.js
+org/jmol/api/JmolScriptManager.js
+org/jmol/api/JmolSelectionListener.js
+org/jmol/api/JmolStatusListener.js
+org/jmol/api/JmolViewer.js
+org/jmol/api/PlatformViewer.js
+org/jmol/api/SymmetryInterface.js
+org/jmol/api/Translator.js
+org/jmol/atomdata/AtomDataServer.js
+org/jmol/atomdata/RadiusData.js
+org/jmol/awt/AwtFile.js
+org/jmol/awt/AwtFont.js
+org/jmol/awt/Display.js
+org/jmol/awt/Image.js
+org/jmol/awt/Mouse.js
+org/jmol/awt/Platform.js
+org/jmol/bspt/Bspf.js
+org/jmol/bspt/Bspt.js
+org/jmol/bspt/CubeIterator.js
+org/jmol/bspt/Element.js
+org/jmol/bspt/Leaf.js
+org/jmol/bspt/Node.js
+org/jmol/c/CBK.js
+org/jmol/c/FIL.js
+org/jmol/c/PAL.js
+org/jmol/c/STER.js
+org/jmol/c/STR.js
+org/jmol/c/VDW.js
+org/jmol/g3d/CylinderRenderer.js
+org/jmol/g3d/G3DRenderer.js
+org/jmol/g3d/Graphics3D.js
+org/jmol/g3d/HermiteRenderer.js
+org/jmol/g3d/LineRenderer.js
+org/jmol/g3d/Pixelator.js
+org/jmol/g3d/Platform3D.js
+org/jmol/g3d/PrecisionRenderer.js
+org/jmol/g3d/SphereRenderer.js
+org/jmol/g3d/TextRenderer.js
+org/jmol/g3d/TextString.js
+org/jmol/g3d/TriangleRenderer.js
+org/jmol/i18n/GT.js
+org/jmol/i18n/Language.js
+org/jmol/i18n/Resource.js
+org/jmol/io/FileReader.js
+org/jmol/modelset/Atom.js
+org/jmol/modelset/AtomCollection.js
+org/jmol/modelset/AtomIteratorWithinModel.js
+org/jmol/modelset/Bond.js
+org/jmol/modelset/BondCollection.js
+org/jmol/modelset/BondIterator.js
+org/jmol/modelset/BondIteratorSelected.js
+org/jmol/modelset/Chain.js
+org/jmol/modelset/Group.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
+org/jmol/modelset/Orientation.js
+org/jmol/modelset/Structure.js
+org/jmol/modelset/Text.js
+org/jmol/modelset/TickInfo.js
+org/jmol/modelsetbio/AlphaMonomer.js
+org/jmol/modelsetbio/AlphaPolymer.js
+org/jmol/modelsetbio/AminoMonomer.js
+org/jmol/modelsetbio/AminoPolymer.js
+org/jmol/modelsetbio/BioModel.js
+org/jmol/modelsetbio/BioModelSet.js
+org/jmol/modelsetbio/BioPolymer.js
+org/jmol/modelsetbio/BioResolver.js
+org/jmol/modelsetbio/Helix.js
+org/jmol/modelsetbio/Monomer.js
+org/jmol/modelsetbio/ProteinStructure.js
+org/jmol/modelsetbio/Sheet.js
+org/jmol/render/BallsRenderer.js
+org/jmol/render/BbcageRenderer.js
+org/jmol/render/CageRenderer.js
+org/jmol/render/FontLineShapeRenderer.js
+org/jmol/render/FrankRenderer.js
+org/jmol/render/HoverRenderer.js
+org/jmol/render/LabelsRenderer.js
+org/jmol/render/MeasuresRenderer.js
+org/jmol/render/RepaintManager.js
+org/jmol/render/ShapeRenderer.js
+org/jmol/render/SticksRenderer.js
+org/jmol/render/TextRenderer.js
+org/jmol/render/UccageRenderer.js
+org/jmol/renderbio/BioShapeRenderer.js
+org/jmol/renderbio/CartoonRenderer.js
+org/jmol/renderbio/RocketsRenderer.js
+org/jmol/renderbio/StrandsRenderer.js
+org/jmol/script/ContextToken.js
+org/jmol/script/SV.js
+org/jmol/script/ScriptCompiler.js
+org/jmol/script/ScriptContext.js
+org/jmol/script/ScriptError.js
+org/jmol/script/ScriptEval.js
+org/jmol/script/ScriptExpr.js
+org/jmol/script/ScriptFlowContext.js
+org/jmol/script/ScriptFunction.js
+org/jmol/script/ScriptManager.js
+org/jmol/script/ScriptMathProcessor.js
+org/jmol/script/ScriptParam.js
+org/jmol/script/ScriptTokenParser.js
+org/jmol/script/T.js
+org/jmol/scriptext/CmdExt.js
+org/jmol/scriptext/MathExt.js
+org/jmol/scriptext/ScriptExt.js
+org/jmol/shape/AtomShape.js
+org/jmol/shape/Balls.js
+org/jmol/shape/Bbcage.js
+org/jmol/shape/FontLineShape.js
+org/jmol/shape/Frank.js
+org/jmol/shape/Hover.js
+org/jmol/shape/Labels.js
+org/jmol/shape/Measures.js
+org/jmol/shape/Mesh.js
+org/jmol/shape/Shape.js
+org/jmol/shape/Sticks.js
+org/jmol/shape/TextShape.js
+org/jmol/shape/Uccage.js
+org/jmol/shapebio/BioShape.js
+org/jmol/shapebio/BioShapeCollection.js
+org/jmol/shapebio/Cartoon.js
+org/jmol/shapebio/Rockets.js
+org/jmol/symmetry/Symmetry.js
+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
+org/jmol/util/ColorEncoder.js
+org/jmol/util/CommandHistory.js
+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
+org/jmol/util/Int2IntHashEntry.js
+org/jmol/util/Logger.js
+org/jmol/util/LoggerInterface.js
+org/jmol/util/MeshSurface.js
+org/jmol/util/Node.js
+org/jmol/util/Normix.js
+org/jmol/util/Point3fi.js
+org/jmol/util/Rectangle.js
+org/jmol/util/Rgb16.js
+org/jmol/util/Shader.js
+org/jmol/util/SimpleEdge.js
+org/jmol/util/SimpleNode.js
+org/jmol/util/SimpleUnitCell.js
+org/jmol/util/TempArray.js
+org/jmol/viewer/ActionManager.js
+org/jmol/viewer/AnimationManager.js
+org/jmol/viewer/ColorManager.js
+org/jmol/viewer/FileManager.js
+org/jmol/viewer/Gesture.js
+org/jmol/viewer/GlobalSettings.js
+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
+org/jmol/viewer/StatusManager.js
+org/jmol/viewer/TransformManager.js
+org/jmol/viewer/Viewer.js
+org/jmol/viewer/binding/Binding.js
+org/jmol/viewer/binding/JmolBinding.js
+org/json/JSONArray.js
+org/json/JSONException.js
+org/json/JSONObject.js
+org/json/JSONTokener.js
 org/xml/sax/AttributeList.js
 org/xml/sax/Attributes.js
 org/xml/sax/ContentHandler.js
@@ -664,13 +1256,11 @@ 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
@@ -678,26 +1268,28 @@ sun/awt/PostEventQueue.js
 sun/awt/RequestFocusController.js
 sun/awt/SunGraphicsCallback.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/OffScreenImageSource.js
 sun/awt/image/SunWritableRaster.js
+sun/font/AttributeValues.js
+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/misc/Unsafe.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
@@ -705,18 +1297,39 @@ 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
+sun/util/resources/ParallelListResourceBundle.js
+sun/util/spi/CalendarProvider.js
 swingjs/JSApp.js
-swingjs/JSApplet.js
 swingjs/JSAppletThread.js
 swingjs/JSAppletViewer.js
-swingjs/JSCharSet.js
+swingjs/JSDummyApplet.js
 swingjs/JSFocusPeer.js
 swingjs/JSFontMetrics.js
 swingjs/JSFrameViewer.js
 swingjs/JSGraphics2D.js
+swingjs/JSGraphicsCompositor.js
 swingjs/JSGraphicsConfiguration.js
 swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
 swingjs/JSKeyEvent.js
 swingjs/JSMenuManager.js
 swingjs/JSMouse.js
@@ -724,12 +1337,17 @@ swingjs/JSScreenDevice.js
 swingjs/JSThreadGroup.js
 swingjs/JSToolkit.js
 swingjs/JSUtil.js
+swingjs/a2s/A2SContainer.js
 swingjs/a2s/Dialog.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
 swingjs/jzlib/Checksum.js
@@ -740,7 +1358,12 @@ 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/DefaultMenuLayout.js
 swingjs/plaf/HTML5LookAndFeel.js
 swingjs/plaf/JSAppletUI.js
@@ -748,19 +1371,24 @@ 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/JSEventHandler.js
+swingjs/plaf/JSFormattedTextFieldUI.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
@@ -773,15 +1401,23 @@ swingjs/plaf/JSScrollBarUI.js
 swingjs/plaf/JSScrollPaneUI.js
 swingjs/plaf/JSSeparatorUI.js
 swingjs/plaf/JSSliderUI.js
+swingjs/plaf/JSSpinnerUI.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/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
@@ -789,3 +1425,9 @@ swingjs/xml/JSSAXParser.js
 swingjs/xml/JSXMLGregorianCalendarImpl.js
 swingjs/xml/JSXMLInputFactory.js
 swingjs/xml/JSXMLStreamReader.js
+uk/ac/dundee/compbio/slivkaclient/Job.js
+uk/ac/dundee/compbio/slivkaclient/JobRequest.js
+uk/ac/dundee/compbio/slivkaclient/Parameter.js
+uk/ac/dundee/compbio/slivkaclient/RemoteFile.js
+uk/ac/dundee/compbio/slivkaclient/SlivkaClient.js
+uk/ac/dundee/compbio/slivkaclient/SlivkaService.js
diff --git a/utils/jalviewjs/classlists/jvjmol.txt b/utils/jalviewjs/classlists/jvjmol.txt
new file mode 100644 (file)
index 0000000..bae181d
--- /dev/null
@@ -0,0 +1,232 @@
+jalview/ext/jmol/JalviewJmolBinding.js
+jalview/ext/jmol/JmolCommands.js
+jalview/ext/jmol/JmolParser.js
+jalview/gui/AppJmol.js
+jalview/gui/AppJmolBinding.js
+jalview/gui/StructureViewerBase.js
+jalview/io/StructureFile.js
+jalview/jbgui/GStructureViewer.js
+jalview/renderer/seqfeatures/FeatureColourFinder.js
+jalview/structure/StructureListener.js
+jalview/structure/StructureMapping.js
+jalview/structure/StructureMappingcommandSet.js
+jalview/structures/models/AAStructureBindingModel.js
+jalview/structures/models/SequenceStructureBindingModel.js
+jalview/util/CaseInsensitiveString.js
+jalview/util/HttpUtils.js
+jalview/util/MapList.js
+jalview/ws/dbsources/EbiFileRetrievedProxy.js
+jalview/ws/dbsources/Pdb.js
+jalview/ws/seqfetcher/DbSourceProxy.js
+jalview/ws/seqfetcher/DbSourceProxyImpl.js
+java/awt/font/TextAttribute.js
+java/awt/image/ImageProducer.js
+java/awt/image/PixelGrabber.js
+java/io/BufferedWriter.js
+java/io/FilterOutputStream.js
+java/io/OutputStream.js
+java/io/OutputStreamWriter.js
+java/io/PrintStream.js
+java/io/StringWriter.js
+java/io/Writer.js
+javajs/api/BytePoster.js
+javajs/api/GenericOutputChannel.js
+javajs/util/A4.js
+javajs/util/BS.js
+javajs/util/CU.js
+javajs/util/DF.js
+javajs/util/LimitedLineReader.js
+javajs/util/M3.js
+javajs/util/M34.js
+javajs/util/M4.js
+javajs/util/Measure.js
+javajs/util/OC.js
+javajs/util/P3.js
+javajs/util/P3i.js
+javajs/util/P4.js
+javajs/util/T3.js
+javajs/util/T3i.js
+javajs/util/T4.js
+javajs/util/V3.js
+mc_view/Atom.js
+mc_view/Bond.js
+mc_view/PDBChain.js
+mc_view/Residue.js
+org/jmol/adapter/readers/pdb/PdbReader.js
+org/jmol/adapter/smarter/Atom.js
+org/jmol/adapter/smarter/AtomIterator.js
+org/jmol/adapter/smarter/AtomSetCollection.js
+org/jmol/adapter/smarter/AtomSetCollectionReader.js
+org/jmol/adapter/smarter/AtomSetObject.js
+org/jmol/adapter/smarter/Bond.js
+org/jmol/adapter/smarter/BondIterator.js
+org/jmol/adapter/smarter/Resolver.js
+org/jmol/adapter/smarter/SmarterJmolAdapter.js
+org/jmol/adapter/smarter/Structure.js
+org/jmol/api/AtomIndexIterator.js
+org/jmol/api/EventManager.js
+org/jmol/api/GenericFileInterface.js
+org/jmol/api/GenericMouseInterface.js
+org/jmol/api/GenericPlatform.js
+org/jmol/api/Interface.js
+org/jmol/api/JmolAdapter.js
+org/jmol/api/JmolAdapterAtomIterator.js
+org/jmol/api/JmolAdapterBondIterator.js
+org/jmol/api/JmolCallbackListener.js
+org/jmol/api/JmolGraphicsInterface.js
+org/jmol/api/JmolPropertyManager.js
+org/jmol/api/JmolRendererInterface.js
+org/jmol/api/JmolRepaintManager.js
+org/jmol/api/JmolScriptEvaluator.js
+org/jmol/api/JmolScriptFunction.js
+org/jmol/api/JmolScriptManager.js
+org/jmol/api/JmolSelectionListener.js
+org/jmol/api/JmolStatusListener.js
+org/jmol/api/JmolViewer.js
+org/jmol/api/PlatformViewer.js
+org/jmol/api/Translator.js
+org/jmol/atomdata/AtomDataServer.js
+org/jmol/atomdata/RadiusData.js
+org/jmol/awt/AwtFile.js
+org/jmol/awt/AwtFont.js
+org/jmol/awt/Display.js
+org/jmol/awt/Image.js
+org/jmol/awt/Mouse.js
+org/jmol/awt/Platform.js
+org/jmol/bspt/Bspf.js
+org/jmol/bspt/Bspt.js
+org/jmol/bspt/CubeIterator.js
+org/jmol/bspt/Element.js
+org/jmol/bspt/Leaf.js
+org/jmol/bspt/Node.js
+org/jmol/c/CBK.js
+org/jmol/c/FIL.js
+org/jmol/c/PAL.js
+org/jmol/c/STER.js
+org/jmol/c/STR.js
+org/jmol/c/VDW.js
+org/jmol/g3d/CylinderRenderer.js
+org/jmol/g3d/G3DRenderer.js
+org/jmol/g3d/Graphics3D.js
+org/jmol/g3d/HermiteRenderer.js
+org/jmol/g3d/LineRenderer.js
+org/jmol/g3d/Pixelator.js
+org/jmol/g3d/Platform3D.js
+org/jmol/g3d/PrecisionRenderer.js
+org/jmol/g3d/SphereRenderer.js
+org/jmol/g3d/TextRenderer.js
+org/jmol/g3d/TextString.js
+org/jmol/g3d/TriangleRenderer.js
+org/jmol/i18n/GT.js
+org/jmol/i18n/Language.js
+org/jmol/i18n/Resource.js
+org/jmol/io/FileReader.js
+org/jmol/modelset/Atom.js
+org/jmol/modelset/AtomCollection.js
+org/jmol/modelset/AtomIteratorWithinModel.js
+org/jmol/modelset/Bond.js
+org/jmol/modelset/BondCollection.js
+org/jmol/modelset/BondIterator.js
+org/jmol/modelset/BondIteratorSelected.js
+org/jmol/modelset/Chain.js
+org/jmol/modelset/Group.js
+org/jmol/modelset/Model.js
+org/jmol/modelset/ModelLoader.js
+org/jmol/modelset/ModelSet.js
+org/jmol/modelset/Orientation.js
+org/jmol/modelset/Structure.js
+org/jmol/modelsetbio/AlphaMonomer.js
+org/jmol/modelsetbio/AlphaPolymer.js
+org/jmol/modelsetbio/BioModel.js
+org/jmol/modelsetbio/BioModelSet.js
+org/jmol/modelsetbio/BioPolymer.js
+org/jmol/modelsetbio/BioResolver.js
+org/jmol/modelsetbio/Helix.js
+org/jmol/modelsetbio/Monomer.js
+org/jmol/modelsetbio/ProteinStructure.js
+org/jmol/render/BallsRenderer.js
+org/jmol/render/FontLineShapeRenderer.js
+org/jmol/render/FrankRenderer.js
+org/jmol/render/RepaintManager.js
+org/jmol/render/ShapeRenderer.js
+org/jmol/render/SticksRenderer.js
+org/jmol/renderbio/BackboneRenderer.js
+org/jmol/renderbio/BioShapeRenderer.js
+org/jmol/renderbio/CartoonRenderer.js
+org/jmol/renderbio/RocketsRenderer.js
+org/jmol/renderbio/StrandsRenderer.js
+org/jmol/script/ContextToken.js
+org/jmol/script/SV.js
+org/jmol/script/ScriptCompiler.js
+org/jmol/script/ScriptContext.js
+org/jmol/script/ScriptError.js
+org/jmol/script/ScriptEval.js
+org/jmol/script/ScriptExpr.js
+org/jmol/script/ScriptFlowContext.js
+org/jmol/script/ScriptFunction.js
+org/jmol/script/ScriptManager.js
+org/jmol/script/ScriptMathProcessor.js
+org/jmol/script/ScriptParam.js
+org/jmol/script/ScriptTokenParser.js
+org/jmol/script/T.js
+org/jmol/scriptext/MathExt.js
+org/jmol/shape/AtomShape.js
+org/jmol/shape/Balls.js
+org/jmol/shape/Frank.js
+org/jmol/shape/Mesh.js
+org/jmol/shape/Shape.js
+org/jmol/shape/Sticks.js
+org/jmol/shapebio/Backbone.js
+org/jmol/shapebio/BioShape.js
+org/jmol/shapebio/BioShapeCollection.js
+org/jmol/shapebio/Cartoon.js
+org/jmol/shapebio/Rockets.js
+org/jmol/thread/HoverWatcherThread.js
+org/jmol/thread/JmolThread.js
+org/jmol/util/BSUtil.js
+org/jmol/util/BoxInfo.js
+org/jmol/util/C.js
+org/jmol/util/ColorEncoder.js
+org/jmol/util/CommandHistory.js
+org/jmol/util/DefaultLogger.js
+org/jmol/util/Edge.js
+org/jmol/util/Elements.js
+org/jmol/util/Escape.js
+org/jmol/util/GData.js
+org/jmol/util/Geodesic.js
+org/jmol/util/Int2IntHash.js
+org/jmol/util/Int2IntHashEntry.js
+org/jmol/util/Logger.js
+org/jmol/util/LoggerInterface.js
+org/jmol/util/MeshSurface.js
+org/jmol/util/Node.js
+org/jmol/util/Normix.js
+org/jmol/util/Point3fi.js
+org/jmol/util/Rectangle.js
+org/jmol/util/Rgb16.js
+org/jmol/util/Shader.js
+org/jmol/util/SimpleEdge.js
+org/jmol/util/SimpleNode.js
+org/jmol/util/TempArray.js
+org/jmol/viewer/ActionManager.js
+org/jmol/viewer/AnimationManager.js
+org/jmol/viewer/ColorManager.js
+org/jmol/viewer/FileManager.js
+org/jmol/viewer/Gesture.js
+org/jmol/viewer/GlobalSettings.js
+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
+org/jmol/viewer/StatusManager.js
+org/jmol/viewer/TransformManager.js
+org/jmol/viewer/Viewer.js
+org/jmol/viewer/binding/Binding.js
+org/jmol/viewer/binding/JmolBinding.js
+sun/awt/image/OffScreenImageSource.js
+sun/font/AttributeValues.js
+sun/font/EAttribute.js
\ No newline at end of file
diff --git a/utils/jalviewjs/classlists/stevesoft.txt b/utils/jalviewjs/classlists/stevesoft.txt
new file mode 100644 (file)
index 0000000..e8f1747
--- /dev/null
@@ -0,0 +1,105 @@
+com/stevesoft/pat/Multi.js
+com/stevesoft/pat/NotImplementedError.js
+com/stevesoft/pat/FileRegex.js
+com/stevesoft/pat/Validator.js
+com/stevesoft/pat/UnicodeCurrency.js
+com/stevesoft/pat/End.js
+com/stevesoft/pat/Ctrl.js
+com/stevesoft/pat/PopRule.js
+com/stevesoft/pat/UnicodePunct.js
+com/stevesoft/pat/StringLike.js
+com/stevesoft/pat/StrPos.js
+com/stevesoft/pat/Skip.js
+com/stevesoft/pat/Branch.js
+com/stevesoft/pat/Transformer.js
+com/stevesoft/pat/RegexReader.js
+com/stevesoft/pat/NUnicodeW.js
+com/stevesoft/pat/BackMatch.js
+com/stevesoft/pat/parsePerl.js
+com/stevesoft/pat/Skipped.js
+com/stevesoft/pat/patInf.js
+com/stevesoft/pat/CodeVal.js
+com/stevesoft/pat/oneChar.js
+com/stevesoft/pat/Replacer.js
+com/stevesoft/pat/wrap/CharArrayBufferWrap.js
+com/stevesoft/pat/wrap/CharArrayWrap.js
+com/stevesoft/pat/wrap/WriterWrap.js
+com/stevesoft/pat/wrap/StringWrap.js
+com/stevesoft/pat/wrap/RandomAccessFileWrap.js
+com/stevesoft/pat/wrap/StringBufferWrap.js
+com/stevesoft/pat/FastMulti.js
+com/stevesoft/pat/TransRepRule.js
+com/stevesoft/pat/RBuffer.js
+com/stevesoft/pat/MultiMin.js
+com/stevesoft/pat/lookAhead.js
+com/stevesoft/pat/SpecialRule.js
+com/stevesoft/pat/PushRule.js
+com/stevesoft/pat/FastChar.js
+com/stevesoft/pat/Multi_stage2.js
+com/stevesoft/pat/RegexTokenizer.js
+com/stevesoft/pat/NUnicodeDigit.js
+com/stevesoft/pat/BackG.js
+com/stevesoft/pat/BadRangeArgs.js
+com/stevesoft/pat/NullRule.js
+com/stevesoft/pat/NUnicodePunct.js
+com/stevesoft/pat/UnicodeDigit.js
+com/stevesoft/pat/UniValidator.js
+com/stevesoft/pat/Or.js
+com/stevesoft/pat/Custom.js
+com/stevesoft/pat/UnicodeW.js
+com/stevesoft/pat/DirFileRegex.js
+com/stevesoft/pat/RegHolder.js
+com/stevesoft/pat/RegRes.js
+com/stevesoft/pat/Bits.js
+com/stevesoft/pat/UnicodeMath.js
+com/stevesoft/pat/patInt.js
+com/stevesoft/pat/RegSyntax.js
+com/stevesoft/pat/Backup.js
+com/stevesoft/pat/TransPat.js
+com/stevesoft/pat/NullPattern.js
+com/stevesoft/pat/OrMark.js
+com/stevesoft/pat/NonDirFileRegex.js
+com/stevesoft/pat/ChangeRule.js
+com/stevesoft/pat/NoPattern.js
+com/stevesoft/pat/Boundary.js
+com/stevesoft/pat/RuleHolder.js
+com/stevesoft/pat/RightRule.js
+com/stevesoft/pat/CaseMgr.js
+com/stevesoft/pat/CustomEndpoint.js
+com/stevesoft/pat/NUnicodeMath.js
+com/stevesoft/pat/DotMulti.js
+com/stevesoft/pat/Bracket.js
+com/stevesoft/pat/Start.js
+com/stevesoft/pat/Any.js
+com/stevesoft/pat/UnicodeLower.js
+com/stevesoft/pat/PatternSub.js
+com/stevesoft/pat/WantMoreTextReplaceRule.js
+com/stevesoft/pat/Pthings.js
+com/stevesoft/pat/Regex.js
+com/stevesoft/pat/Group.js
+com/stevesoft/pat/UnicodeUpper.js
+com/stevesoft/pat/Skip2.js
+com/stevesoft/pat/RegexWriter.js
+com/stevesoft/pat/UnicodeWhite.js
+com/stevesoft/pat/Pattern.js
+com/stevesoft/pat/LeftRule.js
+com/stevesoft/pat/Range.js
+com/stevesoft/pat/RegOpt.js
+com/stevesoft/pat/PartialBuffer.js
+com/stevesoft/pat/UnicodeAlpha.js
+com/stevesoft/pat/Rthings.js
+com/stevesoft/pat/Prop.js
+com/stevesoft/pat/NUnicodeCurrency.js
+com/stevesoft/pat/BackRefRule.js
+com/stevesoft/pat/SubMark.js
+com/stevesoft/pat/NUnicodeAlpha.js
+com/stevesoft/pat/FastBracket.js
+com/stevesoft/pat/NUnicodeWhite.js
+com/stevesoft/pat/StringBufferLike.js
+com/stevesoft/pat/RegSyntaxError.js
+com/stevesoft/pat/BasicStringBufferLike.js
+com/stevesoft/pat/AmpersandRule.js
+com/stevesoft/pat/StringRule.js
+com/stevesoft/pat/ReplaceRule.js
+com/stevesoft/pat/CodeRule.js
+com/stevesoft/pat/SkipBMH.js
index 7d802f2..1ac36de 100644 (file)
@@ -21,6 +21,8 @@ Info = {
 </head>
 <body>
 <script>
+J2S.addDirectDatabaseCall('www.compbio.dundee.ac.uk'),J2S.addDirectDatabaseCall('www.jalview.org'), J2S.addDirectDatabaseCall('ensembl.org')
+J2S.addDirectDatabaseCall('127.0.0.1'), J2S.addDirectDatabaseCall('localhost') 
 SwingJS.getApplet('testApplet', Info)
 getClassList = function(){J2S._saveFile('_j2sclasslist.txt', Clazz.ClassFilesLoaded.sort().join('\n'))}
 </script>
index 65732a4..26d7abe 100644 (file)
Binary files a/utils/jalviewjs/libjs/jmol-app.zip and b/utils/jalviewjs/libjs/jmol-app.zip differ
diff --git a/utils/jalviewjs/libjs/slivka-client-site.zip b/utils/jalviewjs/libjs/slivka-client-site.zip
new file mode 100644 (file)
index 0000000..2e64029
Binary files /dev/null and b/utils/jalviewjs/libjs/slivka-client-site.zip differ
diff --git a/utils/jalviewjs/site-resources/___j2sflags.htm b/utils/jalviewjs/site-resources/___j2sflags.htm
new file mode 100644 (file)
index 0000000..62a786d
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head><title>URL command line flags for SwingJS</title></head>
+<body><h3>URL command-line arguments for SwingJS</h3>
+<br>To enable these flags, simply add them after # or ? on your page.
+<br>The test is very simple -- just a case-sensitive string check. 
+<br>Separate them with &amp; For example:
+<br><a href="test_Test_Class.html?j2sverbose&j2snozcore&j2strace=applet">test_Test_Class.html?j2sverbose&j2snozcore&j2strace=applet</a>
+<br><br><table width=800 border=1 cellpadding=5>
+<tr><th colspan="2"></th></tr>
+<tr><td>j2sargs=a|b|c</td><td>arguments to be passed on to application.main(); use "|" to separate arguments. Overrides Info.args, which should be an array of strings, if present</td></tr>
+<tr><td>j2sdebugclip</td><td>shows all show/restore and clip operations in JSGraphics2D</td></tr>
+<tr><td>j2sdebugcode</td><td>deprecated; see j2snocore</td></tr>
+<tr><td>j2sdebugcore</td><td>deprecated; see j2snozcore</td></tr>
+<tr><td>j2sdebugpaint</td><td>report repaint manager information</td></tr>
+<tr><td>j2sevents</td><td>report ComponentEvent instances</td></tr>
+<tr><td>j2sfilter=xxx</td><td>remove messages with the specified text from System.out</td></tr>
+<tr><td>j2sheadless</td><td>run headlessly (must be a main()-based application or library without Swing or AWT)</td></tr>
+<tr><td>j2slang=en_US</td><td>default language for java.util.Locale (overrides Info.language)</td></tr>
+<tr><td>j2smouse</td><td>report mouse events other than mousemove</td></tr>
+<tr><td>j2smousemove</td><td>report all mouse events, including mousemove</td></tr>
+<tr><td>j2snocore</td><td>do not load core files (from j2s/core/)</td></tr>
+<tr><td>j2snoeval</td><td>use new Function() instead of eval(); breaks debugging, experimental</td></tr>
+<tr><td>j2snooutput</td><td>report only System.err message, not  System.out </td></tr>
+<tr><td>j2snozcore</td><td>use the uncompressed j2s/core/xxxcore.js files, not the compressed core.z.js files</td></tr>
+<tr><td>j2sprofile</td><td>track object creation; use J2S.getProfile() when you want a report; J2S.getProfile() or J2S.getProfile(nsec) to restart profiling anytime.</td></tr>
+<tr><td>j2sstrict</td><td>strict mode -- experimental</td></tr>
+<tr><td>j2strace=xxx or j2strace="xxx"</td><td>throw up an alert in the browser and a debugger statement in the developer whenever the specified text is found in System.out or System.err; if quotes are used, this must be an exact match to the entire output text (particularly useful when the message is something like "0", which otherwise would be next to impossible to find.</td></tr>
+<tr><td>j2sverbose</td><td>report all files loaded using AJAX</td></tr>
+</table>
+</body>
+</html>
@@ -5,6 +5,8 @@
 <script src="swingjs/swingjs2.js"></script>
 <script>
 
+// NOTE: The applet on this page is "Jalview". 
+
 // BH 2019.10.06 adds Tree and Pca functionality
 // BH see issue JAL-3451
 
@@ -24,12 +26,13 @@ Info = {
 jvGet = function(what) {
        switch(what) {
        case "tree":
-               testApplet.app.openTreePanel$jalview_gui_AlignFrame$S$S(null, "NJ","BLOSUM62")
+               Jalview.app.openTreePanel("NJ","BLOSUM62")
                break;
        case "pca":
-               testApplet.app.openPcaPanel$jalview_gui_AlignFrame$S(null, "BLOSUM62")
+               Jalview.app.openPcaPanel("BLOSUM62")
                break;
        case "3D":
+               Jalview.app.showStructure("1a70", "mmcif");
                break;
        }
        
@@ -37,7 +40,7 @@ jvGet = function(what) {
 
 $(document).ready(function() {
 
-  SwingJS.getApplet('testApplet', Info);
+  SwingJS.getApplet('Jalview', Info);
 
 });
 
@@ -60,7 +63,7 @@ The basic idea is that we have something interesting to say &mdash; some sort of
 across to our visitors with more than just text and images. The idea is to have a <b>dynamic</b> page that will involve <b>user interaction</b>. 
 <br><br>
 We start with a Jalview desktop. You can't see it, because I have placed it in a <code>div</code> tag with style <i>width:0px;height:0px</i> just after the period that ends this sentence.
-<div id="jalview-desktop-div" style="width:0px;height:0px;"></div>
+<div id="Jalview-desktop-div" style="width:0px;height:0px;"></div>
 <br>
 The idea is NOT to teach visitors how to use Jalview. The idea is to seamlessly integrate components of Jalview that can be used to enrich a discussion. 
 Like JSmol in <a target="_blank" href="http://proteopedia.org/wiki/index.php/Main_Page"><img src=https://pbs.twimg.com/profile_images/818051034/proteopedia_135x200_small_logo_for_Twitter_400x400.png width=16 height=16/>Proteopedia</a>.
@@ -71,7 +74,7 @@ See the big block of red color? That's the <i>Ferredoxin fold</i>domain.
 
 </div>
 </td><td style="background-color:lightgray;padding:20px">
-<div id="jalview-alignment-div" style="padding:20px;position:relative;top:0px;left:0px;width:680px;height:400px">
+<div id="Jalview-alignment-div" style="padding:20px;position:relative;top:0px;left:0px;width:680px;height:400px">
 <br><br>
 The alignment frame will appear here momentarily. When it does, you can go ahead and manipulate the alignment with your mouse.
 </div>
@@ -85,24 +88,27 @@ The alignment frame will appear here momentarily. When it does, you can go ahead
 <table style="background-color:lightgray"><tr><td  style="padding:0px 0px 0px 20px">
 <b>Select a few alignments</b> by left-dragging across a few rows of the alignment to make a selection box. 
 Then click one of the buttons below to see more information about your selected subset of the alignment.
-<ul>
-<li><button onclick='jvGet("tree")'>similarity tree</button></li>
-<li><button onclick='jvGet("pca")'>principal component analysis</button></li>
-</ul>
-
+<br><br>
+<button onclick='jvGet("tree")'>similarity tree</button> <button onclick='jvGet("pca")'>principal component analysis</button>
+<br><br>
+One more thing. Let's take a <a href="javascript:jvGet('3D')" style="text-decoration:none;color:red">look at the 3D structure</a> of one these proteins. Ferredoxins are important, because they have 
+iron-sulfur clusters that can accept and deliver electrons in metabolic processes. Let's see if we can find it. 
+<br><br>
 
 
 
 </td><td style="padding:0px 0px 0px 0px">
 <table style="border-spacing:0"><tr><td style="background-color:lightgray;padding:10px 0px 10px 20px">
-<div id="jalview-tree-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
+<div id="Jalview-tree-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
 <br><br><br><br>
-jalview-tree-div
+<a href="javascript:jvGet('tree')">Jalview-tree-div</a>
 </div>
 </td><td style="background-color:lightgray;padding:10px 20px 10px 0px">
-<div id="jalview-pca-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
+<div id="Jalview-pca-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
 <br><br><br><br>
-jalview-pca-div
+<a href="javascript:jvGet('pca')">Jalview-pca-div</a>
+<br><br>
+
 </div>
 </td></tr></table>
 
@@ -111,19 +117,23 @@ jalview-pca-div
 
 </td></tr>
 
+<!-- let it float
 <tr>
 
 <td valign=top style="padding:20px;height:650px" >
 
-<div id="jalview-structureviewer-div" style="position:relative;top:0px;left:0px;width:600px;height:600px">
-jalview-strucddtureviewer-div
-</div>
+<div id="Jalview-structureviewer-div" style="position:relative;top:0px;left:0px;width:600px;height:600px">
+<a href="javascript:jvGet('3D')">Jalview-structureviewer-div</a>
+</div> 
 </td>
 <td valign=top style="padding:20px;background-color:white" >
 One more thing. Let's take a look at the 3D structure of one these proteins. Ferredoxins are important, because they have 
 iron-sulfur clusters that can accept and deliver electrons in metabolic processes. Let's see if we can find it. 
 <br><center><button onclick='jvGet("3D")'>add the 3D structure</button>
-</td></tr></table>
+</td></tr>
+ -->
+
+</table>
 
 
 <!-- debugging (hidden) -->
diff --git a/utils/jalviewjs/site-resources/_jalview_embedded_example2.html b/utils/jalviewjs/site-resources/_jalview_embedded_example2.html
new file mode 100644 (file)
index 0000000..89a4f71
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded JalviewJS Example 2</title><meta charset="utf-8" />
+<script src="swingjs/swingjs2.js"></script>
+<script>
+
+// NOTE: The applet on this page is "Jalview". 
+
+// BH 2019.10.06 adds Tree and Pca functionality
+// BH see issue JAL-3451
+
+if (!self.SwingJS)alert('swingjs2.js was not found. It needs to be in swingjs folder in the same directory as ' + document.location.href)
+Info = {
+  code: null,
+  main: "jalview.bin.JalviewJS2",
+  core: "NONE",
+//     core:"_jalview",
+
+       oninit:function() {$("#links").show();},
+       noannotation: true,
+       
+  readyFunction: null,
+       serverURL: 'https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php',
+       j2sPath: 'swingjs/j2s',
+       console:'sysoutdiv',
+       allowjavascript: true
+}
+
+jvGet = function(what) {
+       switch(what) {
+       case "select1":
+               alert(Jalview1.getApp().getSelectedSequences());
+               break;
+       case "select2":
+               alert(Jalview2.getApp().getSelectedSequences());        
+               break;
+       case "select1fasta":
+               alert(Jalview1.getApp().getSelectedSequencesAsAlignment("fasta",true));
+               break;
+       case "select2fasta":
+               alert(Jalview2.getApp().getSelectedSequencesAsAlignment("fasta",true));
+               break;
+       }
+       
+}
+
+$(document).ready(function() {
+
+         SwingJS.getApplet('Jalview1', Info);
+         SwingJS.getApplet('Jalview2', Info);
+
+});
+
+</script>
+</head>
+<body style="background-image: url(images/coolVeryLightBG.png);">
+<table style="width:1000px;border:2px solid lightblue;border-spacing:0;font-size:16pt;" padding="10" valign="top">
+<tr>
+<td style="font-size:24;font-weight:bold;background-color:lightblue" colspan=2><center>Demonstration of embedded JalviewJS components</center>
+</td>
+
+
+</tr><tr>
+
+
+<td colspan=2 valign=top style="padding:20px;background-color:lightgray">
+this page tests two Jalview apps on the same page.
+<div id="Jalview1-desktop-div" style="width:0px;height:0px;"></div>
+<div id="Jalview2-desktop-div" style="width:0px;height:0px;"></div>
+</td></tr>
+<tr><td style="background-color:lightgray;padding:10px">
+<div id="Jalview1-alignment-div" style="padding:10px;position:relative;width:550px;height:300px">
+</td>
+<td style="background-color:lightgray;padding:10px">
+<div id="Jalview2-alignment-div" style="padding:10px;position:relative;width:550px;height:300px">
+</td>
+</tr>
+<tr><td>
+<div style="display:block;width:500px;height:300px;">
+<div id="sysoutdiv" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
+This is System.out. <a href="javascript:J2S.thisApplet._clearConsole()">clear it</a> <br>Add ?j2snocore to URL to see full class list; ?j2sdebug to use uncompressed j2s/core files <br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+</div>
+
+</td><td valign=top>
+<div id=links style="display:none">
+<a href="javascript:jvGet('select1')">Show Jalview1 selections</a>
+
+<a href="javascript:jvGet('select2')">Show Jalview2 selections</a>
+
+<br>
+<a href="javascript:jvGet('select1fasta')">Show Jalview1 selections (fasta)</a>
+
+<a href="javascript:jvGet('select2fasta')">Show Jalview2 selections (fasta)</a>
+</div>
+</td></tr>
+</table>
+
+
+</body>
+</html>
index 7c20d44..968e1eb 100644 (file)
@@ -12,21 +12,24 @@ Info = {
        width: 850,
        height: 550,
   readyFunction: null,
-       serverURL: 'https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php',
+       serverURL: '',
        j2sPath: 'swingjs/j2s',
        console:'sysoutdiv',
        allowjavascript: true
 }
+// direct access to these sites
 </script>
 </head>
 <body>
 <script>
+J2S.addDirectDatabaseCall('www.compbio.dundee.ac.uk'),J2S.addDirectDatabaseCall('www.jalview.org'), J2S.addDirectDatabaseCall('ensembl.org')
+J2S.addDirectDatabaseCall('127.0.0.1'), J2S.addDirectDatabaseCall('localhost')
 SwingJS.getApplet('testApplet', Info)
 getClassList = function(){J2S._saveFile('_j2sclasslist.txt', Clazz.ClassFilesLoaded.sort().join('\n'))}
 </script>
 <div style="position:absolute;left:900px;top:30px;width:600px;height:300px;">
 <div id="sysoutdiv" contentEditable="true" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
-This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a> <br>Add ?j2snocore to URL to see full class list; ?j2sdebug to use uncompressed j2s/core files <br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a>  <a href='javascript:J2S.getProfile()'>start/stop profiling</a><br>see <a href=___j2sflags.htm>___j2sflags.htm</a> for SwingJS URL command-line options<br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
 </div>
 </body>
 </html>
diff --git a/utils/testnglibs/testng-7.4.0-sources.jar b/utils/testnglibs/testng-7.4.0-sources.jar
new file mode 100644 (file)
index 0000000..94f813f
Binary files /dev/null and b/utils/testnglibs/testng-7.4.0-sources.jar differ
diff --git a/utils/testnglibs/testng-7.4.0.jar b/utils/testnglibs/testng-7.4.0.jar
new file mode 100644 (file)
index 0000000..6953973
Binary files /dev/null and b/utils/testnglibs/testng-7.4.0.jar differ
diff --git a/utils/testnglibs/testng-sources.jar b/utils/testnglibs/testng-sources.jar
deleted file mode 100644 (file)
index 7a80303..0000000
Binary files a/utils/testnglibs/testng-sources.jar and /dev/null differ
diff --git a/utils/testnglibs/testng.jar b/utils/testnglibs/testng.jar
deleted file mode 100644 (file)
index e1f127e..0000000
Binary files a/utils/testnglibs/testng.jar and /dev/null differ