From: Jim Procter Date: Thu, 17 Sep 2020 17:47:10 +0000 (+0100) Subject: Merge branch 'releases/Release_2_11_1_Branch' X-Git-Tag: Release_2_11_2_2~1^2~5 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=c6874080bf8a7ddf943200597e23a344c946a808;hp=2b6a74893883a246d40a99a2e981ef6cad4fed5a;p=jalview.git Merge branch 'releases/Release_2_11_1_Branch' --- diff --git a/RELEASE b/RELEASE index 5a89907..3600be2 100644 --- a/RELEASE +++ b/RELEASE @@ -1,2 +1,2 @@ jalview.release=releases/Release_2_11_1_Branch -jalview.version=2.11.1.0 +jalview.version=2.11.1.1 diff --git a/build.gradle b/build.gradle index f7233f1..68d0c04 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,6 @@ +/* Convention for properties. Read from gradle.properties, use lower_case_underlines for property names. + * For properties set within build.gradle, use camelCaseNoSpace. + */ import org.apache.tools.ant.filters.ReplaceTokens import org.gradle.internal.os.OperatingSystem import org.gradle.plugins.ide.eclipse.model.Output @@ -13,9 +16,6 @@ buildscript { mavenCentral() mavenLocal() } - dependencies { - classpath 'org.openclover:clover:4.4.1' - } } @@ -130,21 +130,25 @@ ext { sourceDir = string("${jalviewDir}/${bareSourceDir}") resourceDir = string("${jalviewDir}/${resource_dir}") bareTestSourceDir = string(test_source_dir) - testSourceDir = string("${jalviewDir}/${bareTestSourceDir}") + testDir = string("${jalviewDir}/${bareTestSourceDir}") - // clover - cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}") - cloverDb = string("${buildDir}/clover/clover.db") classesDir = string("${jalviewDir}/${classes_dir}") - if (clover.equals("true")) { - use_clover = true - classesDir = string("${buildDir}/${cloverClassesDir}") - } else { - use_clover = false - classesDir = string("${jalviewDir}/${classes_dir}") - } - classes = classesDir + // clover + useClover = clover.equals("true") + cloverBuildDir = "${buildDir}/clover" + cloverInstrDir = file("${cloverBuildDir}/clover-instr") + cloverClassesDir = file("${cloverBuildDir}/clover-classes") + cloverReportDir = file("${buildDir}/reports/clover") + cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr") + cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes") + //cloverTestClassesDir = cloverClassesDir + cloverDb = string("${cloverBuildDir}/clover.db") + + resourceClassesDir = useClover ? cloverClassesDir : classesDir + + testSourceDir = useClover ? cloverTestInstrDir : testDir + testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}" getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}") buildDist = true @@ -173,8 +177,8 @@ ext { getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}") jvlChannelName += "_${getdownChannelName}" // automatically add the test group Not-bamboo for exclusion - if ("".equals(testngExcludedGroups)) { - testngExcludedGroups = "Not-bamboo" + if ("".equals(testng_excluded_groups)) { + testng_excluded_groups = "Not-bamboo" } install4jExtraScheme = "jalviewb" break @@ -192,10 +196,10 @@ ext { getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}" getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = string("${getdown_channel_base}/${getdownDir}") - if (!file("${ARCHIVEDIR}/${packageDir}").exists()) { + if (!file("${ARCHIVEDIR}/${package_dir}").exists()) { throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution") } else { - packageDir = string("${ARCHIVEDIR}/${packageDir}") + package_dir = string("${ARCHIVEDIR}/${package_dir}") buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}") buildDist = false } @@ -207,10 +211,10 @@ ext { getdownChannelName = string("archive/${JALVIEW_VERSION}") getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = file(getdownWebsiteDir).toURI().toString() - if (!file("${ARCHIVEDIR}/${packageDir}").exists()) { + if (!file("${ARCHIVEDIR}/${package_dir}").exists()) { throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution") } else { - packageDir = string("${ARCHIVEDIR}/${packageDir}") + package_dir = string("${ARCHIVEDIR}/${package_dir}") buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}") buildDist = false } @@ -340,6 +344,7 @@ ext { //libDir = j8libDir libDir = j11libDir libDistDir = j8libDir + digestonlyDir = j8digestonlyDir compile_source_compatibility = 1.8 compile_target_compatibility = 1.8 // these are getdown.txt properties defined dependent on the JAVA_VERSION @@ -353,6 +358,7 @@ ext { JAVA_INTEGER_VERSION = string("11") libDir = j11libDir libDistDir = j11libDir + digestonlyDir = j11digestonlyDir compile_source_compatibility = 11 compile_target_compatibility = 11 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version")) @@ -409,8 +415,8 @@ ext { - buildingHTML = string("${jalviewDir}/${docDir}/building.html") - helpFile = string("${classesDir}/${help_dir}/help.jhm") + buildingHTML = string("${jalviewDir}/${doc_dir}/building.html") + helpFile = string("${resourceClassesDir}/${help_dir}/help.jhm") helpParentDir = string("${jalviewDir}/${help_parent_dir}") helpSourceDir = string("${helpParentDir}/${help_dir}") @@ -453,10 +459,9 @@ sourceSets { srcDirs += helpParentDir } - jar.destinationDir = file("${jalviewDir}/${packageDir}") + jar.destinationDir = file("${jalviewDir}/${package_dir}") compileClasspath = files(sourceSets.main.java.outputDir) - //compileClasspath += files(sourceSets.main.resources.srcDirs) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) runtimeClasspath = compileClasspath @@ -464,18 +469,19 @@ sourceSets { clover { java { - srcDirs = [ cloverInstrDir ] - outputDir = file("${buildDir}/${cloverClassesDir}") + srcDirs cloverInstrDir + outputDir = cloverClassesDir } resources { srcDirs = sourceSets.main.resources.srcDirs } - compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir ) - compileClasspath += files(sourceSets.main.java.outputDir) - compileClasspath += sourceSets.main.compileClasspath - compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"]) + + compileClasspath = files( sourceSets.clover.java.outputDir ) + //compileClasspath += files( testClassesDir ) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) + compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) + compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) runtimeClasspath = compileClasspath } @@ -483,41 +489,20 @@ sourceSets { test { java { srcDirs testSourceDir - outputDir = file("${jalviewDir}/${testOutputDir}") + outputDir = file(testClassesDir) } resources { - srcDirs = sourceSets.main.resources.srcDirs + srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs } compileClasspath = files( sourceSets.test.java.outputDir ) - - if (use_clover) { - compileClasspath = sourceSets.clover.compileClasspath - } else { - compileClasspath += files(sourceSets.main.java.outputDir) - } - - compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) - compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"]) - compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"]) + compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath + compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) runtimeClasspath = compileClasspath } -} - - -// clover bits -dependencies { - if (use_clover) { - cloverCompile 'org.openclover:clover:4.4.1' - testCompile 'org.openclover:clover:4.4.1' - } -} -configurations { - cloverRuntime - cloverRuntime.extendsFrom cloverCompile } @@ -656,61 +641,209 @@ eclipse { } -task cloverInstr { - // only instrument source, we build test classes as normal - inputs.files files (sourceSets.main.allJava,sourceSets.test.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"])) - outputs.dir cloverInstrDir +// clover bits + +task cleanClover { doFirst { - delete cloverInstrDir - def argsList = [ - "--initstring", - cloverDb, - "-d", - cloverInstrDir.getPath(), - ] - argsList.addAll( - inputs.files.files.collect( - { file -> file.absolutePath } - ) + delete cloverBuildDir + delete cloverReportDir + } +} + + +task cloverInstrJava(type: JavaExec) { + group = "Verification" + description = "Create clover instrumented source java files" + + dependsOn cleanClover + + inputs.files(sourceSets.main.allJava) + outputs.dir(cloverInstrDir) + + //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) + classpath = sourceSets.clover.compileClasspath + main = "com.atlassian.clover.CloverInstr" + + def argsList = [ + "--encoding", + "UTF-8", + "--initstring", + cloverDb, + "--destdir", + cloverInstrDir.getPath(), + ] + def srcFiles = sourceSets.main.allJava.files + argsList.addAll( + srcFiles.collect( + { file -> file.absolutePath } ) - String[] args = argsList.toArray() - println("About to instrument "+args.length +" files") - com.atlassian.clover.CloverInstr.mainImpl(args) + ) + args argsList.toArray() + + doFirst { + delete cloverInstrDir + println("Clover: About to instrument "+srcFiles.size() +" files") + } +} + + +task cloverInstrTests(type: JavaExec) { + group = "Verification" + description = "Create clover instrumented source test files" + + dependsOn cleanClover + + inputs.files(testDir) + outputs.dir(cloverTestInstrDir) + + classpath = sourceSets.clover.compileClasspath + main = "com.atlassian.clover.CloverInstr" + + def argsList = [ + "--encoding", + "UTF-8", + "--initstring", + cloverDb, + "--srcdir", + testDir, + "--destdir", + cloverTestInstrDir.getPath(), + ] + args argsList.toArray() + + doFirst { + delete cloverTestInstrDir + println("Clover: About to instrument test files") } } +task cloverInstr { + group = "Verification" + description = "Create clover instrumented all source files" + + dependsOn cloverInstrJava + dependsOn cloverInstrTests +} + + cloverClasses.dependsOn cloverInstr -task cloverReport { +task cloverConsoleReport(type: JavaExec) { group = "Verification" - description = "Creates the Clover report" - inputs.dir "${buildDir}/clover" - outputs.dir "${reportsDir}/clover" + description = "Creates clover console report" + onlyIf { file(cloverDb).exists() } - doFirst { - def argsList = [ - "--initstring", - cloverDb, - "-o", - "${reportsDir}/clover" - ] - String[] args = argsList.toArray() - com.atlassian.clover.reporters.html.HtmlReporter.runReport(args) - - // and generate ${reportsDir}/clover/clover.xml - args = [ - "--initstring", - cloverDb, - "-o", - "${reportsDir}/clover/clover.xml" - ].toArray() - com.atlassian.clover.reporters.xml.XMLReporter.runReport(args) + + inputs.dir cloverClassesDir + + classpath = sourceSets.clover.runtimeClasspath + main = "com.atlassian.clover.reporters.console.ConsoleReporter" + + if (cloverreport_mem.length() > 0) { + maxHeapSize = cloverreport_mem + } + if (cloverreport_jvmargs.length() > 0) { + jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) + } + + def argsList = [ + "--alwaysreport", + "--initstring", + cloverDb, + "--unittests" + ] + + args argsList.toArray() +} + + +task cloverHtmlReport(type: JavaExec) { + group = "Verification" + description = "Creates clover HTML report" + + onlyIf { + file(cloverDb).exists() + } + + def cloverHtmlDir = cloverReportDir + inputs.dir cloverClassesDir + outputs.dir cloverHtmlDir + + classpath = sourceSets.clover.runtimeClasspath + main = "com.atlassian.clover.reporters.html.HtmlReporter" + + if (cloverreport_mem.length() > 0) { + maxHeapSize = cloverreport_mem + } + if (cloverreport_jvmargs.length() > 0) { + jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) + } + + def argsList = [ + "--alwaysreport", + "--initstring", + cloverDb, + "--outputdir", + cloverHtmlDir + ] + + if (cloverreport_html_options.length() > 0) { + argsList += cloverreport_html_options.split(" ") } + + args argsList.toArray() +} + + +task cloverXmlReport(type: JavaExec) { + group = "Verification" + description = "Creates clover XML report" + + onlyIf { + file(cloverDb).exists() + } + + def cloverXmlFile = "${cloverReportDir}/clover.xml" + inputs.dir cloverClassesDir + outputs.file cloverXmlFile + + classpath = sourceSets.clover.runtimeClasspath + main = "com.atlassian.clover.reporters.xml.XMLReporter" + + if (cloverreport_mem.length() > 0) { + maxHeapSize = cloverreport_mem + } + if (cloverreport_jvmargs.length() > 0) { + jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) + } + + def argsList = [ + "--alwaysreport", + "--initstring", + cloverDb, + "--outfile", + cloverXmlFile + ] + + if (cloverreport_xml_options.length() > 0) { + argsList += cloverreport_xml_options.split(" ") + } + + args argsList.toArray() +} + + +task cloverReport { + group = "Verification" + description = "Creates clover reports" + + dependsOn cloverXmlReport + dependsOn cloverHtmlReport } @@ -722,15 +855,7 @@ compileCloverJava { options.compilerArgs += additional_compiler_args print ("Setting target compatibility to "+targetCompatibility+"\n") } - classpath += configurations.cloverRuntime -} - - -task cleanClover { - doFirst { - delete cloverInstrDir - delete cloverDb - } + //classpath += configurations.cloverRuntime } // end clover bits @@ -748,12 +873,6 @@ compileJava { compileTestJava { - if (use_clover) { - dependsOn compileCloverJava - classpath += configurations.cloverRuntime - } else { - classpath += sourceSets.main.runtimeClasspath - } doFirst { sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility @@ -832,8 +951,8 @@ task cleanBuildingHTML(type: Delete) { task convertBuildingMD(type: Exec) { dependsOn cleanBuildingHTML - def buildingMD = "${jalviewDir}/${docDir}/building.md" - def css = "${jalviewDir}/${docDir}/github.css" + def buildingMD = "${jalviewDir}/${doc_dir}/building.md" + def css = "${jalviewDir}/${doc_dir}/github.css" def pandoc = null pandoc_exec.split(",").each { @@ -875,8 +994,8 @@ clean { task syncDocs(type: Sync) { dependsOn convertBuildingMD - def syncDir = "${classesDir}/${docDir}" - from fileTree("${jalviewDir}/${docDir}") + def syncDir = "${resourceClassesDir}/${doc_dir}" + from fileTree("${jalviewDir}/${doc_dir}") into syncDir } @@ -884,7 +1003,7 @@ task syncDocs(type: Sync) { task copyHelp(type: Copy) { def inputDir = helpSourceDir - def outputDir = "${classesDir}/${help_dir}" + def outputDir = "${resourceClassesDir}/${help_dir}" from(inputDir) { exclude '**/*.gif' exclude '**/*.jpg' @@ -912,7 +1031,7 @@ task copyHelp(type: Copy) { task syncLib(type: Sync) { - def syncDir = "${classesDir}/${libDistDir}" + def syncDir = "${resourceClassesDir}/${libDistDir}" from fileTree("${jalviewDir}/${libDistDir}") into syncDir } @@ -921,7 +1040,7 @@ task syncLib(type: Sync) { task syncResources(type: Sync) { from resourceDir include "**/*.*" - into "${classesDir}" + into "${resourceClassesDir}" preserve { include "**" } @@ -938,18 +1057,17 @@ task prepare { //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir test { dependsOn prepare - dependsOn compileJava - if (use_clover) { - dependsOn cloverInstr - } + //dependsOn compileJava ////? DELETE - if (use_clover) { - print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n") + if (useClover) { + dependsOn cloverClasses + } else { //? + dependsOn compileJava //? } useTestNG() { - includeGroups testngGroups - excludeGroups testngExcludedGroups + includeGroups testng_groups + excludeGroups testng_excluded_groups preserveOrder true useDefaultListeners=true } @@ -958,10 +1076,25 @@ test { workingDir = jalviewDir //systemProperties 'clover.jar' System.properties.clover.jar + def testLaf = project.findProperty("test_laf") + if (testLaf != null) { + println("Setting Test LaF to '${testLaf}'") + systemProperty "laf", testLaf + } + def testHiDPIScale = project.findProperty("test_HiDPIScale") + if (testHiDPIScale != null) { + println("Setting Test HiDPI Scale to '${testHiDPIScale}'") + systemProperty "sun.java2d.uiScale", testHiDPIScale + } sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility jvmArgs += additional_compiler_args + doFirst { + if (useClover) { + println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover") + } + } } @@ -987,22 +1120,22 @@ task buildIndices(type: JavaExec) { task compileLinkCheck(type: JavaCompile) { options.fork = true - classpath = files("${jalviewDir}/${utilsDir}") - destinationDir = file("${jalviewDir}/${utilsDir}") - source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"]) - - inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java") - inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java") - outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class") - outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class") + classpath = files("${jalviewDir}/${utils_dir}") + destinationDir = file("${jalviewDir}/${utils_dir}") + source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"]) + + inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java") + inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java") + outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class") + outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class") } task linkCheck(type: JavaExec) { dependsOn prepare, compileLinkCheck - def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out") - classpath = files("${jalviewDir}/${utilsDir}") + def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out") + classpath = files("${jalviewDir}/${utils_dir}") main = "HelpLinksChecker" workingDir = jalviewDir args = [ "${classesDir}/${help_dir}", "-nointernet" ] @@ -1023,12 +1156,12 @@ task linkCheck(type: JavaExec) { // import the pubhtmlhelp target ant.properties.basedir = "${jalviewDir}" ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}" -ant.importBuild "${utilsDir}/publishHelp.xml" +ant.importBuild "${utils_dir}/publishHelp.xml" task cleanPackageDir(type: Delete) { doFirst { - delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar") + delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar") } } @@ -1038,13 +1171,13 @@ jar { dependsOn createBuildProperties manifest { - attributes "Main-Class": mainClass, + attributes "Main-Class": main_class, "Permissions": "all-permissions", "Application-Name": "Jalview Desktop", "Codebase": application_codebase } - destinationDir = file("${jalviewDir}/${packageDir}") + destinationDir = file("${jalviewDir}/${package_dir}") archiveName = rootProject.name+".jar" exclude "cache*/**" @@ -1054,20 +1187,20 @@ jar { exclude "**/*.jar.*" inputs.dir(classesDir) - outputs.file("${jalviewDir}/${packageDir}/${archiveName}") + outputs.file("${jalviewDir}/${package_dir}/${archiveName}") } task copyJars(type: Copy) { from fileTree(dir: classesDir, include: "**/*.jar").files - into "${jalviewDir}/${packageDir}" + into "${jalviewDir}/${package_dir}" } // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well task syncJars(type: Sync) { from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files - into "${jalviewDir}/${packageDir}" + into "${jalviewDir}/${package_dir}" preserve { include jar.archiveName } @@ -1083,7 +1216,7 @@ task makeDist { dependsOn cleanPackageDir dependsOn syncJars dependsOn jar - outputs.dir("${jalviewDir}/${packageDir}") + outputs.dir("${jalviewDir}/${package_dir}") } @@ -1104,7 +1237,7 @@ shadowJar { manifest { attributes 'Implementation-Version': JALVIEW_VERSION } - mainClassName = shadowJarMainClass + mainClassName = shadow_jar_main_class mergeServiceFiles() classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION minimize() @@ -1196,7 +1329,7 @@ task getdownWebsite() { } def codeFiles = [] - fileTree(file(packageDir)).each{ f -> + fileTree(file(package_dir)).each{ f -> if (f.isDirectory()) { def files = fileTree(dir: f, include: ["*"]).getFiles() codeFiles += files @@ -1214,6 +1347,16 @@ task getdownWebsite() { } } + fileTree(dir: digestonlyDir, include: ["*"]).getFiles().sort().each{f -> + def name = f.getName() + def line = "digestonly = ${getdownAppDistDir}/${name}\n" + getdownTextString += line + copy { + from f.getPath() + into getdownAppDir + } + } + // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist /* if (JAVA_VERSION.equals("11")) { @@ -1233,7 +1376,11 @@ task getdownWebsite() { // getdown-launcher.jar should not be in main application class path so the main application can move it when updated. Listed as a resource so it gets updated. //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n" getdownTextString += "resource = ${getdown_launcher_new}\n" - getdownTextString += "class = ${mainClass}\n" + getdownTextString += "class = ${main_class}\n" + /* NOT setting these properties so that getdownappbase and getdowndistdir will default to release version + * getdownTextString += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}\n" + * getdownTextString += "jvmarg = -Dgetdownappbase=${getdownAppBase}\n" + */ def getdown_txt = file("${getdownWebsiteDir}/getdown.txt") getdown_txt.write(getdownTextString) @@ -1260,7 +1407,7 @@ task getdownWebsite() { copy { from getdown_txt from getdownLauncher - from "${getdownWebsiteDir}/${getdown_build_properties}" + from "${getdownAppDir}/${getdown_build_properties}" if (file(getdownLauncher).getName() != getdown_launcher) { rename(file(getdownLauncher).getName(), getdown_launcher) } @@ -1291,7 +1438,7 @@ task getdownWebsite() { } if (buildDist) { - inputs.dir("${jalviewDir}/${packageDir}") + inputs.dir("${jalviewDir}/${package_dir}") } outputs.dir(getdownWebsiteDir) outputs.dir(getdownFilesDir) diff --git a/doc/building.md b/doc/building.md index ad19979..855f966 100644 --- a/doc/building.md +++ b/doc/building.md @@ -276,8 +276,8 @@ although without the many and compelling benefits of the `getdown` launcher. We have made significant customisations to the `getdown` launcher which you can find in `getdown/src/getdown`. -> You don't need to build this afresh as the required `gradle-core.jar` -and `gradle-launcher.jar` files are already distributed in `j11lib` and `getdown/lib` but if you want to, then +> You don't need to build this afresh as the required `getdown-core.jar` +and `getdown-launcher.jar` files are already distributed in `j11lib` and `getdown/lib` but if you want to, then you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 `javac` is forefront in your path and do > @@ -285,9 +285,9 @@ in your path and do >cd getdown/src/getdown >mvn clean package -Dgetdown.host.whitelist="jalview.org,*.jalview.org" >``` -> and you will find the required `.jar` files in `core/target/gradle-core-XXX.jar` -and `launcher/target/gradle-launcher-XXX.jar`. The `gradle-core.jar` should then be copied -to all three of the `j8lib`, `j11lib` and `getdown/lib` folders, whilst the `gradle-launcher.jar` only +> and you will find the required `.jar` files in `core/target/getdown-core-XXX.jar` +and `launcher/target/getdown-launcher-XXX.jar`. The `getdown-core.jar` should then be copied +to all three of the `j8lib`, `j11lib` and `getdown/lib` folders, whilst the `getdown-launcher.jar` only needs to be copied to `getdown/lib`. > >The `mvn` command should ideally include the `-Dgetdown.host.whitelist=*.jalview.org` setting. @@ -318,6 +318,7 @@ java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview getdown, and the final argument, "`jalview`", is a getdown application id (only "`jalview`" is defined here). +The command line sequence for building and relocating the getdown artifacts can be executed as a script via `getdown/src/getdown/mvn_cmd`. Please make sure this script is kept up to date should the getdown build instructions change. ### Running tests diff --git a/getdown/lib/FJVL_VERSION b/getdown/lib/FJVL_VERSION index e7a1de7..07a2dfc 100644 --- a/getdown/lib/FJVL_VERSION +++ b/getdown/lib/FJVL_VERSION @@ -1 +1 @@ -1.8.3-1.2.4_FJVL +1.8.3-1.2.9_FJVL diff --git a/getdown/lib/JVL_VERSION b/getdown/lib/JVL_VERSION index 756f4bd..b971e64 100644 --- a/getdown/lib/JVL_VERSION +++ b/getdown/lib/JVL_VERSION @@ -1 +1 @@ -1.8.3-1.2.4_JVL +1.8.3-1.2.9_JVL diff --git a/getdown/lib/getdown-core.jar b/getdown/lib/getdown-core.jar index 9c0f96e..c927dae 100644 Binary files a/getdown/lib/getdown-core.jar and b/getdown/lib/getdown-core.jar differ diff --git a/getdown/lib/getdown-launcher-local.jar b/getdown/lib/getdown-launcher-local.jar index cfa96f3..31df1e6 100644 Binary files a/getdown/lib/getdown-launcher-local.jar and b/getdown/lib/getdown-launcher-local.jar differ diff --git a/getdown/lib/getdown-launcher.jar b/getdown/lib/getdown-launcher.jar index 2f8baa7..d0e5e35 100644 Binary files a/getdown/lib/getdown-launcher.jar and b/getdown/lib/getdown-launcher.jar differ diff --git a/getdown/src/getdown/ant/pom.xml b/getdown/src/getdown/ant/pom.xml index 8dcef42..c180d5f 100644 --- a/getdown/src/getdown/ant/pom.xml +++ b/getdown/src/getdown/ant/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-1.2.4_FJVL + 1.8.3-1.2.9_FJVL getdown-ant diff --git a/getdown/src/getdown/core/pom.xml b/getdown/src/getdown/core/pom.xml index 2d0e89a..dd387b1 100644 --- a/getdown/src/getdown/core/pom.xml +++ b/getdown/src/getdown/core/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-1.2.4_FJVL + 1.8.3-1.2.9_FJVL getdown-core diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java index fd53c79..313a690 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java @@ -27,6 +27,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; +import jalview.bin.HiDPISetting; import jalview.bin.MemorySetting; //import com.install4j.api.launcher.Variables; @@ -353,6 +354,11 @@ public class Application return _resources; } + public List getDigestOnly () + { + return _digestonly; + } + /** * Returns the digest of the given {@code resource}. */ @@ -771,6 +777,7 @@ public class Application // clear our arrays as we may be reinitializing _codes.clear(); _resources.clear(); + _digestonly.clear(); _auxgroups.clear(); _jvmargs.clear(); _appargs.clear(); @@ -791,6 +798,8 @@ public class Application parseResources(config, "presource", Resource.PRELOAD, _resources); parseResources(config, "nresource", Resource.NATIVE, _resources); + parseResources(config, "digestonly", Resource.NORMAL, _digestonly); + // parse our auxiliary resource groups for (String auxgroup : config.getList("auxgroups")) { ArrayList codes = new ArrayList<>(); @@ -1071,6 +1080,13 @@ public class Application args.add("-Dinstaller_template_version=" + System.getProperty("installer_template_version")); args.add("-Dlauncher_version=" + Build.version()); + // set HiDPI property if wanted + String scalePropertyArg = HiDPISetting.getScalePropertyArg(); + if (scalePropertyArg != null) + { + args.add(scalePropertyArg); + } + // set the native library path if we have native resources // @TODO optional getdown.txt parameter to set addCurrentLibraryPath to true or false? ClassPath javaLibPath = PathBuilder.buildLibsPath(this, true); @@ -1411,6 +1427,8 @@ public class Application clearValidationMarkers(); // if the new copy validates, reinitialize ourselves; otherwise report baffling hoseage if (_digest.validateResource(crsrc, null)) { + // unset _initialisedConfig so new file is initialised + _initialisedConfig = null; init(true); } else { log.warning(CONFIG_FILE + " failed to validate even after redownloading. " + @@ -2066,6 +2084,7 @@ public class Application protected List _codes = new ArrayList<>(); protected List _resources = new ArrayList<>(); + protected List _digestonly = new ArrayList<>(); protected boolean _useCodeCache; protected int _codeCacheRetentionDays; diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java index b04a653..8d96024 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java @@ -81,6 +81,7 @@ public class Digester rsrcs.add(app.getConfigResource()); rsrcs.addAll(app.getCodeResources()); rsrcs.addAll(app.getResources()); + rsrcs.addAll(app.getDigestOnly()); for (Application.AuxGroup ag : app.getAuxGroups()) { rsrcs.addAll(ag.codes); rsrcs.addAll(ag.rsrcs); diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/GetMemory.java b/getdown/src/getdown/core/src/main/java/jalview/bin/GetMemory.java index e89bffb..b01dfb8 100644 --- a/getdown/src/getdown/core/src/main/java/jalview/bin/GetMemory.java +++ b/getdown/src/getdown/core/src/main/java/jalview/bin/GetMemory.java @@ -1,3 +1,23 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.bin; import java.lang.management.ManagementFactory; diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/HiDPISetting.java b/getdown/src/getdown/core/src/main/java/jalview/bin/HiDPISetting.java new file mode 100644 index 0000000..497900f --- /dev/null +++ b/getdown/src/getdown/core/src/main/java/jalview/bin/HiDPISetting.java @@ -0,0 +1,180 @@ +package jalview.bin; + +import java.awt.HeadlessException; +import java.awt.Toolkit; + +public class HiDPISetting +{ + private static final int hidpiThreshold = 160; + + private static final int hidpiMultiThreshold = 240; + + private static final int bigScreenThreshold = 1400; + + private static final String scalePropertyName = "sun.java2d.uiScale"; + + private static final boolean isLinux; + + // private static final boolean isAMac; + + // private static final boolean isWindows; + + public static final String setHiDPIPropertyName = "setHiDPI"; + + public static final String setHiDPIScalePropertyName = "setHiDPIScale"; + + private static boolean setHiDPI = false; + + private static int setHiDPIScale = 0; + + public static int dpi = 0; + + public static int mindimension = 0; + + public static int width = 0; + + public static int scale = 0; + + private static boolean doneInit = false; + + private static boolean allowScalePropertyArg = false; + + static + { + String system = System.getProperty("os.name") == null ? null + : System.getProperty("os.name").toLowerCase(); + if (system != null) + { + isLinux = system.indexOf("linux") > -1; + // isAMac = system.indexOf("mac") > -1; + // isWindows = system.indexOf("windows") > -1; + } + else + { + isLinux = false; + // isAMac = isWindows = false; + } + } + + private static void init() + { + if (doneInit) + { + return; + } + + // get and use command line property values first + String setHiDPIProperty = System.getProperty(setHiDPIPropertyName); + setHiDPI = setHiDPIProperty != null + && setHiDPIProperty.equalsIgnoreCase("true"); + + String setHiDPIScaleProperty = System + .getProperty(setHiDPIScalePropertyName); + if (setHiDPIScaleProperty != null) + { + try + { + setHiDPIScale = Integer.parseInt(setHiDPIScaleProperty); + } catch (NumberFormatException e) + { + System.err.println(setHiDPIScalePropertyName + " property give (" + + setHiDPIScaleProperty + ") but not parseable as integer"); + } + } + if (setHiDPI && setHiDPIScale > 0) + { + setHiDPIScale(setHiDPIScale); + return; + } + + // check to see if the scale property has already been set by something else + // (e.g. the OS) + String existingProperty = System.getProperty(scalePropertyName); + if (existingProperty != null) + { + try + { + int existingPropertyVal = Integer.parseInt(existingProperty); + System.out.println("Existing " + scalePropertyName + " is " + + existingPropertyVal); + if (existingPropertyVal > 1) + { + setHiDPIScale(existingPropertyVal); + return; + } + } catch (NumberFormatException e) + { + System.out.println("Could not convert property " + scalePropertyName + + " vale '" + existingProperty + "' to number"); + } + } + + // Try and auto guess a good scale based on reported DPI (not trustworthy) + // and screen resolution (more trustworthy) + + // get screen dpi + try + { + dpi = Toolkit.getDefaultToolkit().getScreenResolution(); + } catch (HeadlessException e) + { + System.err.println("Cannot get screen resolution: " + e.getMessage()); + } + + // try and get screen size height and width + try + { + int height = Toolkit.getDefaultToolkit().getScreenSize().height; + int width = Toolkit.getDefaultToolkit().getScreenSize().width; + // using mindimension in case of portrait screens + mindimension = Math.min(height, width); + } catch (HeadlessException e) + { + System.err.println( + "Cannot get screen size height and width:" + e.getMessage()); + } + + // attempt at a formula for scaling based on screen dpi and mindimension. + // scale will be an integer >=1. This formula is based on some testing and + // guesswork! + + // scale based on reported dpi. if dpi>hidpiThreshold then scale=2+multiples + // of hidpiMultiThreshold (else scale=1) + // (e.g. dpi of 110 scales 1. dpi of 120 scales 2. dpi of 360 scales 3) + int dpiScale = (dpi - hidpiThreshold > 0) + ? 2 + ((dpi - hidpiThreshold) / hidpiMultiThreshold) + : 1; + + int dimensionScale = 1 + (mindimension / bigScreenThreshold); + + // choose larger of dimensionScale or dpiScale (most likely dimensionScale + // as dpiScale often misreported) + int autoScale = Math.max(dpiScale, dimensionScale); + + // only make an automatic change if scale is changed and other conditions + // (OS is linux) apply, or if setHiDPI has been specified + if ((autoScale > 1 && isLinux) || setHiDPI) + { + setHiDPIScale(autoScale); + return; + } + + // looks like we're not doing any scaling + doneInit = true; + } + + public static void setHiDPIScale(int s) + { + scale = s; + allowScalePropertyArg = true; + doneInit = true; + } + + public static String getScalePropertyArg() + { + init(); + // HiDPI setting. Just looking at Linux to start with. Test with Windows. + return allowScalePropertyArg ? "-D" + scalePropertyName + "=" + scale + : null; + } +} diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java b/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java index 7849ba2..159374a 100644 --- a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java +++ b/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java @@ -1,3 +1,23 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.bin; /** diff --git a/getdown/src/getdown/launcher/pom.xml b/getdown/src/getdown/launcher/pom.xml index ad87541..e7dbeb1 100644 --- a/getdown/src/getdown/launcher/pom.xml +++ b/getdown/src/getdown/launcher/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-1.2.4_FJVL + 1.8.3-1.2.9_FJVL getdown-launcher diff --git a/getdown/src/getdown/mvn_cmd b/getdown/src/getdown/mvn_cmd index 3f9b4a2..e0d7cf1 100755 --- a/getdown/src/getdown/mvn_cmd +++ b/getdown/src/getdown/mvn_cmd @@ -3,7 +3,7 @@ if [ x$JVLVERSION != x ]; then export VERSION=$JVLVERSION else - export VERSION=1.8.3-1.2.4_JVL + export VERSION=1.8.3-1.2.9_JVL fi if [ x${VERSION%_JVL} = x$VERSION ]; then diff --git a/getdown/src/getdown/pom.xml b/getdown/src/getdown/pom.xml index 2ca4aa1..8c7c521 100644 --- a/getdown/src/getdown/pom.xml +++ b/getdown/src/getdown/pom.xml @@ -10,7 +10,7 @@ com.threerings.getdown getdown pom - 1.8.3-1.2.4_FJVL + 1.8.3-1.2.9_FJVL getdown An application installer and updater. diff --git a/gradle.properties b/gradle.properties index 782a495..453572a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,6 @@ -org.gradle.jvmargs=-Xmx1536m -Xms512m +# Convention for properties. Read from gradle.properties, use lower_case_underlines for property names. +# For properties set within build.gradle, use camelCaseNoSpace. +# jalviewDir = . @@ -23,38 +25,35 @@ proxyHost = sqid jalview_keyalg = SHA1withRSA jalview_keydig = SHA1 -testngGroups = Functional -testngExcludedGroups = +testng_groups = Functional +testng_excluded_groups = j8libDir = j8lib j11libDir = j11lib resource_dir = resources help_parent_dir = help help_dir = help -docDir = doc -schemaDir = schemas +doc_dir = doc classes_dir = classes -examplesDir = examples clover = false -use_clover = false -cloverReportJVMHeap = 2g -cloverReportJVMArgs = -Dfile.encoding=UTF-8 -cloverReportHTMLOptions = -cloverReportXMLOptions = -cloverClassesDir = clover-classes -cloverSourcesInstrDir = sources-instr -packageDir = dist +clover_classes_dir = clover-classes +clover_sources_instr_dir = clover-instr +clover_report_dir = clover-report +clover_lib_dir = utils/clover/lib +cloverreport_mem = 2g +cloverreport_jvmargs = -Dfile.encoding=UTF-8 +cloverreport_html_options = +cloverreport_xml_options = +package_dir = dist ARCHIVEDIR = -outputJar = jalview.jar -testOutputDir = tests -utilsDir = utils +test_output_dir = tests +utils_dir = utils build_properties_file = .build_properties application_codebase = *.jalview.org -mainClass = jalview.bin.Jalview -shadowJarMainClass = jalview.bin.Launcher -launcherClass = jalview.bin.Jalview +main_class = jalview.bin.Jalview +shadow_jar_main_class = jalview.bin.Launcher jalview_name = Jalview @@ -107,9 +106,12 @@ getdown_alt_java11_max_version = jre_installs_dir = ~/buildtools/jre j8libDir = j8lib +j8digestonlyDir = j8digestonly j11libDir = j11lib j11modDir = j11mod j11modules = com.sun.istack.runtime,com.sun.xml.bind,com.sun.xml.fastinfoset,com.sun.xml.streambuffer,com.sun.xml.txw2,com.sun.xml.ws.policy,java.activation,java.annotation,java.base,java.compiler,java.datatransfer,java.desktop,java.logging,java.management,java.management.rmi,java.naming,java.prefs,java.rmi,java.scripting,java.security.sasl,java.sql,java.xml,java.xml.bind,java.xml.soap,java.xml.ws,javax.jws,jdk.httpserver,jdk.jsobject,jdk.unsupported,jdk.xml.dom,org.jvnet.mimepull,org.jvnet.staxex,javax.servlet.api,java.ws.rs +j11digestonlyDir = j11digestonly + install4j_home_dir = ~/buildtools/install4j8 install4j_copyright_message = ... diff --git a/help/help/help.jhm b/help/help/help.jhm index 99d010d..7dbb76d 100755 --- a/help/help/help.jhm +++ b/help/help/help.jhm @@ -170,4 +170,5 @@ + diff --git a/help/help/helpTOC.xml b/help/help/helpTOC.xml index a0c7fe6..a72f5ac 100755 --- a/help/help/helpTOC.xml +++ b/help/help/helpTOC.xml @@ -27,7 +27,7 @@ - + @@ -156,6 +156,7 @@ + diff --git a/help/help/html/features/clarguments.html b/help/help/html/features/clarguments.html index 60db069..1eecfb9 100644 --- a/help/help/html/features/clarguments.html +++ b/help/help/html/features/clarguments.html @@ -34,6 +34,11 @@ parameters, then include it at the beginning of the command line to ensure they are processed before any remaining arguments.
+ Typical command line execution follows the following pattern: +
+  jalview -open <Alignment File/URL> [additional import arguments] [export arguments]
+  
+ @@ -41,7 +46,11 @@ User Interface. (automatically disables questionnaire, version and usage stats checks) - + + + + + + + + + + + +
-nodisplay
-open FILE/URL
Specify the alignment file to + open or process by providing additional arguments.
-props FILE/URL
Use the given Jalview properties @@ -225,6 +234,25 @@
-jvmmempc=PERCENT
Only available with standalone executable jar or jalview.bin.Launcher. + Limit maximum heap size (memory) to PERCENT% of total physical memory detected. + This defaults to 90 if total physical memory can be detected. + See Memory usage settings for Jalview for more details. +
+
-jvmmemmax=MAXMEMORY
Only available with standalone executable jar or jalview.bin.Launcher. + Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), + gigabytes(g) or if you're lucky enough, terabytes(t). + This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. + See Memory usage settings for Jalview for more details. +
+
diff --git a/help/help/html/features/commandline.html b/help/help/html/features/commandline.html index e00d390..0ef78f9 100644 --- a/help/help/html/features/commandline.html +++ b/help/help/html/features/commandline.html @@ -38,7 +38,7 @@
  • Standard installation on Linux/Unix:
    -       /PATH_TO_JALVIEW/Jalview -open http://www.jalview.org/examples/jpred_msa.fasta -annotations http://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal
    + /PATH_TO_JALVIEW/Jalview -open https://www.jalview.org/examples/jpred_msa.fasta -annotations https://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal
  • Standard installation on Windows:
           \PATH_TO_JALVIEW\Jalview.exe -open %HOMEPATH%\myalignment.fa
    @@ -65,10 +65,31 @@ open /Applications/Jalview.app --args -open ~/myalignment.fa(put

    Passing JVM Arguments to Jalview
    If you need - to modify parameters for Jalview's Java Virtual Machine, then take a - look at the instructions for how to setting - the JVM's maximum memory. + to modify parameters for Jalview's Java Virtual Machine, or + configure system properties, then take a look at the instructions + for how to setting the JVM's + maximum memory.
    +

    + Changing Jalview's 'Look and Feel'
    If you + are experiencing issues with the font size or layout of Jalview's + GUI, you can try changing Jalview's 'Look and feel' by + specifying a custom system property 'laf' on startup (see setting the JVM's memory for + instructions on how to do this for your platform).
    For the + Jalview standalone executable jar, simply provide one of the + property settings before the -jar argument

    +
      +
    • -Dlaf=system (default look and feel for the OS)
    • +
    • -Dlaf=crossplatform (Java's Metal Look and Feel)
    • +
    • -Dlaf=nimbus (Java's alternative Nimbus Look and Feel)
    • +
    • -Dlaf=mac (only has an effect on OSX)
    • +
    • -Dlaf=gtk (only has an effect on Linux)
    • +
    + The currently configured look and feel is logged to Jalview's console. + Once the look and feel has been changed, it will be stored in + Jalview's .jalview_properties file for future Jalview sessions. +

     

     

    diff --git a/help/help/html/features/groovy.html b/help/help/html/features/groovy.html index ead4436..cc91154 100644 --- a/help/help/html/features/groovy.html +++ b/help/help/html/features/groovy.html @@ -39,7 +39,7 @@ installation of Groovy. Just select Tools→Groovy Console... from the Jalview Desktop's drop-down menu. After a short pause, you should then see the Groovy + href="https://groovy-lang.org/groovyconsole.html">Groovy Console appear. This allows you to interactively execute Groovy scripts whilst Jalview is running. We've also provided a Calculations→Execute Groovy Script button so you can execute the currently loaded @@ -101,7 +101,7 @@ print currentAlFrame.getTitle(); InstallAnywhere version of Jalview, you can find additional groovy scripts in the examples/groovy subfolder of the installation directory. The examples are also available at http://www.jalview.org/examples/groovy. + href="https://www.jalview.org/examples/groovy">https://www.jalview.org/examples/groovy.

    Using Groovy to add new Alignment Calculations
    We've @@ -115,8 +115,8 @@ print currentAlFrame.getTitle(); Creating custom colourschemes
    You can create your own alignment colourschemes with a groovy script. We've provided two examples:

    diff --git a/help/help/html/features/jvlfiles.html b/help/help/html/features/jvlfiles.html index 27742b3..e23af30 100644 --- a/help/help/html/features/jvlfiles.html +++ b/help/help/html/features/jvlfiles.html @@ -46,12 +46,12 @@
     # Jalview Launch File
     # Please install the Jalview Desktop from 
    -# http://www.jalview.org/getdown/release
    +# https://www.jalview.org/getdown/release
     # and then try to open this file again
     jalview.apparg=-open
    -jalview.apparg=http://www.jalview.org/examples/jpred_msa.fasta
    +jalview.apparg=https://www.jalview.org/examples/jpred_msa.fasta
     jalview.apparg=-annotations
    -jalview.apparg=http://www.jalview.org/examples/jpred_msa.seq.concise
    +jalview.apparg=https://www.jalview.org/examples/jpred_msa.seq.concise
     jalview.apparg=-colour
     jalview.apparg=Clustal
     
    @@ -62,7 +62,7 @@ jalview.apparg=Clustal # Please install the Jalview Desktop from # http://www.jalview.org/getdown/release # and then try to open this file again -appbase=http://www.jalview.org/getdown/archive/2_10_5/ +appbase=https://www.jalview.org/getdown/archive/2_10_5/ For security, the Jalview application will only allow appbase URLs from www.jalview.org. diff --git a/help/help/html/features/search.html b/help/help/html/features/search.html index eec68ee..837d7b3 100755 --- a/help/help/html/features/search.html +++ b/help/help/html/features/search.html @@ -36,7 +36,7 @@ td {

    The search box is displayed by pressing Control and F or selecting "Find..." from the "Search" menu.

    - +

    "Find next" will find the next occurrence of the specified and adjust the alignment window view to show it, and "Find all" highlights all matches for a pattern. The @@ -48,18 +48,19 @@ td { of posix and perl style regex - see below for a summary)

  • Gaps are ignored when matching the query to the sequences in the alignment.
  • +
  • Hidden columns can optionally be ignored (since Jalview 2.11)
  • The search is applied to both sequences and their IDs, and optionally also to the description string (since Jalview 2.10)
  • If a region is selected, then search will only - be performed on that region. + be performed on that region.
    + Tip: to quickly clear the current selection, click the + alignment view you wish to search, then press 'Escape'.
  • -
  • To quickly clear the current selection, press the - "Escape" key.
  • Tick the "Match Case" box to perform a case sensitive search.
  • -
  • To access a previously used +
  • To access a previously used query press the down arrow or click on the button on the right of the text field.
@@ -155,12 +156,13 @@ td { stored along with your Jalview user preferences. To open the search history, click on the button to the right of the query field, or press the down arrow key.

- -

The search history keeps up to 99 queries by default. To clear +

+

The search history keeps up to 99 queries by default. To clear the history, or modify the size of the history, right-click the text box.

- -

+

+

+

Other dialogs that provide a query history

diff --git a/help/help/html/features/search.png b/help/help/html/features/search.png index 89adb1a..47c18f4 100644 Binary files a/help/help/html/features/search.png and b/help/help/html/features/search.png differ diff --git a/help/help/html/features/seqfetch.html b/help/help/html/features/seqfetch.html index e726c49..4e9a75f 100755 --- a/help/help/html/features/seqfetch.html +++ b/help/help/html/features/seqfetch.html @@ -72,14 +72,30 @@ "OK" to initiate the retrieval. -

If you use the WSDBFetch sequence fetcher services (EMBL, - UniProt, PFAM, and RFAM) in work for publication, please cite:

-

- Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate - J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez - R.
SOAP-based services provided by the European - Bioinformatics Institute.
Nucleic Acids Res. 33(1):W25-W28 - (2005)

-

+

If you use the Sequence Fetcher, please remember to cite the + corresponding services (linked to below):

+
    +
  • Ensembl - The + Ensembl REST API
  • +
  • EMBL/EMBLCDS - Provided by the European + Nucleotide Archive's ENA Data API
    + Note: Versions of Jalview prior to 2.11.1.1 employed the + XML endpoint of the ENA browser, which was retired in August + 2020.
  • +
  • Uniprot - Free Text Search and Retrieval via the Uniprot REST API
  • +
  • PDB - Free Text Search via the PDBe + REST API and retrieval via WSDbFetch
    + Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate + J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez + R.
    SOAP-based services provided by the European + Bioinformatics Institute.
    Nucleic Acids Res. + 33(1):W25-W28 (2005)

    +
  • +
diff --git a/help/help/html/groovy/featuresCounter.html b/help/help/html/groovy/featuresCounter.html index 3b6705b..bd7144e 100644 --- a/help/help/html/groovy/featuresCounter.html +++ b/help/help/html/groovy/featuresCounter.html @@ -43,7 +43,7 @@ Please note: The 2.10.2 feature counting interface is not compatible with earlier versions.

http://www.jalview.org/examples/groovy/featuresCounter.groovy + href="https://www.jalview.org/examples/groovy/featuresCounter.groovy">https://www.jalview.org/examples/groovy/featuresCounter.groovy - rendered with hilite.me
/*
  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
diff --git a/help/help/html/index.html b/help/help/html/index.html
index 36ed33a..a6bb1b6 100755
--- a/help/help/html/index.html
+++ b/help/help/html/index.html
@@ -45,7 +45,7 @@
   

For more information, you might also want to take a look at the documentation section of the Jalview website (http://www.jalview.org/about/documentation). + href="https://www.jalview.org/about/documentation">https://www.jalview.org/about/documentation).

If you are using the Jalview Desktop application and are looking for @@ -54,9 +54,14 @@ google the online version of these pages. If you don't find what you are looking for, or want to report a bug or make a feature request, then get in contact over at http://www.jalview.org/community + href="https://www.jalview.org/community">https://www.jalview.org/community +

+

+ Logging, troubleshooting and reporting bugs
If + something seems to be wrong with your Jalview installation, or you + think you've found a problem, take a look at Jalview's + logging and bug reporting documentation.

-

Citing Jalview
If you use Jalview in your work, please cite the Jalview 2 paper in Bioinformatics: diff --git a/help/help/html/keys.html b/help/help/html/keys.html index 1a5fc18..0faa1d5 100755 --- a/help/help/html/keys.html +++ b/help/help/html/keys.html @@ -78,32 +78,25 @@ Redo the last sequence edit undone. - Up Arrow - Normal - Moves selected sequence(s) up the alignment - - - Down Arrow - Normal - Moves selected sequence(s) down the alignment. - - - Left Arrow - Normal - Slides selected sequence(s) left. Press Alt key to slide - in cursor mode - - - Right Arrow - Normal - Slides selected sequence(s) right. Press Alt key to slide - in cursor mode + Cursor Keys
(Arrow Keys) +
+ Cursor + Move cursor around alignment.
Press SHIFT to move + cursor from an aligned region to next gap, or to the next + aligned region when at a gap. + Cursor Keys
(Arrow Keys)
- Cursor - Move cursor around alignment + Normal
+ (+Alt in Cursor) + Moves selected sequence(s) up, down, left, or right + according to the direction pressed.
+
+ Alt+Arrow key to move selection or sequence under cursor + in cursor mode. + Page Up diff --git a/help/help/html/logging.html b/help/help/html/logging.html new file mode 100644 index 0000000..0975a47 --- /dev/null +++ b/help/help/html/logging.html @@ -0,0 +1,212 @@ + + + +The Java Console, Logging and Reporting Bugs + + +

+ The Java Console, Logging and Reporting Bugs
+

+

+ Like most programs, Jalview contains bugs, despite our best efforts. + However, Jalview also produces a series of messages during its + operation, often referred to as 'logs'. These logs provide a record + of Jalview's operation. They can also be extremely useful when reporting bugs, since they help the + Jalview developers diagnose and find a workaround for specific + problems that you might encounter. +

+

+ The primary place to look for logs is in the Java + Console which you can open from within Jalview by going to the Tools + menu and checking the box next to Show Java Console. This + option is stored in your Jalview preferences file and so is + remembered across Jalview sessions. +

+

The Java Console will show you information about what the + Jalview application is doing (often in the background) whilst it is + running.

+

However, when tracking down problems preventing Jalview from + starting up properly, you need to look at the startup logs - which + are not shown in the Jalview Console. The location of these depends + on how you launched Jalview:

+

+ Jalview Desktop Installation Launch Logs
If you are using + a standard desktop version of Jalview installed from one of our + install4j installers, then messages about Jalview's initial launch + can be found in +

JALVIEW_APP_DIR/launcher.log
+ where + JALVIEW_APP_DIR is the directory that Jalview's application + was installed into. +
For Jalview 2.11.0 onwards: +
    +
  • In Windows this is %APPDATA%\Local\Jalview by + default +
  • +
  • In macOS this is /Applications/Jalview.app/Contents/Resources/app + by default +
  • +
  • In Linux and other Unix OSes this is ~/opt/jalview + by default +
  • +
+

Jalview Executable Jar Launch Logs
If you are using the Jalview executable jar file (also + used by bioconda and OSX homebrew installations) then the default run class ( + jalview.bin.Launcher -- a minimised launcher that will set + memory and linux dpi settings before re-launching + jalview.bin.Jalview), will output logging information to + STDOUT and STDERR. +

+ +

+ Java Console and Log Level +

+

+ The Java Console is opened by selecting Tools + → Show Java Console. The visibility of the console is stored + in your preferences, so if you quit Jalview with the console open, + it will be shown the next time you start Jalview. You can close the + console by selecting the same menu option again, or just closing the + console window. +

+

The Java Console's text display always shows information about + your system and Jalview installation details. The rest are the most + recent messages output during your Jalview session. Some messages + are only captured by the console when it is open, so to get a full + log for debugging a problem, enable the console and then restart + Jalview.

+ +

+ You can temporarily control the detail of what appears as output by + selecting a Log level using the drop-down list at the + bottom left of the console. There are several levels to choose from: + The most verbose is TRACE, followed by DEBUG, INFO, WARN. When the + Console is opened, the default level will be chosen (INFO). +

+

+ Note! If you change the log level in the Java + Console, this change will only persist for as long as the console + is open. Once you close the console the log level will revert back + to what it had been when you opened the console (usually INFO). +

+

Permanently changing Jalview's default log level
+ You can change the default log level by editing the Jalview + preferences file, .jalview_properties, found in your home + directory (on Windows: %HOMEPATH%, or the folder above 'My + Documents'; on macOS: ~ or /Users/username; on linux/unix: + ~ or /home/username), and setting the property logs.Jalview.level + to the log level you prefer, e.g. +

+  logs.Jalview.level=DEBUG
+  
+ You can also set the property +
+  logs.Axis.level=DEBUG
+  
+

to get debug information for Jalview's JPred service. The Axis log + level cannot be set from within the Java Console. +

+

+ You can also set the logs.jalview.level property to a log level + not usually presented in the Java Console (though restricted to log + levels used by Apache Log4j -- see Log4j + Custom Log Levels for details of the standard log levels + available). Jalview does not currently define any custom log levels. + If you do set the property with a log level that is normally not + visible in the Java Console this should be respected and visibly + selected when you open the console. +

+

+ The Clear button at the bottom of the console will clear + all logging messages except for the initial system information which + is rewritten to the console. +

+

+ The Copy to clipboard button at the bottom right of the + console will copy all of the text in the console to your system + clipboard, ready to paste into another application (e.g. email + composer or issue tracker). +

+ +

Reporting Bugs

+ +

+ If you come across a problem in Jalview where something is not + working as described, or how you think it should, you should first + check the Jalview FAQ to + see if this is a known problem and if there is a suggested + workaround. +

+

+ If there is no FAQ answer covering your problem then you can submit + a bug report on the Jalview + Issue Tracker. It is good practice to search the issue tracker + first to see if the issue has already been reported. If an issue + already exists please continue to add your own comments to the issue + which may well help narrow down the problem, if not then you can + create an account and submit a new bug report: +

+

+ Make sure that you set Project to Jalview (JAL), and Issue + Type to Bug or New Feature or Improvement + appropriately.
Give a one line summary of the issue in the Summary. +
In the Environment text box you can describe the + system you are using. This is usually most easily done by opening + the Java Console, clicking the Clear button, and then + immediately on the Copy to clipboard button, and then + pasting the clipboard into the text box. +

+

+ You can then give more detailed information about how to recreate + the problem in the Description text box. If you want to + attach any screenshots or example alignment files that demonstrate + the problem then you can drag them to the Create Issue dialog in + your browser, or use the Attachment browse facility to + locate them on your computer. +

+ +

+ To help the Jalview team with diagnosing a particular issue, it is + really helpful if you can also add more detailed logs output whilst + re-creating the problem. To do this, open the Java Console, click + the Clear button and select TRACE in the Log level + drop down list.
Whilst leaving the console open, perform the + task in Jalview that re-creates the problem.
Then you can + copy the debug information in the Java Console by clicking on the Copy + to clipboard button and then paste that into the Description, or a + Comment of your issue. +

+ +

+ For other queries or comments about Jalview, remember you can + contact the Jalview team using email via the + Jalview + discussion list, on Twitter @Jalview, or for technical + discussions, via the Jalview developer's chatroom at + https://gitter.im/jalview/developers. +

+ + + diff --git a/help/help/html/memory.html b/help/help/html/memory.html index 81190a9..7d6fb85 100755 --- a/help/help/html/memory.html +++ b/help/help/html/memory.html @@ -80,7 +80,7 @@ arguments, but you must put the jvl file first, e.g.
       /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl /path/to/alignments/myalignment.fa
Alternatively, you can use the standard Jalview command line arguments with or without the jvl file (first), e.g.
-       /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl -open http://www.jalview.org/examples/jpred_msa.fasta -annotations http://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal
You can use command line arguments to control memory + /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl -open https://www.jalview.org/examples/jpred_msa.fasta -annotations https://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal
You can use command line arguments to control memory settings in Windows and macOS too:
In Windows you must use, e.g.
       \PATH_TO_JALVIEW\Jalview.exe %HOMEPATH%\mymemorysetting.jvl -open %HOMEPATH%\myalignment.fa
In macOS you can use the macOS open command like this:
@@ -90,7 +90,17 @@
     
  • Maximum memory limit
    Since 2.11.1.0, Jalview's configuration includes a 'maximum memory limit':
    jalview.jvmmemmax = 32G
    - Adjusting this default (via a JVL file, above) will allow larger amounts of memory to be allocated to Jalview in connjunction with the jalview.jvmmempc setting. + Adjusting this default (via a JVL file, above) will allow larger amounts (or can limit the amount) of memory to be allocated to Jalview in conjunction with the jalview.jvmmempc setting. +

    +
  • +
  • Command line arguments when using the executable jar (jalview-all.jar) or jalview.bin.Launcher
    + If you are using the Jalview standalone executable jar (usually named jalview-all-....jar with a Jalview and Java version designation) or using jalview.bin.Launcher to start Jalview, + then you can set the jvmmempc and jvmmemmax values using application command line arguments -jvmmempc=PERCENT + and -jvmmemmax=MAXMEMORY respectively. PERCENT should be an integer between 1 and 100, and MAXMEMORY should be an amount of memory in bytes, or you can append a "k", "m", "g", or "t" to use units of kilobytes, megabytes, gigabytes or terabytes, e.g. +
    java -jar jalview-all-2.11.1.0-j1.8.jar -jvmmempc=50 -jvmmemmax=20g
    + (this example will launch Jalview with a maximum heap size of the smaller of 20GB or 50% of physical memory detected). +
    The default value for jvmmempc is 90, whilst the default value for jvmmemmax is 32g if Jalview can determine a total physical memory size of the host system, and a more cautious 8g if Jalview is unable to determine a total physical memory size. +

  • Directly opening Jalview with a JVM
    Launching Jalview directly with a JVM is diff --git a/help/help/html/releases.html b/help/help/html/releases.html index 5b6180d..d402a12 100755 --- a/help/help/html/releases.html +++ b/help/help/html/releases.html @@ -57,6 +57,191 @@ li:before {
    2.11.1.1
    + 17/09/2020
    + +
      +
    • + Shift+arrow keys navigate to next gap or + residue in cursor mode +
    • +
    • + Support import of VCF 4.3 by updating + HTSJDK from 2.12 to 2.23 +
    • +
    • + IntervalStore library updated to v.1.1: + optimisations and improvements suggested by Bob Hanson and + improved compatibility with JalviewJS +
    • +
    • + Retrieve GZipped stockholm formatted + alignments from Pfam and Rfam +
    • +
    • + Recognise GZipped content for URLs and File + import (no longer based on .gz extension) +
    • +
    • + Updated Spanish Translation for 2.11.1 +
    • +
    • + Migrate EMBL record retrieval to use latest + ENA Browser (https://www.ebi.ac.uk/ena/browser/home) and + EMBL flat file +
    • +
    • + Improved warning messages, debug logging + and fixed Retry action when Jalview encounters errors when + saving or making backup files. +
    • +
    • + Enhanced Jalview Java Console: +
        +
      • Jalview's logging level can be configured
      • +
      • Copy to Clipboard Buttion
      • +
      +
    • +
    • + Improved support for Hi-DPI (4K) screens + when running on Linux (Requires Java 11+) +
    • +
    Launching Jalview +
      +
    • + Configure Jalview Desktop's look and feel + through a system property +
    • +
    • + Improved built-in documentation and command + line help for configuring Jalview's memory +
    • +
    + + +
      +
    • + Conservation and Quality tracks are shown + but not calculated and no protein or DNA score models are + available for tree/PCA calculation when launched with + Turkish language locale +
    • +
    • + Escape does not clear highlights on the + alignment (Since Jalview 2.10.3) +
    • +
    • + Alt+Left or Right arrow in cursor mode + doesn't slide selected sequences, just sequence under cursor +
    • +
    • + Alt+Up/Down in cursor mode doesn't move + sequence under the cursor +
    • +
    • + Peptide-to-CDS tracking broken when + multiple EMBL gene products shown for a single contig +
    • +
    • + Errors encountered when processing variants + from VCF files yield "Error processing VCF: Format specifier + '%s'" on the console +
    • +
    • + Count of features not shown can be wrong + when there are both local and complementary features mapped + to the position under the cursor +
    • +
    • + Sequence ID for reference sequence is + clipped when Right align Sequence IDs enabled +
    • +
    • + Slider with negative range values not + rendered correctly in VAqua4 (Since 2.10.4) +
    • +
    • + Single quotes not displayed correctly in + internationalised text for some messages and log output +
    • +
    • + Find doesn't report matches that span + hidden gapped columns +
    • +
    • + Resolved memory leaks in Tree and PCA + panels, Alignment viewport and annotation renderer. +
    • +
    • + Jalview ignores file format parameter + specifying output format when exporting an alignment via the + command line +
    • +
    • + Windows 10: For a minority of users, if + backups are not enabled, Jalview sometimes fails to + overwrite an existing file and raises a warning dialog. (in + 2.11.0, and 2.11.1.0, the workaround is to try to save the + file again, and if that fails, delete the original file and + save in place.) +
    • +
    • + Cannot process alignments from HTTPS urls + via command line +
    • +
    • + References to http://www.jalview.org in + program and documentation +
    • +
    Launching Jalview +
      +
    • + Jalview application fails when launched the + first time for a version that has different jars to the + previous launched version. +
    • +
    Developing Jalview +
      +
    • + Fixed issue with cleaning up old coverage + data, causing cloverReport gradle task to fail with an + OutOfMemory error. +
    • +
    • + Migrated the Jalview Version Checker to + monitor the release channel +
    • +
    New Known defects +
      +
    • + CDS shown in result of submitting proteins + in a CDS/Protein alignment to a web service is wrong when + proteins share a common transcript sequence (e.g. + genome of RNA viruses) +
    • +
    • + Co-located features exported and re-imported + are ordered differently when shown on alignment and in + tooltips. (Also affects v2.11.1.0) +
    • +
    • + Drag and drop of alignment file onto + alignment window when in a HiDPI scaled mode in Linux only + works for the top left quadrant of the alignment window +
    • +
    • + Stale build data in jalview standalone jar + builds (only affects 2.11.1.1 branch) +
    • +
    • + Sequence ID colourscheme not re-applied + when alignment view restored from project (since Jalview 2.11.0) +
    • +
    + + + + 2.11.1.0
    22/04/2020
    diff --git a/help/help/html/webServices/AACon.html b/help/help/html/webServices/AACon.html index 5cec67d..52cddb2 100644 --- a/help/help/html/webServices/AACon.html +++ b/help/help/html/webServices/AACon.html @@ -32,7 +32,7 @@ Function, and Genetics
    43(2): 227-241. PubMed or available on the Valdar + href="http://valdarlab.unc.edu/publications">Valdar Group publications page), but the SMERFs score was developed later and described by Manning et al. in 2008 (BMC diff --git a/help/help/html/webServices/msaclient.html b/help/help/html/webServices/msaclient.html index d627c66..472bda0 100644 --- a/help/help/html/webServices/msaclient.html +++ b/help/help/html/webServices/msaclient.html @@ -58,7 +58,7 @@
    • Clustal Omega (version 1.2.4)
    • ClustalW (version 2.1)
    • -
    • Mafft (version 7.310)
    • +
    • Mafft (version 7.310)
    • Muscle (version 3.8.31)
    • T-coffee (version 11.00.8cbe486)
    • Probcons (version 1.12)
    • diff --git a/help/help/html/webServices/newsreader.html b/help/help/html/webServices/newsreader.html index c3c1d3f..2e8b69a 100644 --- a/help/help/html/webServices/newsreader.html +++ b/help/help/html/webServices/newsreader.html @@ -25,7 +25,7 @@

      The Jalview Desktop RSS News Reader
      The Jalview Desktop includes a built in news reader for the Jalview + href="https://www.jalview.org/feeds/desktop/rss">Jalview Desktop News Channel.

      @@ -48,9 +48,9 @@

      The Jalview news reader was introduced in Jalview + href="https://www.jalview.org/releaseHistory.html#Jalview2.7">Jalview version 2.7. Its implementation is based on JSwingReader. + href="https://jswingreader.sourceforge.net/">JSwingReader.


      If you need to prevent the news-reader opening, then add the diff --git a/help/help/html/whatsNew.html b/help/help/html/whatsNew.html index 0f0c7f1..2e18bae 100755 --- a/help/help/html/whatsNew.html +++ b/help/help/html/whatsNew.html @@ -24,58 +24,62 @@

      - Jalview 2.11.1.0 -

      -

      - Jalview 2.11.1.0 is the first minor release for the 2.11 series. - Along with a number of critical bug fixes and improvements it brings - new functionality for mapping sequence features between CDS and - Protein alignments. It is also the first release made under a new four - number versioning scheme, which will allow us to keep track of - patches and bug fixes. + Jalview 2.11.1.1

      +

      Jalview 2.11.1.1 is the first patch release for Jalview version + 2.11.1. In addition to fixes for some critical bugs, it also + contains a handful of new features suggested by the Jalview + community.

      +
        +
      • Shift+arrow keys navigate to next gap or residue in cursor + mode (enable with F2)
      • +
      • Support import of VCF 4.3 by updating HTSJDK from 2.12 to + 2.23
      • +
      • Improved recognition of GZipped files from local disk or + retrieved via the web
      • +
      • EMBL and EMBL CDS database records retrieved from the + European Nucleotide Archive's Data API as 'EMBL Flatfile' records
      • +
      • Improved Java Console and + logging to help track down problems +
      • +
      • Improved support for Hi-DPI (4K) screens when running on + Linux (Requires Java 11+)
      • +
      +

      Critical bug fixes include

        -
      • Virtual Features
        In previous - versions of Jalview, specific nucleotide sequence features such as - genomic variants and exons were transferred to protein products on - import. Jalview 2.11.1 instead provides 'virtual features' that - can be enabled and overlaid on linked CDS/Protein views via their - Sequence - Features dialog. This allows more analyses of nucleotide and - peptide sequence features on alignments in a more flexible and - memory efficient way than in earlier versions.
        - Note: Virtual features work best when variants are - annotated with CSQ fields. Please see this - Groovy script workaround if you are working with VCF files - without CSQ fields. -
      • -
      • Improved VCF data import
        Standard attributes for - filtering variants (e.g. position, QUAL field etc) are now - extracted from VCF files. This new feature was suggested by a user - at the Jalview booth during ISMB 2019.
      • -
      • Extended feature attributes are exported - in GFF3
        Complex attributes from VCF files can be exported - and imported via GFF3
      • -
      • Updated Jalview Installer and Launcher
        Jalview's - installation packages are now built with Install4j 8, which brings - better support for Linux and improved control of file - associations. New parameters on the - Jalview launcher allow an upper memory limit to be specified via - a Jalview launch file, to prevent it from hogging your system.
      • +
      • Jalview runs correctly when launched with Turkish language + settings
      • +
      • Peptide-to-CDS tracking broken when multiple EMBL gene + products shown for a single contig (such as viral genomes)
      • +
      • Errors encountered when processing variants from VCF files + yield "Error processing VCF: Format specifier '%s'" on the console
      • +
      • Count of features not shown can be wrong when there are + both DNA and Protein features mapped to the position under + the cursor
      • +
      • Sequence ID for reference sequence is clipped when Right + align Sequence IDs enabled
      • +
      • Find doesn't report matches that span hidden gapped columns
      • +
      • Jalview ignores file format parameter specifying output + format when exporting an alignment via the command line

      - See the 2.11.1.0 - release notes for full details of bugs fixed and new known issues. + For the full release notes, see the + Jalview 2.11.1.1 release notes.

      - JalviewJS News
      With the release of Jalview 2.11.1.0, - the team are now focused on bringing JalviewJS to full production. - To follow our progress take a look at http://www.jalview.org/jalview-js/ - and follow updates on our new JalviewJS - Releases github repository. + Known Issues

      +
        +
      • We've had reports from a small number of windows 10 users + who see a warning dialog pop up when Jalview tries to save a new + version of an existing file. If you are affected by this bug and + this latest version of Jalview doesn't fix it, please let us know!
      • +
      • Co-located features exported and re-imported are ordered + differently when shown on alignment and in tooltips. (Also affects + v2.11.1.0)
      • +
      • Drag and drop of alignment file onto alignment window when + in a HiDPI scaled mode in Linux only works for the top left + quadrant of the alignment window
      • +
      diff --git a/j11digestonly/htsjdk-2.12.0.jar b/j11digestonly/htsjdk-2.12.0.jar new file mode 100644 index 0000000..bcf201f Binary files /dev/null and b/j11digestonly/htsjdk-2.12.0.jar differ diff --git a/j11digestonly/intervalstore-v1.0.jar b/j11digestonly/intervalstore-v1.0.jar new file mode 100644 index 0000000..668b543 Binary files /dev/null and b/j11digestonly/intervalstore-v1.0.jar differ diff --git a/j11lib/getdown-core.jar b/j11lib/getdown-core.jar index 9c0f96e..c927dae 100644 Binary files a/j11lib/getdown-core.jar and b/j11lib/getdown-core.jar differ diff --git a/j11lib/htsjdk-2.12.0.jar b/j11lib/htsjdk-2.12.0.jar deleted file mode 100644 index 1df12b2..0000000 Binary files a/j11lib/htsjdk-2.12.0.jar and /dev/null differ diff --git a/j11lib/htsjdk-2.23.0.jar b/j11lib/htsjdk-2.23.0.jar new file mode 100644 index 0000000..bcf201f Binary files /dev/null and b/j11lib/htsjdk-2.23.0.jar differ diff --git a/j11lib/intervalstore-v1.0.jar b/j11lib/intervalstore-v1.0.jar deleted file mode 100644 index 4f6101c..0000000 Binary files a/j11lib/intervalstore-v1.0.jar and /dev/null differ diff --git a/j11lib/intervalstore-v1.1.jar b/j11lib/intervalstore-v1.1.jar new file mode 100644 index 0000000..668b543 Binary files /dev/null and b/j11lib/intervalstore-v1.1.jar differ diff --git a/j8digestonly/htsjdk-2.12.0.jar b/j8digestonly/htsjdk-2.12.0.jar new file mode 100644 index 0000000..bcf201f Binary files /dev/null and b/j8digestonly/htsjdk-2.12.0.jar differ diff --git a/j8digestonly/intervalstore-v1.0.jar b/j8digestonly/intervalstore-v1.0.jar new file mode 100644 index 0000000..668b543 Binary files /dev/null and b/j8digestonly/intervalstore-v1.0.jar differ diff --git a/j8lib/getdown-core.jar b/j8lib/getdown-core.jar index 9c0f96e..c927dae 100644 Binary files a/j8lib/getdown-core.jar and b/j8lib/getdown-core.jar differ diff --git a/j8lib/htsjdk-2.12.0.jar b/j8lib/htsjdk-2.12.0.jar deleted file mode 100644 index 1df12b2..0000000 Binary files a/j8lib/htsjdk-2.12.0.jar and /dev/null differ diff --git a/j8lib/htsjdk-2.23.0.jar b/j8lib/htsjdk-2.23.0.jar new file mode 100644 index 0000000..bcf201f Binary files /dev/null and b/j8lib/htsjdk-2.23.0.jar differ diff --git a/j8lib/intervalstore-v1.0.jar b/j8lib/intervalstore-v1.0.jar deleted file mode 100644 index 4f6101c..0000000 Binary files a/j8lib/intervalstore-v1.0.jar and /dev/null differ diff --git a/j8lib/intervalstore-v1.1.jar b/j8lib/intervalstore-v1.1.jar new file mode 100644 index 0000000..668b543 Binary files /dev/null and b/j8lib/intervalstore-v1.1.jar differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index a4b24ed..496fad0 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -377,7 +377,7 @@ label.example = Example label.example_param = Example: {0} label.select_file_format_before_saving = You must select a file format before saving! label.file_format_not_specified = File format not specified -label.couldnt_save_file = Couldn't save file: {0} +label.couldnt_save_file = Couldn''t save file: {0} label.error_saving_file = Error Saving File label.remove_from_default_list = Remove from default list? label.remove_user_defined_colour = Remove user defined colour @@ -406,11 +406,11 @@ label.pdb_entries_couldnt_be_retrieved = The following pdb entries could not be label.couldnt_load_file = Couldn't load file label.couldnt_find_pdb_id_in_file = Couldn't find a PDB id in the file supplied. Please enter an Id to identify this structure. label.no_pdb_id_in_file = No PDB Id in File -label.couldnt_read_pasted_text = Couldn't read the pasted text {0} +label.couldnt_read_pasted_text = Couldn''t read the pasted text {0} label.error_parsing_text = Error parsing text label.input_alignment_from_url = Input Alignment From URL label.input_alignment = Input Alignment -label.couldnt_import_as_vamsas_session = Couldn't import {0} as a new vamsas session. +label.couldnt_import_as_vamsas_session = Couldn''t import {0} as a new vamsas session. label.vamsas_document_import_failed = Vamsas Document Import Failed label.couldnt_locate = Could not locate {0} label.url_not_found = URL not found @@ -855,7 +855,7 @@ label.invalid_name = Invalid name label.set_proxy_settings = Please set up your proxy settings in the 'Connections' tab of the Preferences window label.proxy_authorization_failed = Proxy Authorization Failed label.internal_jalview_error = Internal Jalview Error -label.secondary_structure_prediction_service_couldnt_be_located = The Secondary Structure Prediction Service named {0} at {1} couldn't be located. +label.secondary_structure_prediction_service_couldnt_be_located = The Secondary Structure Prediction Service named {0} at {1} couldn''t be located. label.service_called_is_not_msa_service = The Service called \n{0}\nis not a \nMultiple Sequence Alignment Service\! label.msa_service_is_unknown = The Multiple Sequence Alignment Service named {0} is unknown label.service_called_is_not_seq_search_service = The Service called \n{0}\nis not a \nSequence Search Service\! @@ -985,7 +985,7 @@ error.cannot_set_arguments_to_jabaws_param_set = Cannot set arguments to a JabaW error.implementation_error_runner_config_not_available = Implementation Error: Runner Config not available for a JABAWS service of type {0} ({1}) error.implementation_error_cannot_handle_jaba_param = Implementation Error: Cannot handle Jaba parameter object {0} error.implementation_error_attempt_to_delete_service_preset = Implementation error: Attempt to delete a service preset! -error.implementation_error_cannot_locate_oldname_presetname = Implementation error: Can't locate either oldname ({0}) or presetName ({1}in the datastore!" +error.implementation_error_cannot_locate_oldname_presetname = Implementation error: Can''t locate either oldname ({0}) or presetName ({1}in the datastore!" error.implementation_error_jabaws_param_set_only_handled_by = Implementation error: JabaWsParamSets can only be handled by JabaParamStore error.cannot_set_source_file_for = Cannot set source file for {0} error.mismatch_service_instance_preset = Probable mismatch between service instance and preset! @@ -993,7 +993,7 @@ error.cannot_set_params_for_ws_preset = Cannot set Parameters for a Jaba Web ser error.implementation_error_can_only_instantiate_jaba_param_sets = Implementation error: Can only instantiate Jaba parameter sets error.no_aacon_service_found = No AACon service found error.implementation_error_couldnt_copy_value_constraint = Implementation error: could not copy ValueConstrain! -error.couldnt_encode_as_utf8 = Couldn't encode {0} as UTF-8. +error.couldnt_encode_as_utf8 = Couldn''t encode {0} as UTF-8. error.tree_inputtype_not_yet_implemented = Tree InputType not yet implemented error.implementation_error_need_to_have_httpresponse = Implementation Error: need to have an HttpResponse to process error.dbrefsource_implementation_exception =DBRefSource Implementation Exception @@ -1049,18 +1049,18 @@ error.implementation_error_reset_called_for_invalid_source = Implementation Erro exception.number_of_residues_in_query_sequence_differ_from_prediction = Number of residues in {0} supposed query sequence ({1}\n{2})\ndiffer from number of prediction sites in prediction ({3}) label.mapped = mapped exception.jpredconcide_entry_has_unexpected_number_of_columns = JPredConcise: Entry ({0}) has an unexpected number of columns -exception.couldnt_parse_concise_annotation_for_prediction = Couldn't parse concise annotation for prediction profile.\n{0} +exception.couldnt_parse_concise_annotation_for_prediction = Couldn''t parse concise annotation for prediction profile.\n{0} exception.newfile = NewickFile\: {0}\n label.no_tree_read_in = No Tree read in -exception.rnaml_couldnt_access_datasource = Couldn't access datasource ({0}) -exception.ranml_couldnt_process_data = Couldn't process data as RNAML file ({0}) +exception.rnaml_couldnt_access_datasource = Couldn''t access datasource ({0}) +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.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} -exception.couldnt_store_sequence_mappings = Couldn't store sequence mappings for {0} +exception.couldnt_store_sequence_mappings = Couldn''t store sequence mappings for {0} exception.matrix_too_many_iteration = Too many iterations in {0} (max is {1}) exception.browser_not_found = Exception in finding browser: {0} exception.browser_unable_to_locate = Unable to locate browser: {0} @@ -1071,7 +1071,6 @@ exception.unable_to_create_internet_config = Unable to create an Internet Config exception.invocation_target_calling_url = InvocationTargetException while calling openURL: {0} exception.illegal_access_calling_url = IllegalAccessException while calling openURL: {0} exception.interrupted_launching_browser = InterruptedException while launching browser: {0} -exception.ebiembl_retrieval_failed_on = EBI EMBL XML retrieval failed on {0}:{1} exception.no_pdb_records_for_chain = No PDB Records for {0} chain {1} exception.unexpected_handling_rnaml_translation_for_pdb = Unexpected exception when handling RNAML translation of PDB data exception.couldnt_recover_sequence_properties_for_alignment = Couldn't recover sequence properties for alignment @@ -1094,7 +1093,7 @@ warn.service_not_supported = Service not supported! warn.input_is_too_big = Input is too big! warn.invalid_job_param_set = Invalid job parameter set! warn.oneseq_msainput_selection = The current selection only contains a single sequence. Do you want to submit all sequences for alignment instead ? -info.job_couldnt_be_run_server_doesnt_support_program = Job could not be run because the server doesn't support this program.\n{0} +info.job_couldnt_be_run_server_doesnt_support_program = Job could not be run because the server doesn''t support this program.\n{0} info.job_couldnt_be_run_exceeded_hard_limit = Job could not be run because it exceeded a hard limit on the server.\n{0} info.job_couldnt_be_run_incorrect_param_setting = Job could not be run because some of the parameter settings are not supported by the server.\n{0}\nPlease check to make sure you have used the correct parameter set for this service\!\n info.no_jobs_ran = No jobs ran @@ -1346,6 +1345,7 @@ label.backupfiles_confirm_save_file = Confirm save file label.backupfiles_confirm_save_file_backupfiles_roll_wrong = Something possibly went wrong with the backups of this file. label.backupfiles_confirm_save_new_saved_file_ok = The new saved file seems okay. label.backupfiles_confirm_save_new_saved_file_not_ok = The new saved file might not be okay. +label.continue_operation = Continue operation? label.backups = Backups label.backup = Backup label.backup_files = Backup Files @@ -1412,3 +1412,9 @@ label.include_linked_features = Include {0} features label.include_linked_tooltip = Include visible {0} features
      converted to local sequence coordinates label.features_not_shown = {0} feature(s) not shown label.no_features_to_sort_by = No features to sort by +label.log_level = Log level +label.log_level_tooltip = Temporarily set the log level for this console. The log level will revert to {0} when this Java console is closed. +label.copy_to_clipboard = Copy to clipboard +label.copy_to_clipboard_tooltip = Copy all of the log text in this console to the system clipboard +label.ignore_hidden = Ignore hidden columns +label.ignore_hidden_tooltip = Ignore any characters in hidden columns when matching diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 4e0fb1f..6364f5e 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -211,7 +211,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 las secuencias +label.show_sequence_features = Mostrar las características de secuencia label.nucleotide = Nucleótido label.to_new_alignment = A nuevo alineamiento label.to_this_alignment = Añadir a este alineamiento @@ -693,7 +693,12 @@ label.annotations_for_params = Anotaciones de - {0} label.generating_features_for_params = Generando características de - {0} label.generating_annotations_for_params = Generando anotaciones de - {0} label.varna_params = VARNA - {0} -label.sequence_feature_settings = Configuración de las características de la secuencia +label.sequence_feature_settings = Configuración de las características de secuencia +label.sequence_feature_settings_for = Configuración de las características de secuencia para {0} +label.sequence_feature_settings_for_view = Configuración de las características de secuencia para vista "{0}" +label.sequence_feature_settings_for_CDS_and_Protein = Configuración de las características de secuencia para CDS y Proteína +action.undo_changes_to_feature_settings = Deshacer todos los cambios no aplicados +action.undo_changes_to_feature_settings_and_close_the_dialog = Deshacer cambios pendientes, cerrar diálogo label.pairwise_aligned_sequences = Secuencias alineadas a pares label.original_data_for_params = Datos originales de {0} label.points_for_params = Puntos de {0} @@ -990,7 +995,6 @@ exception.unable_to_create_internet_config = Imposible crear una instancia de co exception.invocation_target_calling_url = InvocationTargetException mientras se invocaba openURL: {0} exception.illegal_access_calling_url = IllegalAccessException mientras se invocaba openURL: {0} exception.interrupted_launching_browser = InterruptedException mientras se lanzaba el navegador: {0} -exception.ebiembl_retrieval_failed_on = La recuperación de datos EBI EMBL XML ha fallado en {0}:{1} exception.no_pdb_records_for_chain = No se han encontrado registros {0} para la cadena {1} exception.unexpected_handling_rnaml_translation_for_pdb = Excepcion inesperada cuando se traducían a RNAML los datos PDB exception.couldnt_recover_sequence_properties_for_alignment = No es posible recuperar las propiedades de la secuencia para el alineamiento @@ -1191,6 +1195,7 @@ warn.urls_not_contacted=URLs que no pudieron ser contactados label.prot_alignment_colour=Color del Alineamiento Proteico info.associate_wit_sequence=Asociar con secuencia label.protein=Proteína +label.CDS=CDS warn.oneseq_msainput_selection=La selección actual sólo contiene una única secuencia. ¿Quieres enviar todas las secuencias para la alineación en su lugar? label.use_rnaview=Usar RNAView para estructura secondaria label.search_all=Introducir uno o más valores de búsqueda separados por punto y coma ";" (Nota: buscará en toda la base de datos PDB) @@ -1341,6 +1346,7 @@ label.backupfiles_confirm_save_file = Confirmar guardar archivo label.backupfiles_confirm_save_file_backupfiles_roll_wrong = Posiblemente algo está mal con los archivos de respaldos. label.backupfiles_confirm_save_new_saved_file_ok = El nuevo archivo guardado parece estar bien. label.backupfiles_confirm_save_new_saved_file_not_ok = El nuevo archivo guardado podría no estar bien. +label.continue_operation = ¿Continuar operación? label.backups = Respaldos label.backup = Respaldo label.backup_files = Archivos de respaldos @@ -1407,3 +1413,9 @@ label.include_linked_features = Incluir caracter label.include_linked_tooltip = Incluir características de {0}
      convertidas a coordenadas de secuencia local label.features_not_shown = {0} característica(s) no mostradas label.no_features_to_sort_by = No hay características para ordenar +label.log_level = Nivel del registro +label.log_level_tooltip = Establezca temporalmente el nivel de registro para esta consola. El nivel de registro volverá a {0} cuando se cierre esta consola de Java. +label.copy_to_clipboard = Copiar en el portapapeles +label.copy_to_clipboard_tooltip = Copie todo el texto de registro en esta consola al portapapeles del sistema +label.ignore_hidden = Ignorar columnas ocultas +label.ignore_hidden_tooltip = Ignorar caracteres en columnas ocultas diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java index 3cbef6d..c545c7f 100644 --- a/src/jalview/analysis/Finder.java +++ b/src/jalview/analysis/Finder.java @@ -23,17 +23,18 @@ package jalview.analysis; import jalview.api.AlignViewportI; import jalview.api.FinderI; import jalview.datamodel.AlignmentI; -import jalview.datamodel.Range; import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResults; import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import jalview.datamodel.VisibleContigsIterator; import jalview.util.Comparison; +import jalview.util.MapList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; -import java.util.Vector; import com.stevesoft.pat.Regex; @@ -50,7 +51,7 @@ public class Finder implements FinderI /* * sequences matched by id or description */ - private Vector idMatches; + private List idMatches; /* * the viewport to search over @@ -63,11 +64,24 @@ public class Finder implements FinderI private int sequenceIndex; /* - * column position in sequence to search from, base 0 - * - absolute column number including any hidden columns - * (position after start of last match for a repeat search) + * position offset in sequence to search from, base 0 + * (position after start of last match for a 'find next') */ - private int columnIndex; + private int residueIndex; + + /* + * the true sequence position of the start of the + * last sequence searched (when 'ignore hidden regions' does not apply) + */ + private int searchedSequenceStartPosition; + + /* + * when 'ignore hidden regions' applies, this holds the mapping from + * the visible sequence positions (1, 2, ...) to true sequence positions + */ + private MapList searchedSequenceMap; + + private String seqToSearch; /** * Constructor for searching a viewport @@ -78,33 +92,35 @@ public class Finder implements FinderI { this.viewport = av; this.sequenceIndex = 0; - this.columnIndex = -1; + this.residueIndex = -1; } @Override public void findAll(String theSearchString, boolean matchCase, - boolean searchDescription) + boolean searchDescription, boolean ignoreHidden) { /* * search from the start */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; - doFind(theSearchString, matchCase, searchDescription, true); + doFind(theSearchString, matchCase, searchDescription, true, + ignoreHidden); /* * reset to start for next search */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; } @Override public void findNext(String theSearchString, boolean matchCase, - boolean searchDescription) + boolean searchDescription, boolean ignoreHidden) { - doFind(theSearchString, matchCase, searchDescription, false); + doFind(theSearchString, matchCase, searchDescription, false, + ignoreHidden); if (searchResults.isEmpty() && idMatches.isEmpty()) { @@ -112,7 +128,7 @@ public class Finder implements FinderI * search failed - reset to start for next search */ sequenceIndex = 0; - columnIndex = -1; + residueIndex = -1; } } @@ -123,18 +139,19 @@ public class Finder implements FinderI * @param matchCase * @param searchDescription * @param findAll + * @param ignoreHidden */ protected void doFind(String theSearchString, boolean matchCase, - boolean searchDescription, boolean findAll) + boolean searchDescription, boolean findAll, boolean ignoreHidden) { + searchResults = new SearchResults(); + idMatches = new ArrayList<>(); + String searchString = matchCase ? theSearchString : theSearchString.toUpperCase(); Regex searchPattern = new Regex(searchString); searchPattern.setIgnoreCase(!matchCase); - searchResults = new SearchResults(); - idMatches = new Vector<>(); - SequenceGroup selection = viewport.getSelectionGroup(); if (selection != null && selection.getSize() < 1) { @@ -144,108 +161,202 @@ public class Finder implements FinderI AlignmentI alignment = viewport.getAlignment(); int end = alignment.getHeight(); - while (sequenceIndex < end) + getSequence(ignoreHidden); + + boolean found = false; + while ((!found || findAll) && sequenceIndex < end) { - SequenceI seq = alignment.getSequenceAt(sequenceIndex); - boolean found = findNextMatch(seq, searchString, searchPattern, - searchDescription); - if (found && !findAll) + found = findNextMatch(searchString, searchPattern, searchDescription, + ignoreHidden); + } + } + + /** + * Calculates and saves the sequence string to search. The string is restricted + * to the current selection region if there is one, and is saved with all gaps + * removed. + *

      + * If there are hidden columns, and option {@ignoreHidden} is selected, then + * only visible positions of the sequence are included, and a mapping is also + * constructed from the returned string positions to the true sequence + * positions. + *

      + * Note we have to do this each time {@code findNext} or {@code findAll} is + * called, in case the alignment, selection group or hidden columns have + * changed. In particular, if the sequence at offset {@code sequenceIndex} in + * the alignment is (no longer) in the selection group, search is advanced to + * the next sequence that is. + *

      + * Sets sequence string to the empty string if there are no more sequences (in + * selection group if any) at or after {@code sequenceIndex}. + *

      + * Returns true if a sequence could be found, false if end of alignment was + * reached + * + * @param ignoreHidden + * @return + */ + private boolean getSequence(boolean ignoreHidden) + { + AlignmentI alignment = viewport.getAlignment(); + if (sequenceIndex >= alignment.getHeight()) + { + seqToSearch = ""; + return false; + } + SequenceI seq = alignment.getSequenceAt(sequenceIndex); + SequenceGroup selection = viewport.getSelectionGroup(); + if (selection != null && !selection.contains(seq)) + { + if (!nextSequence(ignoreHidden)) { - return; + return false; } - if (!found) + seq = alignment.getSequenceAt(sequenceIndex); + } + + String seqString = null; + if (ignoreHidden) + { + seqString = getVisibleSequence(seq); + this.searchedSequenceStartPosition = 1; + } + else + { + int startCol = 0; + int endCol = seq.getLength() - 1; + this.searchedSequenceStartPosition = seq.getStart(); + if (selection != null) { - sequenceIndex++; - columnIndex = -1; + startCol = selection.getStartRes(); + endCol = Math.min(endCol, selection.getEndRes()); + this.searchedSequenceStartPosition = seq.findPosition(startCol); } + seqString = seq.getSequenceAsString(startCol, endCol + 1); } + + /* + * remove gaps; note that even if this leaves an empty string, we 'search' + * the sequence anyway (for possible match on name or description) + */ + String ungapped = AlignSeq.extractGaps(Comparison.GapChars, seqString); + this.seqToSearch = ungapped; + + return true; } /** - * Answers the start-end column range of the visible region of - * sequence starting at or after the given column. - * If there are no hidden columns, this just returns the remaining width of - * the sequence. The range is restricted to the current selection - * if there is one. Answers null if there are no visible columns at or after - * column. + * Returns a string consisting of only the visible residues of {@code seq} from + * alignment column {@ fromColumn}, restricted to the current selection region + * if there is one. + *

      + * As a side-effect, also computes the mapping from the true sequence positions + * to the positions (1, 2, ...) of the returned sequence. This is to allow + * search matches in the visible sequence to be converted to sequence positions. + * + * @param seq + * @return */ - protected Range getNextVisibleSequenceRegion(SequenceI sequence, - int column) + private String getVisibleSequence(SequenceI seq) { - int seqColStart = column; - int seqColEnd = sequence.getLength() - 1; - /* - * restrict search to (next) visible column region, - * in case there are hidden columns + * get start / end columns of sequence and convert to base 0 + * (so as to match the visible column ranges) */ - AlignmentI alignment = viewport.getAlignment(); - VisibleContigsIterator visibleRegions = alignment.getHiddenColumns() - .getVisContigsIterator(column, alignment.getWidth(), - false); - int[] visible = visibleRegions.hasNext() ? visibleRegions.next() : null; - if (visible == null) - { - columnIndex = seqColEnd + 1; - return null; - } - seqColStart = Math.max(seqColStart, visible[0]); - seqColEnd = Math.min(seqColEnd, visible[1]); + int seqStartCol = seq.findIndex(seq.getStart()) - 1; + int seqEndCol = seq.findIndex(seq.getStart() + seq.getLength() - 1) - 1; + Iterator visibleColumns = viewport.getViewAsVisibleContigs(true); + StringBuilder visibleSeq = new StringBuilder(seqEndCol - seqStartCol); + List fromRanges = new ArrayList<>(); - /* - * restrict search to selected region if there is one - */ - SequenceGroup selection = viewport.getSelectionGroup(); - if (selection != null) + while (visibleColumns.hasNext()) { - int selectionStart = selection.getStartRes(); - int selectionEnd = selection.getEndRes(); - if (selectionStart > seqColEnd || selectionEnd < seqColStart) + int[] range = visibleColumns.next(); + if (range[0] > seqEndCol) + { + // beyond the end of the sequence + break; + } + if (range[1] < seqStartCol) + { + // before the start of the sequence + continue; + } + String subseq = seq.getSequenceAsString(range[0], range[1] + 1); + String ungapped = AlignSeq.extractGaps(Comparison.GapChars, subseq); + visibleSeq.append(ungapped); + if (!ungapped.isEmpty()) { /* - * sequence region doesn't overlap selection region + * visible region includes at least one non-gap character, + * so add the range to the mapping being constructed */ - columnIndex = seqColEnd + 1; - return null; + int seqResFrom = seq.findPosition(range[0]); + int seqResTo = seqResFrom + ungapped.length() - 1; + fromRanges.add(new int[] { seqResFrom, seqResTo }); } - seqColStart = Math.max(seqColStart, selectionStart); - seqColEnd = Math.min(seqColEnd, selectionEnd); } - return new Range(seqColStart, seqColEnd); + /* + * construct the mapping + * from: visible sequence positions 1..length + * to: true residue positions of the alignment sequence + */ + List toRange = Arrays + .asList(new int[] + { 1, visibleSeq.length() }); + searchedSequenceMap = new MapList(fromRanges, toRange, 1, 1); + + return visibleSeq.toString(); } /** - * Finds the next match in the given sequence, starting at column at - * columnIndex. Answers true if a match is found, else false. If - * a match is found, columnIndex is advanced to the column after - * the start of the matched region, ready for a search from the next position. + * Advances the search to the next sequence in the alignment. Sequences not in + * the current selection group (if there is one) are skipped. The (sub-)sequence + * to be searched is extracted, gaps removed, and saved, or set to null if there + * are no more sequences to search. + *

      + * Returns true if a sequence could be found, false if end of alignment was + * reached * - * @param seq + * @param ignoreHidden + */ + private boolean nextSequence(boolean ignoreHidden) + { + sequenceIndex++; + residueIndex = -1; + + return getSequence(ignoreHidden); + } + + /** + * Finds the next match in the given sequence, starting at offset + * {@code residueIndex}. Answers true if a match is found, else false. + *

      + * If a match is found, {@code residueIndex} is advanced to the position after + * the start of the matched region, ready for the next search. + *

      + * If no match is found, {@code sequenceIndex} is advanced ready to search the + * next sequence. + * + * @param seqToSearch * @param searchString * @param searchPattern * @param matchDescription + * @param ignoreHidden * @return */ - protected boolean findNextMatch(SequenceI seq, String searchString, - Regex searchPattern, boolean matchDescription) + protected boolean findNextMatch(String searchString, + Regex searchPattern, boolean matchDescription, + boolean ignoreHidden) { - SequenceGroup selection = viewport.getSelectionGroup(); - if (selection != null && !selection.contains(seq)) - { - /* - * this sequence is not in the selection - advance to next sequence - */ - return false; - } - - if (columnIndex < 0) + if (residueIndex < 0) { /* * at start of sequence; try find by residue number, in sequence id, * or (optionally) in sequence description */ - if (doNonMotifSearches(seq, searchString, searchPattern, + if (doNonMotifSearches(searchString, searchPattern, matchDescription)) { return true; @@ -255,83 +366,66 @@ public class Finder implements FinderI /* * search for next match in sequence string */ - int end = seq.getLength(); - while (columnIndex < end) + int end = seqToSearch.length(); + while (residueIndex < end) { - if (searchNextVisibleRegion(seq, searchPattern)) + boolean matched = searchPattern.searchFrom(seqToSearch, residueIndex); + if (matched) { - return true; + if (recordMatch(searchPattern, ignoreHidden)) + { + return true; + } + } + else + { + residueIndex = Integer.MAX_VALUE; } - } - return false; - } - - /** - * Searches the sequence, starting from columnIndex, and adds the - * next match (if any) to searchResults. The search is restricted - * to the next visible column region, and to the selection region - * if there is one. Answers true if a match is added, else false. - * - * @param seq - * @param searchPattern - * @return - */ - protected boolean searchNextVisibleRegion(SequenceI seq, Regex searchPattern) - { - Range visible = getNextVisibleSequenceRegion(seq, columnIndex); - if (visible == null) - { - return false; - } - String seqString = seq.getSequenceAsString(visible.start, visible.end + 1); - String noGaps = AlignSeq.extractGaps(Comparison.GapChars, seqString); - - if (searchPattern.search(noGaps)) - { - int sequenceStartPosition = seq.findPosition(visible.start); - recordMatch(seq, searchPattern, sequenceStartPosition); - return true; - } - else - { - /* - * no match - advance columnIndex past this visible region - * so the next visible region (if any) is searched next - */ - columnIndex = visible.end + 1; } + nextSequence(ignoreHidden); return false; } /** * Adds the match held in the searchPattern Regex to the * searchResults, unless it is a subregion of the last match - * recorded. columnIndex is advanced to the position after the + * recorded. residueIndex is advanced to the position after the * start of the matched region, ready for the next search. Answers true if a * match was added, else false. + *

      + * Matches that lie entirely within hidden regions of the alignment are not + * added. * - * @param seq * @param searchPattern - * @param firstResiduePosition + * @param ignoreHidden * @return */ - protected boolean recordMatch(SequenceI seq, Regex searchPattern, - int firstResiduePosition) + protected boolean recordMatch(Regex searchPattern, boolean ignoreHidden) { + SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex); + /* - * get start/end of the match in sequence coordinates + * convert start/end of the match to sequence coordinates */ int offset = searchPattern.matchedFrom(); - int matchStartPosition = firstResiduePosition + offset; + int matchStartPosition = this.searchedSequenceStartPosition + offset; int matchEndPosition = matchStartPosition + searchPattern.charsMatched() - 1; /* - * update columnIndex to next column after the start of the match + * update residueIndex to next position after the start of the match * (findIndex returns a value base 1, columnIndex is held base 0) */ - columnIndex = seq.findIndex(matchStartPosition); + residueIndex += offset + 1; + + /* + * return false if the match is entirely in a hidden region + */ + if (allHidden(seq, matchStartPosition, matchEndPosition)) + { + return false; + } /* * check that this match is not a subset of the previous one (JAL-2302) @@ -343,7 +437,7 @@ public class Finder implements FinderI if (lastMatch == null || !lastMatch.contains(seq, matchStartPosition, matchEndPosition)) { - searchResults.addResult(seq, matchStartPosition, matchEndPosition); + addMatch(seq, matchStartPosition, matchEndPosition, ignoreHidden); return true; } @@ -351,6 +445,60 @@ public class Finder implements FinderI } /** + * Adds one match to the stored list. If hidden residues are being skipped, then + * the match may need to be split into contiguous positions of the sequence (so + * it does not include skipped residues). + * + * @param seq + * @param matchStartPosition + * @param matchEndPosition + * @param ignoreHidden + */ + private void addMatch(SequenceI seq, int matchStartPosition, + int matchEndPosition, boolean ignoreHidden) + { + if (!ignoreHidden) + { + /* + * simple case + */ + searchResults.addResult(seq, matchStartPosition, matchEndPosition); + return; + } + + /* + * get start-end contiguous ranges in underlying sequence + */ + int[] truePositions = searchedSequenceMap + .locateInFrom(matchStartPosition, matchEndPosition); + searchResults.addResult(seq, truePositions); + } + + /** + * Returns true if all residues are hidden, else false + * + * @param seq + * @param fromPos + * @param toPos + * @return + */ + private boolean allHidden(SequenceI seq, int fromPos, int toPos) + { + if (!viewport.hasHiddenColumns()) + { + return false; + } + for (int res = fromPos; res <= toPos; res++) + { + if (isVisible(seq, res)) + { + return false; + } + } + return true; + } + + /** * Does searches other than for residue patterns. Currently this includes *

        *
      • find residue by position (if search string is a number)
      • @@ -359,24 +507,29 @@ public class Finder implements FinderI *
      * Answers true if a match is found, else false. * - * @param seq * @param searchString * @param searchPattern * @param includeDescription * @return */ - protected boolean doNonMotifSearches(SequenceI seq, String searchString, + protected boolean doNonMotifSearches(String searchString, Regex searchPattern, boolean includeDescription) { + SequenceI seq = viewport.getAlignment().getSequenceAt(sequenceIndex); + /* * position sequence search to start of sequence */ - columnIndex = 0; - - if (searchForResidueNumber(seq, searchString)) + residueIndex = 0; + try { - return true; + int res = Integer.parseInt(searchString); + return searchForResidueNumber(seq, res); + } catch (NumberFormatException ex) + { + // search pattern is not a number } + if (searchSequenceName(seq, searchPattern)) { return true; @@ -402,7 +555,7 @@ public class Finder implements FinderI String desc = seq.getDescription(); if (desc != null && searchPattern.search(desc) && !idMatches.contains(seq)) { - idMatches.addElement(seq); + idMatches.add(seq); return true; } return false; @@ -421,45 +574,56 @@ public class Finder implements FinderI { if (searchPattern.search(seq.getName()) && !idMatches.contains(seq)) { - idMatches.addElement(seq); + idMatches.add(seq); return true; } return false; } /** - * Tries to interpret the search string as a residue position, and if valid, - * adds the position to the search results and returns true, else answers - * false + * If the residue position is valid for the sequence, and in a visible column, + * adds the position to the search results and returns true, else answers false. + * + * @param seq + * @param resNo + * @return */ - protected boolean searchForResidueNumber(SequenceI seq, String searchString) + protected boolean searchForResidueNumber(SequenceI seq, int resNo) { - try + if (seq.getStart() <= resNo && seq.getEnd() >= resNo) { - int res = Integer.parseInt(searchString); - if (seq.getStart() <= res && seq.getEnd() >= res) + if (isVisible(seq, resNo)) { - searchResults.addResult(seq, res, res); + searchResults.addResult(seq, resNo, resNo); return true; } - } catch (NumberFormatException ex) - { } return false; } - /* (non-Javadoc) - * @see jalview.analysis.FinderI#getIdMatch() + /** + * Returns true if the residue is in a visible column, else false + * + * @param seq + * @param res + * @return */ + private boolean isVisible(SequenceI seq, int res) + { + if (!viewport.hasHiddenColumns()) + { + return true; + } + int col = seq.findIndex(res); // base 1 + return viewport.getAlignment().getHiddenColumns().isVisible(col - 1); // base 0 + } + @Override - public Vector getIdMatches() + public List getIdMatches() { return idMatches; } - /* (non-Javadoc) - * @see jalview.analysis.FinderI#getSearchResults() - */ @Override public SearchResultsI getSearchResults() { diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 785dd14..56d25d5 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -38,6 +38,7 @@ import jalview.viewmodel.ViewportRanges; import java.awt.Color; import java.awt.Font; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -528,4 +529,16 @@ public interface AlignViewportI extends ViewStyleI * - a group defined on sequences in the alignment held by the view */ void addSequenceGroup(SequenceGroup sequenceGroup); + + /** + * Returns an interator over the [start, end] column positions of the visible + * regions of the alignment + * + * @param selectedRegionOnly + * if true, and the view has a selection region, + * then only the intersection of visible columns + * with the selection region is returned + * @return + */ + Iterator getViewAsVisibleContigs(boolean selectedRegionOnly); } diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java index 8aa2858..fff3b38 100644 --- a/src/jalview/api/FeatureRenderer.java +++ b/src/jalview/api/FeatureRenderer.java @@ -297,4 +297,11 @@ public interface FeatureRenderer * @return */ MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence, int pos); + + /** + * Sends a message to let any registered parties know that something about + * feature rendering has changed + */ + void notifyFeaturesChanged(); + } diff --git a/src/jalview/api/FeatureSettingsModelI.java b/src/jalview/api/FeatureSettingsModelI.java index c0fc523..c8a835a 100644 --- a/src/jalview/api/FeatureSettingsModelI.java +++ b/src/jalview/api/FeatureSettingsModelI.java @@ -35,7 +35,8 @@ public interface FeatureSettingsModelI extends Comparator // interface, simplifying instantiating classes /** - * Answers true if the specified feature type is displayed + * Answers true if the specified feature type is to be displayed, false if no + * preference * * @param type * @return @@ -43,6 +44,15 @@ public interface FeatureSettingsModelI extends Comparator boolean isFeatureDisplayed(String type); /** + * Answers true if the specified feature type is to be hidden, false if no + * preference + * + * @param type + * @return + */ + boolean isFeatureHidden(String type); + + /** * Answers true if the specified feature group is displayed * * @param group diff --git a/src/jalview/api/FeaturesDisplayedI.java b/src/jalview/api/FeaturesDisplayedI.java index e69785f..9387e3f 100644 --- a/src/jalview/api/FeaturesDisplayedI.java +++ b/src/jalview/api/FeaturesDisplayedI.java @@ -39,6 +39,8 @@ public interface FeaturesDisplayedI void setVisible(String featureType); + void setHidden(String featureType); + /** * Sets all the specified feature types to visible. Visibility of other * feature types is not changed. diff --git a/src/jalview/api/FinderI.java b/src/jalview/api/FinderI.java index 71b35ef..8f6232e 100644 --- a/src/jalview/api/FinderI.java +++ b/src/jalview/api/FinderI.java @@ -34,35 +34,48 @@ public interface FinderI /** * Performs a find for the given search string (interpreted as a regular * expression). Search may optionally be case-sensitive, and may optionally - * including match in sequence description (sequence id is always searched). - * If the viewport has an active selection, then the find is restricted to the - * selection region. Sequences matched by id or description can be retrieved - * by getIdMatches(), and matched residue patterns by getSearchResults(). + * including match in sequence description (sequence id is always searched). If + * the viewport has an active selection, then the find is restricted to the + * selection region. Sequences matched by id or description can be retrieved by + * getIdMatches(), and matched residue patterns by getSearchResults(). + *

      + * If {@code ignoreHidden} is true, then any residues in hidden columns are + * ignored (skipped) when matching, so for example pattern {@code KRT} would + * match sequence {@code KRqmT} (where {@code qm} are in hidden columns). + *

      + * Matches of entirely hidden patterns are not returned. Matches that span + * hidden regions on one or both sides may be returned. * * @param theSearchString * @param caseSensitive * @param searchDescription + * @param ignoreHidden * @return */ void findAll(String theSearchString, boolean caseSensitive, - boolean searchDescription); + boolean searchDescription, boolean ignoreHidden); /** * Finds the next match for the given search string (interpreted as a regular * expression), starting from the position after the last match found. Search * may optionally be case-sensitive, and may optionally including match in - * sequence description (sequence id is always searched). If the viewport has - * an active selection, then the find is restricted to the selection region. + * sequence description (sequence id is always searched). If the viewport has an + * active selection, then the find is restricted to the selection region. * Sequences matched by id or description can be retrieved by getIdMatches(), * and matched residue patterns by getSearchResults(). + *

      + * If {@code ignoreHidden} is true, any hidden residues are skipped (matches may + * span them). If false, they are included for matching purposes. In either + * cases, entirely hidden matches are not returned. * * @param theSearchString * @param caseSensitive * @param searchDescription + * @param ignoreHidden * @return */ void findNext(String theSearchString, boolean caseSensitive, - boolean searchDescription); + boolean searchDescription, boolean ignoreHidden); /** * Returns the (possibly empty) list of sequences matched on sequence name or diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index e9081b0..365f886 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -344,7 +344,7 @@ public class AlignmentPanel extends Panel int verticalOffset, boolean redrawOverview, boolean centre) { // do we need to scroll the panel? - if (results != null && results.getSize() > 0) + if (results != null && results.getCount() > 0) { AlignmentI alignment = av.getAlignment(); int seqIndex = alignment.findIndex(results); diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index 2fc3441..a78e41c 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -171,11 +171,13 @@ public class Finder extends Panel implements ActionListener boolean doSearchDescription = searchDescription.getState(); if (doFindAll) { - finder.findAll(searchString, isCaseSensitive, doSearchDescription); + finder.findAll(searchString, isCaseSensitive, doSearchDescription, + false); } else { - finder.findNext(searchString, isCaseSensitive, doSearchDescription); + finder.findNext(searchString, isCaseSensitive, doSearchDescription, + false); } searchResults = finder.getSearchResults(); @@ -209,13 +211,13 @@ public class Finder extends Panel implements ActionListener String message = (idMatches.size() > 0) ? "" + idMatches.size() + " IDs" : ""; if (idMatches.size() > 0 && searchResults != null - && searchResults.getSize() > 0) + && searchResults.getCount() > 0) { message += " and "; } if (searchResults != null) { - message += searchResults.getSize() + " subsequence matches."; + message += searchResults.getCount() + " subsequence matches."; } ap.alignFrame.statusBar.setText(MessageManager .formatMessage("label.search_results", new String[] diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index 35d6aa3..600d19c 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -20,16 +20,6 @@ */ package jalview.bin; -import jalview.datamodel.PDBEntry; -import jalview.gui.UserDefinedColours; -import jalview.schemes.ColourSchemeLoader; -import jalview.schemes.ColourSchemes; -import jalview.schemes.UserColourScheme; -import jalview.structure.StructureImportSettings; -import jalview.urls.IdOrgSettings; -import jalview.util.ColorUtils; -import jalview.ws.sifts.SiftsSettings; - import java.awt.Color; import java.io.BufferedReader; import java.io.File; @@ -37,6 +27,8 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; @@ -46,12 +38,26 @@ 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 org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.SimpleLayout; +import jalview.datamodel.PDBEntry; +import jalview.gui.UserDefinedColours; +import jalview.schemes.ColourSchemeLoader; +import jalview.schemes.ColourSchemes; +import jalview.schemes.UserColourScheme; +import jalview.structure.StructureImportSettings; +import jalview.urls.IdOrgSettings; +import jalview.util.ColorUtils; +import jalview.ws.sifts.SiftsSettings; + /** * Stores and retrieves Jalview Application Properties Lists and fields within * list entries are separated by '|' symbols unless otherwise stated (|) clauses @@ -168,8 +174,8 @@ import org.apache.log4j.SimpleLayout; * when shading by annotation *

    • ANNOTATIONCOLOUR_MAX (red) Shade used for maximum value of annotation * when shading by annotation
    • - *
    • www.jalview.org (http://www.jalview.org) a property enabling all HTTP - * requests to be redirected to a mirror of http://www.jalview.org
    • + *
    • www.jalview.org (https://www.jalview.org) a property enabling all HTTP + * requests to be redirected to a mirror of https://www.jalview.org
    • *
    • FIGURE_AUTOIDWIDTH (false) Expand the left hand column of an exported * alignment figure to accommodate even the longest sequence ID or annotation * label.
    • @@ -309,6 +315,10 @@ public class Cache // lcastor = Logger.getLogger("org.exolab.castor.xml.Marshaller"); // lcastor.setLevel(Level.toLevel(Cache.getDefault("logs.Castor.Level", // Level.INFO.toString()))); + // we shouldn't need to do this + org.apache.log4j.Logger.getRootLogger() + .setLevel(org.apache.log4j.Level.INFO); + jalview.bin.Cache.log.setLevel(Level.toLevel(Cache .getDefault("logs.Jalview.level", Level.INFO.toString()))); // laxis.addAppender(ap); @@ -422,7 +432,7 @@ public class Cache DEFAULT_CACHE_THRESHOLD_IN_DAYS)); IdOrgSettings.setUrl(getDefault("ID_ORG_HOSTURL", - "http://www.jalview.org/services/identifiers")); + "https://www.jalview.org/services/identifiers")); IdOrgSettings.setDownloadLocation(ID_ORG_FILE); StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache @@ -435,8 +445,8 @@ public class Cache String jnlpVersion = System.getProperty("jalview.version"); - // jnlpVersion will be null if a latest version check for the channel needs to - // be done + // jnlpVersion will be null if a latest version check for the channel needs + // to be done // Dont do this check if running in headless mode if (jnlpVersion == null && getDefault("VERSION_CHECK", true) @@ -446,9 +456,12 @@ public class Cache class VersionChecker extends Thread { + @Override public void run() { + String buildPropertiesUrl = Cache.getAppbaseBuildProperties(); + String orgtimeout = System .getProperty("sun.net.client.defaultConnectTimeout"); if (orgtimeout == null) @@ -462,28 +475,19 @@ public class Cache { System.setProperty("sun.net.client.defaultConnectTimeout", "5000"); - java.net.URL url = new java.net.URL(Cache - .getDefault("www.jalview.org", "http://www.jalview.org") - + "/webstart/jalview.jnlp"); + java.net.URL url = new java.net.URL(buildPropertiesUrl); + BufferedReader in = new BufferedReader( new InputStreamReader(url.openStream())); - String line = null; - while ((line = in.readLine()) != null) - { - if (line.indexOf("jalview.version") == -1) - { - continue; - } - - line = line.substring(line.indexOf("value=") + 7); - line = line.substring(0, line.lastIndexOf("\"")); - remoteVersion = line; - break; - } + + Properties remoteBuildProperties = new Properties(); + remoteBuildProperties.load(in); + remoteVersion = remoteBuildProperties.getProperty("VERSION"); } catch (Exception ex) { - System.out.println( - "Non-fatal exception when checking version at www.jalview.org :"); + System.out + .println("Non-fatal exception when checking version at " + + buildPropertiesUrl + ":"); System.out.println(ex); remoteVersion = getProperty("VERSION"); } @@ -555,8 +559,8 @@ public class Cache new BuildDetails(codeVersion, null, codeInstallation); if (printV && reportVersion) { - System.out - .println("Jalview Version: " + codeVersion + codeInstallation); + System.out.println( + "Jalview Version: " + codeVersion + codeInstallation); } } @@ -1099,16 +1103,23 @@ public class Cache public static String getVersionDetailsForConsole() { StringBuilder sb = new StringBuilder(); - sb.append("Jalview Version: " + jalview.bin.Cache.getDefault("VERSION", "TEST")); + sb.append("Jalview Version: "); + sb.append(jalview.bin.Cache.getDefault("VERSION", "TEST")); sb.append("\n"); - sb.append("Jalview Installation: " - + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")); + sb.append("Jalview Installation: "); + sb.append(jalview.bin.Cache.getDefault("INSTALLATION", "unknown")); sb.append("\n"); - sb.append("Build Date: " + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")); + sb.append("Build Date: "); + sb.append(jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")); sb.append("\n"); - sb.append("Java version: " + System.getProperty("java.version")); + sb.append("Java version: "); + sb.append(System.getProperty("java.version")); sb.append("\n"); - sb.append(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version")); + sb.append(System.getProperty("os.arch")); + sb.append(" "); + sb.append(System.getProperty("os.name")); + sb.append(" "); + sb.append(System.getProperty("os.version")); sb.append("\n"); appendIfNotNull(sb, "Install4j version: ", System.getProperty("sys.install4jVersion"), "\n", null); @@ -1116,9 +1127,23 @@ public class Cache System.getProperty("installer_template_version"), "\n", null); appendIfNotNull(sb, "Launcher version: ", System.getProperty("launcher_version"), "\n", null); - if (jalview.bin.Cache.getDefault("VERSION", "TEST").equals("DEVELOPMENT")) { + LookAndFeel laf = UIManager.getLookAndFeel(); + String lafName = laf == null ? "Not obtained" : laf.getName(); + String lafClass = laf == null ? "unknown" : laf.getClass().getName(); + sb.append("LookAndFeel: "); + sb.append(lafName); + sb.append(" ("); + sb.append(lafClass); + sb.append(")\n"); + // Not displayed in release version ( determined by possible version number + // regex 9[9.]*9[.-_a9]* ) + if (Pattern.matches("^\\d[\\d\\.]*\\d[\\.\\-\\w]*$", + jalview.bin.Cache.getDefault("VERSION", "TEST"))) + { appendIfNotNull(sb, "Getdown appdir: ", System.getProperty("getdownappdir"), "\n", null); + appendIfNotNull(sb, "Getdown appbase: ", + System.getProperty("getdownappbase"), "\n", null); appendIfNotNull(sb, "Java home: ", System.getProperty("java.home"), "\n", "unknown"); } @@ -1135,4 +1160,69 @@ public class Cache // eg 'built from Source' or update channel return jalview.bin.Cache.getDefault("INSTALLATION", "unknown"); } + + public static String getStackTraceString(Throwable t) + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + return sw.toString(); + } + + /** + * Getdown appbase methods + */ + + private static final String releaseAppbase; + + private static String getdownAppbase; + + private static String getdownDistDir; + + static + { + Float specversion = Float + .parseFloat(System.getProperty("java.specification.version")); + releaseAppbase = (specversion < 9) + ? "https://www.jalview.org/getdown/release/1.8" + : "https://www.jalview.org/getdown/release/11"; + } + + // look for properties (passed in by getdown) otherwise default to release + private static void setGetdownAppbase() + { + if (getdownAppbase != null) + { + return; + } + String appbase = System.getProperty("getdownappbase"); + String distDir = System.getProperty("getdowndistdir"); + if (appbase == null) + { + appbase = releaseAppbase; + distDir = "release"; + } + if (appbase.endsWith("/")) + { + appbase = appbase.substring(0, appbase.length() - 1); + } + if (distDir == null) + { + distDir = appbase.equals(releaseAppbase) ? "release" : "alt"; + } + getdownAppbase = appbase; + getdownDistDir = distDir; + } + + public static String getGetdownAppbase() + { + setGetdownAppbase(); + return getdownAppbase; + } + + public static String getAppbaseBuildProperties() + { + String appbase = getGetdownAppbase(); + return appbase + "/" + getdownDistDir + "/build_properties"; + } } diff --git a/src/jalview/bin/HiDPISetting.java b/src/jalview/bin/HiDPISetting.java new file mode 100644 index 0000000..497900f --- /dev/null +++ b/src/jalview/bin/HiDPISetting.java @@ -0,0 +1,180 @@ +package jalview.bin; + +import java.awt.HeadlessException; +import java.awt.Toolkit; + +public class HiDPISetting +{ + private static final int hidpiThreshold = 160; + + private static final int hidpiMultiThreshold = 240; + + private static final int bigScreenThreshold = 1400; + + private static final String scalePropertyName = "sun.java2d.uiScale"; + + private static final boolean isLinux; + + // private static final boolean isAMac; + + // private static final boolean isWindows; + + public static final String setHiDPIPropertyName = "setHiDPI"; + + public static final String setHiDPIScalePropertyName = "setHiDPIScale"; + + private static boolean setHiDPI = false; + + private static int setHiDPIScale = 0; + + public static int dpi = 0; + + public static int mindimension = 0; + + public static int width = 0; + + public static int scale = 0; + + private static boolean doneInit = false; + + private static boolean allowScalePropertyArg = false; + + static + { + String system = System.getProperty("os.name") == null ? null + : System.getProperty("os.name").toLowerCase(); + if (system != null) + { + isLinux = system.indexOf("linux") > -1; + // isAMac = system.indexOf("mac") > -1; + // isWindows = system.indexOf("windows") > -1; + } + else + { + isLinux = false; + // isAMac = isWindows = false; + } + } + + private static void init() + { + if (doneInit) + { + return; + } + + // get and use command line property values first + String setHiDPIProperty = System.getProperty(setHiDPIPropertyName); + setHiDPI = setHiDPIProperty != null + && setHiDPIProperty.equalsIgnoreCase("true"); + + String setHiDPIScaleProperty = System + .getProperty(setHiDPIScalePropertyName); + if (setHiDPIScaleProperty != null) + { + try + { + setHiDPIScale = Integer.parseInt(setHiDPIScaleProperty); + } catch (NumberFormatException e) + { + System.err.println(setHiDPIScalePropertyName + " property give (" + + setHiDPIScaleProperty + ") but not parseable as integer"); + } + } + if (setHiDPI && setHiDPIScale > 0) + { + setHiDPIScale(setHiDPIScale); + return; + } + + // check to see if the scale property has already been set by something else + // (e.g. the OS) + String existingProperty = System.getProperty(scalePropertyName); + if (existingProperty != null) + { + try + { + int existingPropertyVal = Integer.parseInt(existingProperty); + System.out.println("Existing " + scalePropertyName + " is " + + existingPropertyVal); + if (existingPropertyVal > 1) + { + setHiDPIScale(existingPropertyVal); + return; + } + } catch (NumberFormatException e) + { + System.out.println("Could not convert property " + scalePropertyName + + " vale '" + existingProperty + "' to number"); + } + } + + // Try and auto guess a good scale based on reported DPI (not trustworthy) + // and screen resolution (more trustworthy) + + // get screen dpi + try + { + dpi = Toolkit.getDefaultToolkit().getScreenResolution(); + } catch (HeadlessException e) + { + System.err.println("Cannot get screen resolution: " + e.getMessage()); + } + + // try and get screen size height and width + try + { + int height = Toolkit.getDefaultToolkit().getScreenSize().height; + int width = Toolkit.getDefaultToolkit().getScreenSize().width; + // using mindimension in case of portrait screens + mindimension = Math.min(height, width); + } catch (HeadlessException e) + { + System.err.println( + "Cannot get screen size height and width:" + e.getMessage()); + } + + // attempt at a formula for scaling based on screen dpi and mindimension. + // scale will be an integer >=1. This formula is based on some testing and + // guesswork! + + // scale based on reported dpi. if dpi>hidpiThreshold then scale=2+multiples + // of hidpiMultiThreshold (else scale=1) + // (e.g. dpi of 110 scales 1. dpi of 120 scales 2. dpi of 360 scales 3) + int dpiScale = (dpi - hidpiThreshold > 0) + ? 2 + ((dpi - hidpiThreshold) / hidpiMultiThreshold) + : 1; + + int dimensionScale = 1 + (mindimension / bigScreenThreshold); + + // choose larger of dimensionScale or dpiScale (most likely dimensionScale + // as dpiScale often misreported) + int autoScale = Math.max(dpiScale, dimensionScale); + + // only make an automatic change if scale is changed and other conditions + // (OS is linux) apply, or if setHiDPI has been specified + if ((autoScale > 1 && isLinux) || setHiDPI) + { + setHiDPIScale(autoScale); + return; + } + + // looks like we're not doing any scaling + doneInit = true; + } + + public static void setHiDPIScale(int s) + { + scale = s; + allowScalePropertyArg = true; + doneInit = true; + } + + public static String getScalePropertyArg() + { + init(); + // HiDPI setting. Just looking at Linux to start with. Test with Windows. + return allowScalePropertyArg ? "-D" + scalePropertyName + "=" + scale + : null; + } +} diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 8c9d391..1e292ba 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -30,6 +30,7 @@ import jalview.io.DataSourceType; import jalview.io.FileFormat; import jalview.io.FileFormatException; import jalview.io.FileFormatI; +import jalview.io.FileFormats; import jalview.io.FileLoader; import jalview.io.HtmlSvgOutput; import jalview.io.IdentifyFile; @@ -61,13 +62,33 @@ import java.util.HashMap; import java.util.Map; import java.util.Vector; -import javax.swing.LookAndFeel; import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; import com.threerings.getdown.util.LaunchUtil; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; +import jalview.ext.so.SequenceOntology; +import jalview.gui.AlignFrame; +import jalview.gui.Desktop; +import jalview.gui.PromptUserConfig; +import jalview.io.AppletFormatAdapter; +import jalview.io.BioJsHTMLOutput; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.FileFormatException; +import jalview.io.FileFormatI; +import jalview.io.FileLoader; +import jalview.io.HtmlSvgOutput; +import jalview.io.IdentifyFile; +import jalview.io.NewickFile; +import jalview.io.gff.SequenceOntologyFactory; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.ws.jws2.Jws2Discoverer; /** * Main class for Jalview Application
      @@ -183,7 +204,7 @@ public class Jalview * main class for Jalview application * * @param args - * open filename + * open filename */ public static void main(String[] args) { @@ -198,22 +219,24 @@ public class Jalview { System.setSecurityManager(null); System.out - .println("Java version: " - + System.getProperty("java.version")); + .println("Java version: " + System.getProperty("java.version")); System.out.println("Java Home: " + System.getProperty("java.home")); System.out.println(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version")); String val = System.getProperty("sys.install4jVersion"); - if (val != null) { - System.out.println("Install4j version: " + val); + if (val != null) + { + System.out.println("Install4j version: " + val); } val = System.getProperty("installer_template_version"); - if (val != null) { + if (val != null) + { System.out.println("Install4j template version: " + val); } val = System.getProperty("launcher_version"); - if (val != null) { + if (val != null) + { System.out.println("Launcher version: " + val); } @@ -296,51 +319,7 @@ public class Jalview desktop = null; - try - { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ex) - { - System.err.println("Unexpected Look and Feel Exception"); - ex.printStackTrace(); - } - if (Platform.isAMac()) - { - - LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager - .getLookAndFeel(); - System.setProperty("com.apple.mrj.application.apple.menu.about.name", - "Jalview"); - System.setProperty("apple.laf.useScreenMenuBar", "true"); - if (lookAndFeel != null) - { - try - { - UIManager.setLookAndFeel(lookAndFeel); - } catch (Throwable e) - { - System.err.println( - "Failed to set QuaQua look and feel: " + e.toString()); - } - } - if (lookAndFeel == null - || !(lookAndFeel.getClass().isAssignableFrom( - UIManager.getLookAndFeel().getClass())) - || !UIManager.getLookAndFeel().getClass().toString() - .toLowerCase().contains("quaqua")) - { - try - { - System.err.println( - "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976"); - UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel"); - } catch (Throwable e) - { - System.err.println( - "Failed to reset look and feel: " + e.toString()); - } - } - } + setLookAndFeel(); /* * configure 'full' SO model if preferences say to, else use the default (SO @@ -353,6 +332,7 @@ public class Jalview if (!headless) { + desktop = new Desktop(); desktop.setInBatchMode(true); // indicate we are starting up @@ -361,11 +341,13 @@ public class Jalview JalviewTaskbar.setTaskbar(this); } catch (Exception e) { - System.out.println("Cannot set Taskbar"); + Cache.log.info("Cannot set Taskbar"); + Cache.log.error(e.getMessage()); // e.printStackTrace(); } catch (Throwable t) { - System.out.println("Cannot set Taskbar"); + Cache.log.info("Cannot set Taskbar"); + Cache.log.error(t.getMessage()); // t.printStackTrace(); } @@ -401,7 +383,7 @@ public class Jalview // String defurl = // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl"; // // - String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl"; + String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl"; Cache.log.debug( "Starting questionnaire with default url: " + defurl); desktop.checkForQuestionnaire(defurl); @@ -469,7 +451,11 @@ public class Jalview } System.out.println("CMD [-open " + file + "] executed successfully!"); - if (!file.startsWith("http://")) + + + protocol = AppletFormatAdapter.checkProtocol(file); + + if (protocol == DataSourceType.FILE) { if (!(new File(file)).exists()) { @@ -481,8 +467,6 @@ public class Jalview } } - protocol = AppletFormatAdapter.checkProtocol(file); - try { format = new IdentifyFile().identify(file, protocol); @@ -666,18 +650,34 @@ public class Jalview af.createEPS(outputFile); continue; } - - if (af.saveAlignment(file, format)) - { - System.out.println("Written alignment in " + format - + " format to " + file); + 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."); } - else + if (outFormat != null) { - System.out.println("Error writing file " + file + " in " - + format + " format!!"); + if (!outFormat.isWritable()) + { + System.out.println( + "This version of Jalview does not support alignment export as " + + outputFormat); + } + else + { + if (af.saveAlignment(file, outFormat)) + { + System.out.println("Written alignment in " + format + + " format to " + file); + } + else + { + System.out.println("Error writing file " + file + " in " + + format + " format!!"); + } + } } - } while (aparser.getSize() > 0) @@ -696,13 +696,16 @@ public class Jalview { file = jalview.bin.Cache.getDefault("STARTUP_FILE", jalview.bin.Cache.getDefault("www.jalview.org", - "http://www.jalview.org") - + "/examples/exampleFile_2_7.jar"); + "https://www.jalview.org") + + "/examples/exampleFile_2_7.jvp"); if (file.equals( - "http://www.jalview.org/examples/exampleFile_2_3.jar")) + "http://www.jalview.org/examples/exampleFile_2_3.jar") || file.equals( + "http://www.jalview.org/examples/exampleFile_2_7.jar")) { + file.replace("http:", "https:"); // hardwire upgrade of the startup file - file.replace("_2_3.jar", "_2_7.jar"); + file.replace("_2_3", "_2_7"); + file.replace("2_7.jar", "2_7.jvp"); // and remove the stale setting jalview.bin.Cache.removeProperty("STARTUP_FILE"); } @@ -760,6 +763,204 @@ public class Jalview } } + private static void setLookAndFeel() + { + // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or + // "mac" + // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac, + // try Quaqua/Vaqua. + String lafProp = System.getProperty("laf"); + String lafSetting = Cache.getDefault("PREFERRED_LAF", null); + String laf = "none"; + if (lafProp != null) + { + laf = lafProp; + } + else if (lafSetting != null) + { + laf = lafSetting; + } + boolean lafSet = false; + switch (laf) + { + case "crossplatform": + lafSet = setCrossPlatformLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "system": + lafSet = setSystemLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "gtk": + lafSet = setGtkLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "metal": + lafSet = setMetalLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "nimbus": + lafSet = setNimbusLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "quaqua": + lafSet = setQuaquaLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "vaqua": + lafSet = setVaquaLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "mac": + lafSet = setMacLookAndFeel(); + if (!lafSet) + { + Cache.log.error("Could not set requested laf=" + laf); + } + break; + case "none": + break; + default: + Cache.log.error("Requested laf=" + laf + " not implemented"); + } + if (!lafSet) + { + setSystemLookAndFeel(); + if (Platform.isLinux()) + { + setMetalLookAndFeel(); + } + if (Platform.isAMac()) + { + setMacLookAndFeel(); + } + } + } + + private static boolean setCrossPlatformLookAndFeel() + { + return setGenericLookAndFeel(false); + } + + private static boolean setSystemLookAndFeel() + { + return setGenericLookAndFeel(true); + } + + private static boolean setGenericLookAndFeel(boolean system) + { + boolean set = false; + try + { + UIManager.setLookAndFeel( + system ? UIManager.getSystemLookAndFeelClassName() + : UIManager.getCrossPlatformLookAndFeelClassName()); + set = true; + } catch (Exception ex) + { + Cache.log.error("Unexpected Look and Feel Exception"); + Cache.log.error(ex.getMessage()); + Cache.log.debug(Cache.getStackTraceString(ex)); + } + return set; + } + + private static boolean setSpecificLookAndFeel(String name, + String className, boolean nameStartsWith) + { + boolean set = false; + try + { + for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) + { + if (info.getName() != null && nameStartsWith + ? info.getName().toLowerCase() + .startsWith(name.toLowerCase()) + : info.getName().toLowerCase().equals(name.toLowerCase())) + { + className = info.getClassName(); + break; + } + } + UIManager.setLookAndFeel(className); + set = true; + } catch (Exception ex) + { + Cache.log.error("Unexpected Look and Feel Exception"); + Cache.log.error(ex.getMessage()); + Cache.log.debug(Cache.getStackTraceString(ex)); + } + return set; + } + + private static boolean setGtkLookAndFeel() + { + return setSpecificLookAndFeel("gtk", + "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true); + } + + private static boolean setMetalLookAndFeel() + { + return setSpecificLookAndFeel("metal", + "javax.swing.plaf.metal.MetalLookAndFeel", false); + } + + private static boolean setNimbusLookAndFeel() + { + return setSpecificLookAndFeel("nimbus", + "javax.swing.plaf.nimbus.NimbusLookAndFeel", false); + } + + private static boolean setQuaquaLookAndFeel() + { + return setSpecificLookAndFeel("quaqua", + ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass() + .getName(), + false); + } + + private static boolean setVaquaLookAndFeel() + { + return setSpecificLookAndFeel("vaqua", + "org.violetlib.aqua.AquaLookAndFeel", false); + } + + private static boolean setMacLookAndFeel() + { + boolean set = false; + System.setProperty("com.apple.mrj.application.apple.menu.about.name", + "Jalview"); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + set = setQuaquaLookAndFeel(); + if ((!set) || !UIManager.getLookAndFeel().getClass().toString() + .toLowerCase().contains("quaqua")) + { + set = setVaquaLookAndFeel(); + } + return set; + } + private static void showUsage() { System.out.println( @@ -798,7 +999,9 @@ public class Jalview + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n" + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n" + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n" - + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n"); + + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n" + + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n" + + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n"); } private static void startUsageStats(final Desktop desktop) @@ -836,10 +1039,10 @@ public class Jalview * Locate the given string as a file and pass it to the groovy interpreter. * * @param groovyscript - * the script to execute + * the script to execute * @param jalviewContext - * the Jalview Desktop object passed in to the groovy - * binding as the 'Jalview' object. + * the Jalview Desktop object passed in to the groovy binding as the + * 'Jalview' object. */ private void executeGroovyScript(String groovyscript, AlignFrame af) { @@ -968,8 +1171,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() { diff --git a/src/jalview/bin/Launcher.java b/src/jalview/bin/Launcher.java index fb1c5cd..fecacbe 100644 --- a/src/jalview/bin/Launcher.java +++ b/src/jalview/bin/Launcher.java @@ -49,8 +49,8 @@ public class Launcher /** * main method for jalview.bin.Launcher. This restarts the same JRE's JVM with - * the same arguments but with memory adjusted based on extracted -jvmmempc and - * -jvmmemmax application arguments. If on a Mac then extra dock:icon and + * the same arguments but with memory adjusted based on extracted -jvmmempc + * and -jvmmemmax application arguments. If on a Mac then extra dock:icon and * dock:name arguments are also set. * * @param args @@ -130,7 +130,7 @@ public class Launcher if (!memSet) { long maxMemLong = MemorySetting.getMemorySetting(jvmmemmax, jvmmempc); - + if (maxMemLong > 0) { memSetting = "-Xmx" + Long.toString(maxMemLong); @@ -153,6 +153,14 @@ public class Launcher } } + String scalePropertyArg = HiDPISetting.getScalePropertyArg(); + if (scalePropertyArg != null) + { + System.out.println("Running " + startClass + " with scale setting " + + scalePropertyArg); + command.add(scalePropertyArg); + } + command.add(startClass); command.addAll(arguments); @@ -160,7 +168,8 @@ public class Launcher // System.out.println("COMMAND: " + String.join(" ", builder.command())); System.out.println("Running " + startClass + " with " - + (memSetting == null ? "no memory setting" : memSetting)); + + (memSetting == null ? "no memory setting" + : ("memory setting " + memSetting))); if (Boolean.parseBoolean(System.getProperty("launcherstop"))) { @@ -176,7 +185,8 @@ public class Launcher if (e.getMessage().toLowerCase().contains("memory")) { System.out.println("Caught a memory exception: " + e.getMessage()); - // Probably the "Cannot allocate memory" error, try without the memory setting + // Probably the "Cannot allocate memory" error, try without the memory + // setting ArrayList commandNoMem = new ArrayList<>(); for (int i = 0; i < command.size(); i++) { diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index 31736e5..7c3bba7 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -33,6 +33,7 @@ import java.util.List; */ public class SearchResults implements SearchResultsI { + private int count; private List matches = new ArrayList<>(); @@ -168,18 +169,41 @@ public class SearchResults implements SearchResultsI if (!matches.contains(m)) { matches.add(m); + count++; } return m; } @Override + public void addResult(SequenceI seq, int[] positions) + { + /* + * we only increment the match count by 1 - or not at all, + * if the matches are all duplicates of existing + */ + int beforeCount = count; + for (int i = 0; i < positions.length - 1; i += 2) + { + addResult(seq, positions[i], positions[i + 1]); + } + if (count > beforeCount) + { + count = beforeCount + 1; + } + } + + @Override public boolean involvesSequence(SequenceI sequence) { + final int start = sequence.getStart(); + final int end = sequence.getEnd(); + SequenceI ds = sequence.getDatasetSequence(); - for (SearchResultMatchI _m : matches) + for (SearchResultMatchI m : matches) { - SequenceI matched = _m.getSequence(); - if (matched != null && (matched == sequence || matched == ds)) + SequenceI matched = m.getSequence(); + if (matched != null && (matched == sequence || matched == ds) + && (m.getEnd() >= start) && (m.getStart() <= end)) { return true; } @@ -282,9 +306,9 @@ public class SearchResults implements SearchResultsI } @Override - public int getSize() + public int getCount() { - return matches.size(); + return count; } @Override diff --git a/src/jalview/datamodel/SearchResultsI.java b/src/jalview/datamodel/SearchResultsI.java index 3c3ad4e..c89f363 100644 --- a/src/jalview/datamodel/SearchResultsI.java +++ b/src/jalview/datamodel/SearchResultsI.java @@ -41,6 +41,17 @@ public interface SearchResultsI SearchResultMatchI addResult(SequenceI seq, int start, int end); /** + * Adds one ore more [start, end] ranges to the results (unless already added + * to avoid duplicates). This method only increments the match count by 1. + * This is for the case where a match spans ignored hidden residues - it is + * formally two or more contiguous matches, but only counted as one match. + * + * @param seq + * @param positions + */ + void addResult(SequenceI seq, int[] positions); + + /** * adds all match results in the argument to this set * * @param toAdd @@ -74,11 +85,13 @@ public interface SearchResultsI int[] getResults(SequenceI sequence, int start, int end); /** - * Returns the number of matches found + * Returns the number of matches found. Note that if a match straddles ignored + * hidden residues, it is counted as one match, although formally recorded as + * two (or more) contiguous matched sequence regions * * @return */ - int getSize(); + int getCount(); /** * Returns true if no search result matches are held. diff --git a/src/jalview/datamodel/features/FeatureStore.java b/src/jalview/datamodel/features/FeatureStore.java index 54c0d59..7651016 100644 --- a/src/jalview/datamodel/features/FeatureStore.java +++ b/src/jalview/datamodel/features/FeatureStore.java @@ -20,8 +20,6 @@ */ package jalview.datamodel.features; -import jalview.datamodel.SequenceFeature; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -30,7 +28,9 @@ import java.util.Set; import intervalstore.api.IntervalStoreI; import intervalstore.impl.BinarySearcher; +import intervalstore.impl.BinarySearcher.Compare; import intervalstore.impl.IntervalStore; +import jalview.datamodel.SequenceFeature; /** * A data store for a set of sequence features that supports efficient lookup of @@ -277,7 +277,7 @@ public class FeatureStore * binary search the sorted list to find the insertion point */ int insertPosition = BinarySearcher.findFirst(contactFeatureStarts, - f -> f.getBegin() >= feature.getBegin()); + true, Compare.GE, feature.getBegin()); contactFeatureStarts.add(insertPosition, feature); @@ -286,7 +286,7 @@ public class FeatureStore * binary search the sorted list to find the insertion point */ insertPosition = BinarySearcher.findFirst(contactFeatureEnds, - f -> f.getEnd() >= feature.getEnd()); + false, Compare.GE, feature.getEnd()); contactFeatureEnds.add(insertPosition, feature); return true; @@ -314,8 +314,8 @@ public class FeatureStore */ // int pos = binarySearch(features, // SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION)); - int pos = BinarySearcher.findFirst(features, - val -> val.getBegin() >= feature.getBegin()); + int pos = BinarySearcher.findFirst(features, true, Compare.GE, + feature.getBegin()); int len = features.size(); while (pos < len) { @@ -396,7 +396,7 @@ public class FeatureStore * whose end point is not before the target range */ int index = BinarySearcher.findFirst(contactFeatureEnds, - f -> f.getEnd() >= from); + false, Compare.GE, (int) from); while (index < contactFeatureEnds.size()) { @@ -449,7 +449,7 @@ public class FeatureStore List result) { int index = BinarySearcher.findFirst(contactFeatureStarts, - f -> f.getBegin() >= from); + true, Compare.GE, (int) from); while (index < contactFeatureStarts.size()) { diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index db2f0e1..8ac4991 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -20,11 +20,8 @@ */ package jalview.datamodel.features; -import jalview.datamodel.SequenceFeature; -import jalview.io.gff.SequenceOntologyFactory; -import jalview.io.gff.SequenceOntologyI; - import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -33,6 +30,9 @@ import java.util.Set; import java.util.TreeMap; import intervalstore.api.IntervalI; +import jalview.datamodel.SequenceFeature; +import jalview.io.gff.SequenceOntologyFactory; +import jalview.io.gff.SequenceOntologyI; /** * A class that stores sequence features in a way that supports efficient @@ -44,7 +44,6 @@ import intervalstore.api.IntervalI; */ public class SequenceFeatures implements SequenceFeaturesI { - /* * map from feature type to structured store of features for that type * null types are permitted (but not a good idea!) @@ -414,7 +413,10 @@ public class SequenceFeatures implements SequenceFeaturesI public static void sortFeatures(List features, final boolean forwardStrand) { - IntervalI.sortIntervals(features, forwardStrand); + Collections.sort(features, + forwardStrand + ? IntervalI.COMPARE_BEGIN_ASC_END_DESC + : IntervalI.COMPARE_END_DESC); } /** diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index 9c7ce63..461226b 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -629,10 +629,10 @@ public class EnsemblGene extends EnsemblSeqProxy SequenceOntologyI so = SequenceOntologyFactory.getInstance(); @Override - public boolean isFeatureDisplayed(String type) + public boolean isFeatureHidden(String type) { - return (so.isA(type, SequenceOntologyI.EXON) - || so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT)); + return (!so.isA(type, SequenceOntologyI.EXON) + && !so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT)); } @Override diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 1ceabd1..b2224ab 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -22,11 +22,13 @@ package jalview.ext.jmol; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; +import jalview.api.FeatureSettingsModelI; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.gui.AppJmol; import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.io.StructureFile; @@ -37,6 +39,7 @@ import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; import jalview.util.MessageManager; +import jalview.ws.dbsources.Pdb; import java.awt.Color; import java.awt.Container; @@ -1190,7 +1193,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel FeatureRenderer fr = getFeatureRenderer(null); if (fr != null) { - fr.featuresAdded(); + FeatureSettingsModelI colours = new Pdb().getFeatureColourScheme(); + ((AppJmol) getViewer()).getAlignmentPanel().av + .applyFeaturesStyle(colours); } refreshGUI(); loadNotifiesHandled++; diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index 232561c..54b2989 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -296,7 +296,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI private void jbInit() throws Exception { - txt_search = new JvCacheableInputBox<>(getCacheKey()); + txt_search = new JvCacheableInputBox<>(getCacheKey(), 45); populateCmbSearchTargetOptions(); Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800 : getTempUserPrefs().get("FTSPanel.width"); diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index b7d8b3a..4be7d0e 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -20,6 +20,54 @@ */ package jalview.gui; +import java.awt.BorderLayout; +import java.awt.Component; +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.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.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.JEditorPane; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + import jalview.analysis.AlignmentSorter; import jalview.analysis.AlignmentUtils; import jalview.analysis.CrossRef; @@ -91,6 +139,7 @@ import jalview.schemes.ColourSchemes; import jalview.schemes.ResidueColourScheme; import jalview.schemes.TCoffeeColourScheme; import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.viewmodel.AlignmentViewport; import jalview.viewmodel.ViewportRanges; import jalview.ws.DBRefFetcher; @@ -100,53 +149,6 @@ import jalview.ws.jws2.Jws2Discoverer; import jalview.ws.jws2.jabaws2.Jws2Instance; import jalview.ws.seqfetcher.DbSourceProxy; -import java.awt.BorderLayout; -import java.awt.Component; -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.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.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.JEditorPane; -import javax.swing.JInternalFrame; -import javax.swing.JLayeredPane; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JScrollPane; -import javax.swing.SwingUtilities; - /** * DOCUMENT ME! * @@ -536,7 +538,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } if (viewport.cursorMode) { - alignPanel.getSeqPanel().moveCursor(0, 1); + alignPanel.getSeqPanel().moveCursor(0, 1, + evt.isShiftDown() && !evt.isAltDown()); } break; @@ -547,9 +550,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } if (viewport.cursorMode) { - alignPanel.getSeqPanel().moveCursor(0, -1); + alignPanel.getSeqPanel().moveCursor(0, -1, + evt.isShiftDown() && !evt.isAltDown()); } - break; case KeyEvent.VK_LEFT: @@ -560,7 +563,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } else { - alignPanel.getSeqPanel().moveCursor(-1, 0); + alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown()); } break; @@ -572,7 +575,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } else { - alignPanel.getSeqPanel().moveCursor(1, 0); + alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown()); } break; @@ -1168,7 +1171,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, statusBar.setText(MessageManager.formatMessage( "label.successfully_saved_to_file_in_format", new Object[] - { fileName, format })); + { file, format })); } else @@ -1197,39 +1200,66 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, else { // create backupfiles object and get new temp filename destination + Cache.log.trace("ALIGNFRAME making backupfiles object for " + file); BackupFiles backupfiles = new BackupFiles(file); try { - PrintWriter out = new PrintWriter( - new FileWriter(backupfiles.getTempFilePath())); + String tempFilePath = backupfiles.getTempFilePath(); + Cache.log.trace( + "ALIGNFRAME setting PrintWriter to " + tempFilePath); + PrintWriter out = new PrintWriter(new FileWriter(tempFilePath)); + + Cache.log.trace( + "ALIGNFRAME about to write to temp file " + tempFilePath); out.print(output); + Cache.log.trace("ALIGNFRAME about to close file"); out.close(); + Cache.log.trace("ALIGNFRAME closed file"); this.setTitle(file); statusBar.setText(MessageManager.formatMessage( "label.successfully_saved_to_file_in_format", new Object[] - { fileName, format.getName() })); + { file, format.getName() })); + } catch (IOException e) + { + success = false; + Cache.log.error( + "ALIGNFRAME Something happened writing the temp file"); + Cache.log.error(e.getMessage()); + Cache.log.debug(Cache.getStackTraceString(e)); + } catch (Exception ex) { success = false; - ex.printStackTrace(); + Cache.log.error( + "ALIGNFRAME Something unexpected happened writing the temp file"); + Cache.log.error(ex.getMessage()); + Cache.log.debug(Cache.getStackTraceString(ex)); } backupfiles.setWriteSuccess(success); + Cache.log.debug("ALIGNFRAME writing temp file was " + + (success ? "" : "NOT ") + "successful"); // do the backup file roll and rename the temp file to actual file + Cache.log.trace("ALIGNFRAME about to rollBackupsAndRenameTempFile"); success = backupfiles.rollBackupsAndRenameTempFile(); + Cache.log.debug("ALIGNFRAME performed rollBackupsAndRenameTempFile " + + (success ? "" : "un") + "successfully"); } } if (!success) { - JvOptionPane.showInternalMessageDialog(this, MessageManager - .formatMessage("label.couldnt_save_file", new Object[] - { fileName }), - MessageManager.getString("label.error_saving_file"), - JvOptionPane.WARNING_MESSAGE); + if (!Platform.isHeadless()) + { + JvOptionPane.showInternalMessageDialog(this, MessageManager + .formatMessage("label.couldnt_save_file", new Object[] + { file }), + MessageManager.getString("label.error_saving_file"), + JvOptionPane.WARNING_MESSAGE); + } } return success; @@ -1719,10 +1749,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * DOCUMENT ME! + * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection or the sequence under cursor in keyboard mode * * @param up - * DOCUMENT ME! + * or down (if !up) */ public void moveSelectedSequences(boolean up) { @@ -1730,8 +1760,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (sg == null) { - return; + if (viewport.cursorMode) + { + sg = new SequenceGroup(); + sg.addSequence(viewport.getAlignment() + .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY),false); + } else { + return; + } + } + + if (sg.getSize() < 1) + { + return; } + + // TODO: JAL-3733 - add an event to the undo buffer for this ! + viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); alignPanel.paintAlignment(true, false); @@ -1740,19 +1785,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, synchronized void slideSequences(boolean right, int size) { List sg = new ArrayList<>(); - if (viewport.cursorMode) - { - sg.add(viewport.getAlignment() - .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY)); - } - else if (viewport.getSelectionGroup() != null - && viewport.getSelectionGroup().getSize() != viewport - .getAlignment().getHeight()) + if (viewport.getSelectionGroup() != null && viewport.getSelectionGroup() + .getSize() != viewport.getAlignment().getHeight()) { sg = viewport.getSelectionGroup() .getSequences(viewport.getHiddenRepSequences()); } + if (sg.size() == 0 && viewport.cursorMode) + { + sg.add(viewport.getAlignment() + .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY)); + } + if (sg.size() < 1) { return; @@ -2450,7 +2495,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } viewport.setSelectionGroup(null); viewport.getColumnSelection().clear(); - viewport.setSelectionGroup(null); + viewport.setSearchResults(null); alignPanel.getIdPanel().getIdCanvas().searchResults = null; // JAL-2034 - should delegate to // alignPanel to decide if overview needs @@ -2687,15 +2732,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * DOCUMENT ME! + * Opens a Finder dialog * * @param e - * DOCUMENT ME! */ @Override public void findMenuItem_actionPerformed(ActionEvent e) { - new Finder(); + new Finder(alignPanel); } /** @@ -2736,8 +2780,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (viewport.getViewName() == null) { - viewport.setViewName(MessageManager - .getString("label.view_name_original")); + viewport.setViewName( + MessageManager.getString("label.view_name_original")); } /* @@ -3366,8 +3410,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * otherwise set the chosen colour scheme (or null for 'None') */ ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name, - viewport, - viewport.getAlignment(), viewport.getHiddenRepSequences()); + viewport, viewport.getAlignment(), + viewport.getHiddenRepSequences()); changeColour(cs); } @@ -5694,6 +5738,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } private Rectangle lastFeatureSettingsBounds = null; + @Override public void setFeatureSettingsGeometry(Rectangle bounds) { diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 05e446e..8e12ff5 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -59,7 +59,6 @@ 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; @@ -446,31 +445,6 @@ public class AlignViewport extends AlignmentViewport } /** - * returns the visible column regions of the alignment - * - * @param selectedRegionOnly - * true to just return the contigs intersecting with the selected - * area - * @return - */ - public Iterator getViewAsVisibleContigs(boolean selectedRegionOnly) - { - int start = 0; - int end = 0; - if (selectedRegionOnly && selectionGroup != null) - { - start = selectionGroup.getStartRes(); - end = selectionGroup.getEndRes() + 1; - } - else - { - end = alignment.getWidth(); - } - return (alignment.getHiddenColumns().getVisContigsIterator(start, end, - false)); - } - - /** * get hash of undo and redo list for the alignment * * @return long[] { historyList.hashCode, redoList.hashCode }; @@ -1018,8 +992,8 @@ public class AlignViewport extends AlignmentViewport FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas .getFeatureRenderer(); - List origRenderOrder = new ArrayList(), - origGroups = new ArrayList(); + List origRenderOrder = new ArrayList<>(); + List origGroups = new ArrayList<>(); // preserve original render order - allows differentiation between user configured colours and autogenerated ones origRenderOrder.addAll(fr.getRenderOrder()); origGroups.addAll(fr.getFeatureGroups()); @@ -1030,7 +1004,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? // @@ -1062,6 +1036,10 @@ public class AlignViewport extends AlignmentViewport { displayed.setVisible(type); } + else if (featureSettings.isFeatureHidden(type)) + { + displayed.setHidden(type); + } } } @@ -1090,6 +1068,8 @@ public class AlignViewport extends AlignmentViewport fr.orderFeatures(featureSettings); } fr.setTransparency(featureSettings.getTransparency()); + + fr.notifyFeaturesChanged(); } public String getViewName() diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java index 60ad75d..7ed178b 100644 --- a/src/jalview/gui/AnnotationColourChooser.java +++ b/src/jalview/gui/AnnotationColourChooser.java @@ -53,8 +53,6 @@ import net.miginfocom.swing.MigLayout; @SuppressWarnings("serial") public class AnnotationColourChooser extends AnnotationRowFilter { - private static final int ONETHOUSAND = 1000; - private ColourSchemeI oldcs; private JButton defColours; @@ -147,7 +145,8 @@ public class AnnotationColourChooser extends AnnotationRowFilter "error.implementation_error_dont_know_about_threshold_setting")); } thresholdIsMin.setSelected(acg.isThresholdIsMinMax()); - thresholdValue.setText("" + acg.getAnnotationThreshold()); + thresholdValue + .setText(String.valueOf(acg.getAnnotationThreshold())); } jbInit(); @@ -338,7 +337,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter { updateView(); } - getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; + getCurrentAnnotation().threshold.value = getSliderValue(); propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); ap.paintAlignment(false, false); @@ -378,6 +377,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter thresholdValue.setEnabled(true); thresholdIsMin.setEnabled(!useOriginalColours.isSelected()); + final AlignmentAnnotation currentAnnotation = getCurrentAnnotation(); if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD) { slider.setEnabled(false); @@ -386,33 +386,26 @@ public class AnnotationColourChooser extends AnnotationRowFilter thresholdIsMin.setEnabled(false); } else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD - && getCurrentAnnotation().threshold == null) + && currentAnnotation.threshold == null) { - getCurrentAnnotation().setThreshold(new GraphLine( - (getCurrentAnnotation().graphMax - - getCurrentAnnotation().graphMin) / 2f, + currentAnnotation.setThreshold(new GraphLine( + (currentAnnotation.graphMax - currentAnnotation.graphMin) + / 2f, "Threshold", Color.black)); } if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD) { adjusting = true; - float range = getCurrentAnnotation().graphMax * ONETHOUSAND - - getCurrentAnnotation().graphMin * ONETHOUSAND; - - slider.setMinimum( - (int) (getCurrentAnnotation().graphMin * ONETHOUSAND)); - slider.setMaximum( - (int) (getCurrentAnnotation().graphMax * ONETHOUSAND)); - slider.setValue( - (int) (getCurrentAnnotation().threshold.value * ONETHOUSAND)); - thresholdValue.setText(getCurrentAnnotation().threshold.value + ""); - slider.setMajorTickSpacing((int) (range / 10f)); + setSliderModel(currentAnnotation.graphMin, currentAnnotation.graphMax, + currentAnnotation.threshold.value); slider.setEnabled(true); + + setThresholdValueText(); thresholdValue.setEnabled(true); adjusting = false; } - colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem); + colorAlignmentContaining(currentAnnotation, selectedThresholdItem); ap.alignmentChanged(); } diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index 5adecad..d941107 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -21,6 +21,7 @@ package jalview.gui; +import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.HiddenColumns; import jalview.io.cache.JvCacheableInputBox; import jalview.schemes.AnnotationColourGradient; @@ -254,7 +255,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter { if (slider.isEnabled()) { - getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; + getCurrentAnnotation().threshold.value = getSliderValue(); updateView(); propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); @@ -284,6 +285,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter thresholdValue.setEnabled(true); percentThreshold.setEnabled(true); + final AlignmentAnnotation currentAnnotation = getCurrentAnnotation(); if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD) { slider.setEnabled(false); @@ -294,26 +296,22 @@ public class AnnotationColumnChooser extends AnnotationRowFilter } else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD) { - if (getCurrentAnnotation().threshold == null) + if (currentAnnotation.threshold == null) { - getCurrentAnnotation().setThreshold(new jalview.datamodel.GraphLine( - (getCurrentAnnotation().graphMax - - getCurrentAnnotation().graphMin) / 2f, + currentAnnotation.setThreshold(new jalview.datamodel.GraphLine( + (currentAnnotation.graphMax + - currentAnnotation.graphMin) / 2f, "Threshold", Color.black)); } adjusting = true; - float range = getCurrentAnnotation().graphMax * 1000 - - getCurrentAnnotation().graphMin * 1000; - slider.setMinimum((int) (getCurrentAnnotation().graphMin * 1000)); - slider.setMaximum((int) (getCurrentAnnotation().graphMax * 1000)); - slider.setValue( - (int) (getCurrentAnnotation().threshold.value * 1000)); + setSliderModel(currentAnnotation.graphMin, + currentAnnotation.graphMax, + currentAnnotation.threshold.value); setThresholdValueText(); - slider.setMajorTickSpacing((int) (range / 10f)); slider.setEnabled(true); thresholdValue.setEnabled(true); adjusting = false; @@ -321,10 +319,10 @@ public class AnnotationColumnChooser extends AnnotationRowFilter // build filter params filterParams.setThresholdType( AnnotationFilterParameter.ThresholdType.NO_THRESHOLD); - if (getCurrentAnnotation().isQuantitative()) + if (currentAnnotation.isQuantitative()) { filterParams - .setThresholdValue(getCurrentAnnotation().threshold.value); + .setThresholdValue(currentAnnotation.threshold.value); if (selectedThresholdItem == AnnotationColourGradient.ABOVE_THRESHOLD) { @@ -380,7 +378,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter // adding them to the selection av.showAllHiddenColumns(); av.getColumnSelection().filterAnnotations( - getCurrentAnnotation().annotations, filterParams); + currentAnnotation.annotations, filterParams); boolean hideCols = getActionOption() == ACTION_OPTION_HIDE; if (hideCols) @@ -720,7 +718,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter private static final String FILTER_BY_ANN_CACHE_KEY = "CACHE.SELECT_FILTER_BY_ANNOT"; public JvCacheableInputBox searchBox = new JvCacheableInputBox<>( - FILTER_BY_ANN_CACHE_KEY); + FILTER_BY_ANN_CACHE_KEY, 23); public SearchPanel(AnnotationColumnChooser aColChooser) { @@ -730,7 +728,6 @@ public class AnnotationColumnChooser extends AnnotationRowFilter this.setBorder(new TitledBorder( MessageManager.getString("label.search_filter"))); - searchBox.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXX"); searchBox.setToolTipText( MessageManager.getString("info.enter_search_text_here")); searchBox.getEditor().getEditorComponent() diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 16db94c..10fc3cf 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -629,6 +629,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, mouseDragLastX = -1; mouseDragLastY = -1; mouseDragging = false; + if (dragMode == DragMode.Resize) + { + ap.adjustAnnotationHeight(); + } dragMode = DragMode.Undefined; ap.getScalePanel().mouseReleased(evt); @@ -668,16 +672,25 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } /** - * DOCUMENT ME! + * Action on starting or continuing a mouse drag. There are two possible + * actions: + *
        + *
      • drag up or down on a graphed annotation increases or decreases the + * height of the graph
      • + *
      • dragging left or right selects the columns dragged across
      • + *
      + * A drag on a graph annotation is treated as column selection if it starts + * with more horizontal than vertical movement, and as resize if it starts + * with more vertical than horizontal movement. Once started, the drag does + * not change mode. * * @param evt - * DOCUMENT ME! */ @Override public void mouseDragged(MouseEvent evt) { /* - * todo: if dragMode is Undefined: + * if dragMode is Undefined: * - set to Select if dx > dy * - set to Resize if dy > dx * - do nothing if dx == dy diff --git a/src/jalview/gui/AnnotationRowFilter.java b/src/jalview/gui/AnnotationRowFilter.java index f13cb10..6f72e10 100644 --- a/src/jalview/gui/AnnotationRowFilter.java +++ b/src/jalview/gui/AnnotationRowFilter.java @@ -35,6 +35,8 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.math.BigDecimal; +import java.math.MathContext; import java.util.HashMap; import java.util.Map; import java.util.Vector; @@ -44,7 +46,6 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JInternalFrame; import javax.swing.JPanel; -import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -52,6 +53,10 @@ import javax.swing.event.ChangeListener; @SuppressWarnings("serial") public abstract class AnnotationRowFilter extends JPanel { + private static final String TWO_DP = "%.2f"; + + private final static MathContext FOUR_SIG_FIG = new MathContext(4); + protected AlignViewport av; protected AlignmentPanel ap; @@ -64,7 +69,7 @@ public abstract class AnnotationRowFilter extends JPanel protected JCheckBox percentThreshold = new JCheckBox(); - protected JSlider slider = new JSlider(); + protected Slider slider; protected JTextField thresholdValue = new JTextField(20); @@ -103,6 +108,8 @@ public abstract class AnnotationRowFilter extends JPanel { this.av = viewport; this.ap = alignPanel; + this.slider = new Slider(0f, 100f, 50f); + thresholdValue.addFocusListener(new FocusAdapter() { @Override @@ -140,16 +147,62 @@ public abstract class AnnotationRowFilter extends JPanel adjusting = true; if (percentThreshold.isSelected()) { - thresholdValue.setText("" + (slider.getValue() - slider.getMinimum()) - * 100f / (slider.getMaximum() - slider.getMinimum())); + thresholdValue + .setText(String.format(TWO_DP, getSliderPercentageValue())); } else { - thresholdValue.setText((slider.getValue() / 1000f) + ""); + /* + * round to 4 significant digits without trailing zeroes + */ + float f = getSliderValue(); + BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG) + .stripTrailingZeros(); + thresholdValue.setText(formatted.toPlainString()); } adjusting = oldadj; } + /** + * Answers the value of the slider position (descaled to 'true' value) + * + * @return + */ + protected float getSliderValue() + { + return slider.getSliderValue(); + } + + /** + * Sets the slider value (scaled from the true value to the slider range) + * + * @param value + */ + protected void setSliderValue(float value) + { + slider.setSliderValue(value); + } + /** + * Answers the value of the slider position as a percentage between minimum and + * maximum of its range + * + * @return + */ + protected float getSliderPercentageValue() + { + return slider.getSliderPercentageValue(); + } + + /** + * Sets the slider position for a given percentage value of its min-max range + * + * @param pct + */ + protected void setSliderPercentageValue(float pct) + { + slider.setSliderPercentageValue(pct); + } + protected void addSliderMouseListeners() { @@ -290,6 +343,10 @@ public abstract class AnnotationRowFilter extends JPanel updateView(); } + /** + * Updates the slider position, and the display, for an update in the slider's + * text input field + */ protected void thresholdValue_actionPerformed() { try @@ -297,12 +354,11 @@ public abstract class AnnotationRowFilter extends JPanel float f = Float.parseFloat(thresholdValue.getText()); if (percentThreshold.isSelected()) { - slider.setValue(slider.getMinimum() + ((int) ((f / 100f) - * (slider.getMaximum() - slider.getMinimum())))); + setSliderPercentageValue(f); } else { - slider.setValue((int) (f * 1000)); + setSliderValue(f); } updateView(); } catch (NumberFormatException ex) @@ -528,4 +584,23 @@ public abstract class AnnotationRowFilter extends JPanel valueChanged(true); } } + + /** + * Sets the min-max range and current value of the slider, with rescaling from + * true values to slider range as required + * + * @param min + * @param max + * @param value + */ + protected void setSliderModel(float min, float max, float value) + { + slider.setSliderModel(min, max, value); + + /* + * tick mark every 10th position + */ + slider.setMajorTickSpacing( + (slider.getMaximum() - slider.getMinimum()) / 10); + } } diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java index 724cec1..3432911 100644 --- a/src/jalview/gui/AppJmolBinding.java +++ b/src/jalview/gui/AppJmolBinding.java @@ -208,11 +208,6 @@ public class AppJmolBinding extends JalviewJmolBinding AlignmentPanel ap = (alignment == null) ? appJmolWindow.getAlignmentPanel() : (AlignmentPanel) alignment; - if (ap.av.isShowSequenceFeatures()) - { - return ap.av.getAlignPanel().getSeqPanel().seqCanvas.fr; - } - - return null; + return ap.av.getAlignPanel().getFeatureRenderer(); } } diff --git a/src/jalview/gui/BlogReader.java b/src/jalview/gui/BlogReader.java index 65c8b5a..c43c699 100644 --- a/src/jalview/gui/BlogReader.java +++ b/src/jalview/gui/BlogReader.java @@ -256,7 +256,7 @@ public class BlogReader extends JPanel chan.setURL( jalview.bin.Cache.getDefault("JALVIEW_NEWS_RSS", jalview.bin.Cache.getDefault("www.jalview.org", - "http://www.jalview.org") + "https://www.jalview.org") + "/feeds/desktop/rss")); loadLastM(); _channelModel.addChannel(chan); diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index a7349b8..47bad81 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -575,7 +575,8 @@ public class ChimeraViewFrame extends StructureViewerBase /* * ensure that any newly discovered features (e.g. RESNUM) - * are added to any open feature settings dialog + * are notified to the FeatureRenderer (and added to any + * open feature settings dialog) */ FeatureRenderer fr = getBinding().getFeatureRenderer(null); if (fr != null) diff --git a/src/jalview/gui/Console.java b/src/jalview/gui/Console.java index 4c019a6..6cd8106 100644 --- a/src/jalview/gui/Console.java +++ b/src/jalview/gui/Console.java @@ -20,15 +20,20 @@ */ package jalview.gui; -import jalview.util.MessageManager; - import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.GraphicsEnvironment; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.Rectangle; import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; @@ -38,12 +43,19 @@ import java.io.PipedOutputStream; import java.io.PrintStream; import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; +import org.apache.log4j.Level; import org.apache.log4j.SimpleLayout; +import jalview.bin.Cache; +import jalview.util.MessageManager; + /** * Simple Jalview Java Console. Version 1 - allows viewing of console output * after desktop is created. Acquired with thanks from RJHM's site @@ -88,6 +100,10 @@ public class Console extends WindowAdapter private int MIN_HEIGHT = 250; + private JComboBox logLevelCombo = new JComboBox(); + + protected Level startingLogLevel = Level.INFO; + public Console() { // create all components and add them @@ -115,17 +131,107 @@ public class Console extends WindowAdapter // textArea = cpt.getTextArea(); textArea = new JTextArea(); textArea.setEditable(false); - JButton button = new JButton(MessageManager.getString("action.clear")); + JButton clearButton = new JButton( + MessageManager.getString("action.clear")); + JButton copyToClipboardButton = new JButton( + MessageManager.getString("label.copy_to_clipboard")); + copyToClipboardButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + copyConsoleTextToClipboard(); + } + }); + copyToClipboardButton.addMouseListener(new MouseAdapter() + { + private Color bg = textArea.getBackground(); + + private Color fg = textArea.getForeground(); + + public void mousePressed(MouseEvent e) + { + textArea.setBackground(textArea.getSelectionColor()); + textArea.setForeground(textArea.getSelectedTextColor()); + } + + public void mouseReleased(MouseEvent e) + { + textArea.setBackground(bg); + textArea.setForeground(fg); + } + + }); + copyToClipboardButton.setToolTipText( + MessageManager.getString("label.copy_to_clipboard_tooltip")); + + JLabel logLevelLabel = new JLabel( + MessageManager.getString("label.log_level") + ":"); + + // logLevelCombo.addItem(Level.ALL); + logLevelCombo.addItem(Level.TRACE); + logLevelCombo.addItem(Level.DEBUG); + logLevelCombo.addItem(Level.INFO); + logLevelCombo.addItem(Level.WARN); + // logLevelCombo.addItem(Level.ERROR); + // logLevelCombo.addItem(Level.FATAL); + // logLevelCombo.addItem(Level.OFF); + // set startingLogLevel + startingLogLevel = Cache.log == null ? Level.INFO + : Cache.log.getLevel(); + setChosenLogLevelCombo(); + logLevelCombo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (Cache.log != null) + { + Cache.log.setLevel((Level) logLevelCombo.getSelectedItem()); + } + } + + }); // frame = cpt; frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(new JScrollPane(textArea), BorderLayout.CENTER); - frame.getContentPane().add(button, BorderLayout.SOUTH); + JPanel southPanel = new JPanel(); + southPanel.setLayout(new GridBagLayout()); + + JPanel logLevelPanel = new JPanel(); + logLevelPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT); + logLevelPanel.add(logLevelLabel); + logLevelPanel.add(logLevelCombo); + String logLevelTooltip = MessageManager.formatMessage( + "label.log_level_tooltip", startingLogLevel.toString()); + logLevelLabel.setToolTipText(logLevelTooltip); + logLevelCombo.setToolTipText(logLevelTooltip); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 1; + gbc.gridheight = 1; + gbc.weightx = 0.1; + southPanel.add(logLevelPanel, gbc); + + gbc.gridx++; + gbc.weightx = 0.8; + gbc.fill = GridBagConstraints.HORIZONTAL; + southPanel.add(clearButton, gbc); + + gbc.gridx++; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.NONE; + southPanel.add(copyToClipboardButton, gbc); + + southPanel.setVisible(true); + frame.getContentPane().add(southPanel, BorderLayout.SOUTH); frame.setVisible(visible); updateConsole = visible; frame.addWindowListener(this); - button.addActionListener(this); + clearButton.addActionListener(this); + if (redirect) { redirectStreams(); @@ -151,6 +257,53 @@ public class Console extends WindowAdapter textAppender.start(); } + private void setChosenLogLevelCombo() + { + setChosenLogLevelCombo(startingLogLevel); + } + + private void setChosenLogLevelCombo(Level setLogLevel) + { + logLevelCombo.setSelectedItem(setLogLevel); + if (!logLevelCombo.getSelectedItem().equals(setLogLevel)) + { + // setLogLevel not (yet) in list + if (setLogLevel != null && setLogLevel instanceof Level) + { + // add new item to list (might be set via .jalview_properties) + boolean added = false; + for (int i = 0; i < logLevelCombo.getItemCount(); i++) + { + Level l = (Level) logLevelCombo.getItemAt(i); + if (l.isGreaterOrEqual(setLogLevel)) + { + logLevelCombo.insertItemAt(setLogLevel, i); + added = true; + break; + } + } + if (!added) // lower priority than others or some confusion -- add to + // end of list + { + logLevelCombo.addItem(setLogLevel); + } + logLevelCombo.setSelectedItem(setLogLevel); + } + else + { + logLevelCombo.setSelectedItem(Level.INFO); + } + } + } + + private void copyConsoleTextToClipboard() + { + String consoleText = textArea.getText(); + StringSelection consoleTextSelection = new StringSelection(consoleText); + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + cb.setContents(consoleTextSelection, null); + } + PipedOutputStream pout = null, perr = null; public void redirectStreams() @@ -644,12 +797,19 @@ public class Console extends WindowAdapter frame.setVisible(selected); if (selected == true) { + setChosenLogLevelCombo(); redirectStreams(); updateConsole = true; frame.toFront(); } else { + // reset log level to what it was before + if (Cache.log != null) + { + Cache.log.setLevel(startingLogLevel); + } + unredirectStreams(); updateConsole = false; } diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java index b3bff0d..279d89f 100644 --- a/src/jalview/gui/CutAndPasteTransfer.java +++ b/src/jalview/gui/CutAndPasteTransfer.java @@ -272,11 +272,8 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer */ if (viewport != null && viewport.getAlignment() != null) { - if (proxyColourScheme != null) - { - viewport.applyFeaturesStyle(proxyColourScheme); - } ((AlignViewport) viewport).addAlignment(al, title); + viewport.applyFeaturesStyle(proxyColourScheme); } else { diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 7564dda..0441e5e 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -927,7 +927,7 @@ public class Desktop extends jalview.jbgui.GDesktop } catch (java.lang.ClassCastException cex) { Cache.log.warn( - "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869", + "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869", cex); } } @@ -1135,7 +1135,7 @@ public class Desktop extends jalview.jbgui.GDesktop panel.add(history); history.setPreferredSize(new Dimension(400, 20)); history.setEditable(true); - history.addItem("http://www."); + history.addItem("https://www."); String historyItems = jalview.bin.Cache.getProperty("RECENT_URL"); @@ -1351,7 +1351,7 @@ public class Desktop extends jalview.jbgui.GDesktop "..Checking..") + " is available for download from " + jalview.bin.Cache.getDefault("www.jalview.org", - "http://www.jalview.org") + "https://www.jalview.org") + " !!"); if (red) { @@ -1362,7 +1362,7 @@ public class Desktop extends jalview.jbgui.GDesktop "AUTHORFNAMES", "The Jalview Authors (See AUTHORS file for current list)") + "

      Development managed by The Barton Group, University of Dundee, Scotland, UK.
      " - + "

      For help, see the FAQ at www.jalview.org/faq and/or join the jalview-discuss@jalview.org mailing list" + + "

      For help, see the FAQ at www.jalview.org/faq and/or join the jalview-discuss@jalview.org mailing list" + "

      If you use Jalview, please cite:" + "
      Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)" + "
      Jalview Version 2 - a multiple sequence alignment editor and analysis workbench" diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index b87e4bf..aeac959 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -162,7 +162,7 @@ public class FeatureSettings extends JPanel JPanel groupPanel; - JSlider transparency = new JSlider(); + JSlider transparency= new JSlider(); private JCheckBox showComplementOnTop; diff --git a/src/jalview/gui/FeatureTypeSettings.java b/src/jalview/gui/FeatureTypeSettings.java index 5b77dfc..512cf06 100644 --- a/src/jalview/gui/FeatureTypeSettings.java +++ b/src/jalview/gui/FeatureTypeSettings.java @@ -49,6 +49,8 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.math.BigDecimal; +import java.math.MathContext; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; @@ -63,7 +65,6 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; -import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; import javax.swing.border.LineBorder; @@ -79,6 +80,8 @@ import javax.swing.event.ChangeListener; */ public class FeatureTypeSettings extends JalviewDialog { + private final static MathContext FOUR_SIG_FIG = new MathContext(4); + private final static String LABEL_18N = MessageManager .getString("label.label"); @@ -142,11 +145,6 @@ public class FeatureTypeSettings extends JalviewDialog private float max; /* - * scale factor for conversion between absolute min-max and slider - */ - private float scaleFactor; - - /* * radio button group, to select what to colour by: * simple colour, by category (text), or graduated */ @@ -169,7 +167,7 @@ public class FeatureTypeSettings extends JalviewDialog private JComboBox threshold = new JComboBox<>(); - private JSlider slider = new JSlider(); + private Slider slider; private JTextField thresholdValue = new JTextField(20); @@ -348,12 +346,11 @@ public class FeatureTypeSettings extends JalviewDialog * update min-max scaling if there is a range to work with, * else disable the widgets (this shouldn't happen if only * valid options are offered in the combo box) + * offset slider to have only non-negative values if necessary (JAL-2983) */ - scaleFactor = (max == min) ? 1f : 100f / (max - min); - float range = (max - min) * scaleFactor; - slider.setMinimum((int) (min * scaleFactor)); - slider.setMaximum((int) (max * scaleFactor)); - slider.setMajorTickSpacing((int) (range / 10f)); + slider.setSliderModel(min, max, min); + slider.setMajorTickSpacing( + (int) ((slider.getMaximum() - slider.getMinimum()) / 10f)); threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); @@ -365,8 +362,8 @@ public class FeatureTypeSettings extends JalviewDialog fc.isAboveThreshold() ? ABOVE_THRESHOLD_OPTION : BELOW_THRESHOLD_OPTION); slider.setEnabled(true); - slider.setValue((int) (fc.getThreshold() * scaleFactor)); - thresholdValue.setText(String.valueOf(fc.getThreshold())); + slider.setSliderValue(fc.getThreshold()); + setThresholdValueText(fc.getThreshold()); thresholdValue.setEnabled(true); thresholdIsMin.setEnabled(true); } @@ -641,6 +638,7 @@ public class FeatureTypeSettings extends JalviewDialog thresholdValue_actionPerformed(); } }); + slider = new Slider(0f, 100f, 50f); slider.setPaintLabels(false); slider.setPaintTicks(true); slider.setBackground(Color.white); @@ -657,8 +655,7 @@ public class FeatureTypeSettings extends JalviewDialog { if (!adjusting) { - thresholdValue - .setText(String.valueOf(slider.getValue() / scaleFactor)); + setThresholdValueText(slider.getSliderValue()); thresholdValue.setBackground(Color.white); // to reset red for invalid sliderValueChanged(); } @@ -1033,8 +1030,8 @@ public class FeatureTypeSettings extends JalviewDialog float f = Float.parseFloat(thresholdValue.getText()); f = Float.max(f, this.min); f = Float.min(f, this.max); - thresholdValue.setText(String.valueOf(f)); - slider.setValue((int) (f * scaleFactor)); + setThresholdValueText(f); + slider.setSliderValue(f); threshline.value = f; thresholdValue.setBackground(Color.white); // ok adjusting = false; @@ -1047,13 +1044,25 @@ public class FeatureTypeSettings extends JalviewDialog } /** + * Sets the text field for threshold value, rounded to four significant figures + * + * @param f + */ + void setThresholdValueText(float f) + { + BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG) + .stripTrailingZeros(); + thresholdValue.setText(formatted.toPlainString()); + } + + /** * Action on change of threshold slider value. This may be done interactively * (by moving the slider), or programmatically (to update the slider after * manual input of a threshold value). */ protected void sliderValueChanged() { - threshline.value = getRoundedSliderValue(); + threshline.value = slider.getSliderValue(); /* * repaint alignment, but not Overview or structure, @@ -1062,21 +1071,6 @@ public class FeatureTypeSettings extends JalviewDialog colourChanged(false); } - /** - * Converts the slider value to its absolute value by dividing by the - * scaleFactor. Rounding errors are squashed by forcing min/max of slider - * range to the actual min/max of feature score range - * - * @return - */ - private float getRoundedSliderValue() - { - int value = slider.getValue(); - float f = value == slider.getMaximum() ? max - : (value == slider.getMinimum() ? min : value / scaleFactor); - return f; - } - void addActionListener(ActionListener listener) { if (featureSettings != null) diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index a4d7ad0..537c323 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -28,9 +28,9 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.jbgui.GFinder; import jalview.util.MessageManager; -import jalview.viewmodel.AlignmentViewport; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; @@ -65,7 +65,7 @@ public class Finder extends GFinder private static final int MIN_HEIGHT = 120; - private static final int MY_HEIGHT = 120; + private static final int MY_HEIGHT = 150; private static final int MY_WIDTH = 400; @@ -82,33 +82,17 @@ public class Finder extends GFinder private SearchResultsI searchResults; - /* - * true if we only search a given alignment view - */ - private boolean focusfixed; - - /** - * Creates a new Finder object with no associated viewport or panel. Each Find - * or Find Next action will act on whichever viewport has focus at the time. - */ - public Finder() - { - this(null, null); - } - /** - * Constructor given an associated viewport and alignment panel. Constructs - * and displays an internal frame where the user can enter a search string. + * Constructor given an associated alignment panel. Constructs and displays an + * internal frame where the user can enter a search string. * - * @param viewport * @param alignPanel */ - public Finder(AlignmentViewport viewport, AlignmentPanel alignPanel) + public Finder(AlignmentPanel alignPanel) { - av = viewport; + av = alignPanel.getAlignViewport(); ap = alignPanel; finders = new HashMap<>(); - focusfixed = viewport != null; frame = new JInternalFrame(); frame.setContentPane(this); frame.setLayer(JLayeredPane.PALETTE_LAYER); @@ -122,6 +106,7 @@ public class Finder extends GFinder } }); addEscapeHandler(); + Desktop.addInternalFrame(frame, MessageManager.getString("label.find"), MY_WIDTH, MY_HEIGHT); frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); @@ -172,13 +157,14 @@ public class Finder extends GFinder /** * if !focusfixed and not in a desktop environment, checks that av and ap are * valid. Otherwise, gets the topmost alignment window and sets av and ap - * accordingly + * accordingly. Also sets the 'ignore hidden' checkbox disabled if the viewport + * has no hidden columns. * * @return false if no alignment window was found */ boolean getFocusedViewport() { - if (focusfixed || Desktop.desktop == null) + if (Desktop.desktop == null) { if (ap != null && av != null) { @@ -198,6 +184,7 @@ public class Finder extends GFinder { av = ((AlignFrame) alignFrame).viewport; ap = ((AlignFrame) alignFrame).alignPanel; + ignoreHidden.setEnabled(av.hasHiddenColumns()); return true; } } @@ -274,13 +261,16 @@ public class Finder extends GFinder boolean isCaseSensitive = caseSensitive.isSelected(); boolean doSearchDescription = searchDescription.isSelected(); + boolean skipHidden = ignoreHidden.isSelected(); if (doFindAll) { - finder.findAll(searchString, isCaseSensitive, doSearchDescription); + finder.findAll(searchString, isCaseSensitive, doSearchDescription, + skipHidden); } else { - finder.findNext(searchString, isCaseSensitive, doSearchDescription); + finder.findNext(searchString, isCaseSensitive, doSearchDescription, + skipHidden); } searchResults = finder.getSearchResults(); @@ -314,11 +304,11 @@ public class Finder extends GFinder : ""; if (searchResults != null) { - if (idMatch.size() > 0 && searchResults.getSize() > 0) + if (idMatch.size() > 0 && searchResults.getCount() > 0) { message += " and "; } - message += searchResults.getSize() + message += searchResults.getCount() + " subsequence matches found."; } JvOptionPane.showInternalMessageDialog(this, message, null, @@ -388,4 +378,15 @@ public class Finder extends GFinder ap.alignFrame.requestFocus(); } } + + @Override + protected void paintComponent(Graphics g) + { + /* + * enable 'hidden regions' option only if + * 'top' viewport has hidden columns + */ + getFocusedViewport(); + super.paintComponent(g); + } } diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index a925227..541c253 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -320,6 +320,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI if (hasHiddenRows || alignViewport.isDisplayReferenceSeq()) { g.setFont(getHiddenFont(sequence, alignViewport)); + fm = g.getFontMetrics(); } // Selected sequence colours diff --git a/src/jalview/gui/OptsAndParamsPage.java b/src/jalview/gui/OptsAndParamsPage.java index 5342c90..92b612a 100644 --- a/src/jalview/gui/OptsAndParamsPage.java +++ b/src/jalview/gui/OptsAndParamsPage.java @@ -53,7 +53,6 @@ import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; -import javax.swing.JSlider; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.TitledBorder; @@ -93,7 +92,7 @@ public class OptsAndParamsPage JLabel optlabel = new JLabel(); - JComboBox val = new JComboBox(); + JComboBox val = new JComboBox<>(); public OptionBox(OptionI opt) { @@ -127,7 +126,7 @@ public class OptsAndParamsPage } } add(enabled, BorderLayout.NORTH); - for (Object str : opt.getPossibleValues()) + for (String str : opt.getPossibleValues()) { val.addItem(str); } @@ -277,7 +276,7 @@ public class OptsAndParamsPage boolean choice = false; - JComboBox choicebox; + JComboBox choicebox; JPanel controlPanel = new JPanel(); @@ -289,7 +288,7 @@ public class OptsAndParamsPage boolean integ = false; - Object lastVal; + String lastVal; ParameterI parameter; @@ -299,7 +298,7 @@ public class OptsAndParamsPage JButton showDesc = new JButton(); - JSlider slider = null; + Slider slider = null; JTextArea string = new JTextArea(); @@ -363,7 +362,7 @@ public class OptsAndParamsPage validate(); } - private void makeExpanderParam(ParameterI parm) + private void makeExpanderParam(final ParameterI parm) { setPreferredSize(new Dimension(PARAM_WIDTH, PARAM_CLOSEDHEIGHT)); setBorder(new TitledBorder(parm.getName())); @@ -451,6 +450,9 @@ public class OptsAndParamsPage validate(); } + /** + * Action on input in text field + */ @Override public void actionPerformed(ActionEvent e) { @@ -467,24 +469,20 @@ public class OptsAndParamsPage private void checkIfModified() { - Object cstate = updateSliderFromValueField(); - boolean notmod = false; - if (cstate.getClass() == lastVal.getClass()) - { - if (cstate instanceof int[]) - { - notmod = (((int[]) cstate)[0] == ((int[]) lastVal)[0]); - } - else if (cstate instanceof float[]) - { - notmod = (((float[]) cstate)[0] == ((float[]) lastVal)[0]); - } - else if (cstate instanceof String[]) - { - notmod = (((String[]) cstate)[0].equals(((String[]) lastVal)[0])); - } - } - pmdialogbox.argSetModified(this, !notmod); + Object cstate = getCurrentValue(); + boolean modified = !cstate.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 @@ -566,16 +564,20 @@ public class OptsAndParamsPage } + /** + * Action on change of slider value + */ @Override public void stateChanged(ChangeEvent e) { if (!adjusting) { - valueField.setText("" + ((integ) ? ("" + slider.getValue()) - : ("" + slider.getValue() / 1000f))); + float value = slider.getSliderValue(); + valueField.setText( + integ ? Integer.toString((int) value) + : Float.toString(value)); checkIfModified(); } - } public void updateControls(ParameterI parm) @@ -586,14 +588,12 @@ public class OptsAndParamsPage { if (choice) { - choicebox = new JComboBox(); + choicebox = new JComboBox<>(); choicebox.addActionListener(this); controlPanel.add(choicebox, BorderLayout.CENTER); } else { - slider = new JSlider(); - slider.addChangeListener(this); valueField = new JTextField(); valueField.addActionListener(this); valueField.addKeyListener(new KeyListener() @@ -622,9 +622,13 @@ public class OptsAndParamsPage } }); 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); - } } @@ -634,8 +638,8 @@ public class OptsAndParamsPage { if (init) { - List vals = parm.getPossibleValues(); - for (Object val : vals) + List vals = parm.getPossibleValues(); + for (String val : vals) { choicebox.addItem(val); } @@ -651,96 +655,105 @@ public class OptsAndParamsPage valueField.setText(parm.getValue()); } } - lastVal = updateSliderFromValueField(); + lastVal = getCurrentValue(); adjusting = false; } - public Object updateSliderFromValueField() + private Slider makeSlider(ValueConstrainI validValue) + { + if (validValue != null) + { + final Number minValue = validValue.getMin(); + final Number maxValue = validValue.getMax(); + if (minValue != null && maxValue != null) + { + return new Slider(minValue.floatValue(), maxValue.floatValue(), + minValue.floatValue()); + } + } + + /* + * otherwise, a nominal slider which will not be visible + */ + return new Slider(0, 100, 50); + } + + public void updateSliderFromValueField() { - int iVal; - float fVal; if (validator != null) { + final Number minValue = validator.getMin(); + final Number maxValue = validator.getMax(); if (integ) { - iVal = 0; + int iVal = 0; try { valueField.setText(valueField.getText().trim()); iVal = Integer.valueOf(valueField.getText()); - if (validator.getMin() != null - && validator.getMin().intValue() > iVal) + if (minValue != null + && minValue.intValue() > iVal) { - iVal = validator.getMin().intValue(); + iVal = minValue.intValue(); // TODO: provide visual indication that hard limit was reached for // this parameter } - if (validator.getMax() != null - && validator.getMax().intValue() < iVal) + if (maxValue != null && maxValue.intValue() < iVal) { - iVal = validator.getMax().intValue(); - // TODO: provide visual indication that hard limit was reached for - // this parameter + iVal = maxValue.intValue(); } - } catch (Exception e) + } catch (NumberFormatException e) { + System.err.println(e.toString()); } - ; - // update value field to reflect any bound checking we performed. - valueField.setText("" + iVal); - if (validator.getMin() != null && validator.getMax() != null) + if (minValue != null || maxValue != null) { - slider.getModel().setRangeProperties(iVal, 1, - validator.getMin().intValue(), - validator.getMax().intValue() + 1, true); + valueField.setText(String.valueOf(iVal)); + slider.setSliderValue(iVal); } else { slider.setVisible(false); } - return new int[] { iVal }; } else { - fVal = 0f; + float fVal = 0f; try { valueField.setText(valueField.getText().trim()); fVal = Float.valueOf(valueField.getText()); - if (validator.getMin() != null - && validator.getMin().floatValue() > fVal) + if (minValue != null + && minValue.floatValue() > fVal) { - fVal = validator.getMin().floatValue(); + 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 (validator.getMax() != null - && validator.getMax().floatValue() < fVal) + if (maxValue != null + && maxValue.floatValue() < fVal) { - fVal = validator.getMax().floatValue(); + 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 (Exception e) + } catch (NumberFormatException e) { + System.err.println(e.toString()); } - ; - if (validator.getMin() != null && validator.getMax() != null) + if (minValue != null && maxValue != null) { - slider.getModel().setRangeProperties((int) (fVal * 1000f), 1, - (int) (validator.getMin().floatValue() * 1000f), - 1 + (int) (validator.getMax().floatValue() * 1000f), - true); + slider.setSliderModel(minValue.floatValue(), + maxValue.floatValue(), fVal); } else { slider.setVisible(false); } - return new float[] { fVal }; } } else @@ -748,14 +761,8 @@ public class OptsAndParamsPage if (!choice) { slider.setVisible(false); - return new String[] { valueField.getText().trim() }; - } - else - { - return new String[] { (String) choicebox.getSelectedItem() }; } } - } } @@ -801,9 +808,9 @@ public class OptsAndParamsPage URL linkImageURL = getClass().getResource("/images/link.gif"); - Map optSet = new java.util.LinkedHashMap(); + Map optSet = new java.util.LinkedHashMap<>(); - Map paramSet = new java.util.LinkedHashMap(); + Map paramSet = new java.util.LinkedHashMap<>(); public Map getOptSet() { @@ -904,7 +911,7 @@ public class OptsAndParamsPage */ public List getCurrentSettings() { - List argSet = new ArrayList(); + List argSet = new ArrayList<>(); for (OptionBox opts : getOptSet().values()) { OptionI opt = opts.getOptionIfEnabled(); diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index 4f660a2..3512e42 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -141,6 +141,12 @@ public class PCAPanel extends GPCAPanel protected void close_actionPerformed() { setPcaModel(null); + if (this.rc != null) + { + this.rc.sequencePoints = null; + this.rc.setAxisEndPoints(null); + this.rc = null; + } } @Override diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java index 1f61bae..f51bfbf 100755 --- a/src/jalview/gui/Preferences.java +++ b/src/jalview/gui/Preferences.java @@ -289,8 +289,8 @@ public class Preferences extends GPreferences startupCheckbox .setSelected(Cache.getDefault("SHOW_STARTUP_FILE", true)); startupFileTextfield.setText(Cache.getDefault("STARTUP_FILE", - Cache.getDefault("www.jalview.org", "http://www.jalview.org") - + "/examples/exampleFile_2_3.jar")); + Cache.getDefault("www.jalview.org", "https://www.jalview.org") + + "/examples/exampleFile_2_7.jvp")); /* * Set Colours tab defaults diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index cd974a4..bb85948 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -460,47 +460,80 @@ public class SeqPanel extends JPanel void moveCursor(int dx, int dy) { - seqCanvas.cursorX += dx; - seqCanvas.cursorY += dy; - + moveCursor(dx, dy,false); + } + void moveCursor(int dx, int dy, boolean nextWord) + { HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX)) + if (nextWord) { - int original = seqCanvas.cursorX - dx; int maxWidth = av.getAlignment().getWidth(); - - if (!hidden.isVisible(seqCanvas.cursorX)) - { - int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx); - int[] region = hidden.getRegionWithEdgeAtRes(visx); - - if (region != null) // just in case + int maxHeight=av.getAlignment().getHeight(); + SequenceI seqAtRow = av.getAlignment().getSequenceAt(seqCanvas.cursorY); + // look for next gap or residue + boolean isGap = Comparison.isGap(seqAtRow.getCharAt(seqCanvas.cursorX)); + int p = seqCanvas.cursorX,lastP,r=seqCanvas.cursorY,lastR; + do + { + lastP = p; + lastR = r; + if (dy != 0) { - if (dx == 1) + r += dy; + if (r < 0) { - // moving right - seqCanvas.cursorX = region[1] + 1; + r = 0; } - else if (dx == -1) + if (r >= maxHeight) { - // moving left - seqCanvas.cursorX = region[0] - 1; + r = maxHeight - 1; } + seqAtRow = av.getAlignment().getSequenceAt(r); } - seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX; - } + p = nextVisible(hidden, maxWidth, p, dx); + } while ((dx != 0 ? p != lastP : r != lastR) + && isGap == Comparison.isGap(seqAtRow.getCharAt(p))); + seqCanvas.cursorX=p; + seqCanvas.cursorY=r; + } else { + int maxWidth = av.getAlignment().getWidth(); + seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX, dx); + seqCanvas.cursorY += dy; + } + scrollToVisible(false); + } + + private int nextVisible(HiddenColumns hidden,int maxWidth, int original, int dx) + { + int newCursorX=original+dx; + if (av.hasHiddenColumns() && !hidden.isVisible(newCursorX)) + { + int visx = hidden.absoluteToVisibleColumn(newCursorX - dx); + int[] region = hidden.getRegionWithEdgeAtRes(visx); - if (seqCanvas.cursorX >= maxWidth - || !hidden.isVisible(seqCanvas.cursorX)) + if (region != null) // just in case { - seqCanvas.cursorX = original; + if (dx == 1) + { + // moving right + newCursorX = region[1] + 1; + } + else if (dx == -1) + { + // moving left + newCursorX = region[0] - 1; + } } } - - scrollToVisible(false); + newCursorX = (newCursorX < 0) ? 0 : newCursorX; + if (newCursorX >= maxWidth + || !hidden.isVisible(newCursorX)) + { + newCursorX = original; + } + return newCursorX; } - /** * Scroll to make the cursor visible in the viewport. * @@ -893,11 +926,12 @@ public class SeqPanel extends JPanel AlignFrame af = Desktop.getAlignFrameFor(complement); FeatureRendererModel fr2 = af.getFeatureRenderer(); - int j = results.getSize(); + List matches = results.getResults(); + int j = matches.size(); List infos = new ArrayList<>(); for (int i = 0; i < j; i++) { - SearchResultMatchI match = results.getResults().get(i); + SearchResultMatchI match = matches.get(i); int pos = match.getStart(); if (pos == match.getEnd()) { @@ -1065,7 +1099,7 @@ public class SeqPanel extends JPanel pos); if (mf != null) { - unshownFeatures = seqARep.appendFeatures(tooltipText, + unshownFeatures += seqARep.appendFeatures(tooltipText, pos, mf, fr2, MAX_TOOLTIP_LENGTH); } } @@ -1200,7 +1234,7 @@ public class SeqPanel extends JPanel { char sequenceChar = sequence.getCharAt(column); int pos = sequence.findPosition(column); - setStatusMessage(sequence, seqIndex, sequenceChar, pos); + setStatusMessage(sequence.getName(), seqIndex, sequenceChar, pos); return pos; } @@ -1216,7 +1250,7 @@ public class SeqPanel extends JPanel * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2) * * - * @param sequence + * @param seqName * @param seqIndex * sequence position in the alignment (1..) * @param sequenceChar @@ -1224,7 +1258,7 @@ public class SeqPanel extends JPanel * @param residuePos * the sequence residue position (if not over a gap) */ - protected void setStatusMessage(SequenceI sequence, int seqIndex, + protected void setStatusMessage(String seqName, int seqIndex, char sequenceChar, int residuePos) { StringBuilder text = new StringBuilder(32); @@ -1234,7 +1268,7 @@ public class SeqPanel extends JPanel */ String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1); text.append("Sequence").append(seqno).append(" ID: ") - .append(sequence.getName()); + .append(seqName); String residue = null; @@ -1279,7 +1313,8 @@ public class SeqPanel extends JPanel { return; } - SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence(); + SequenceI alignedSeq = al.getSequenceAt(sequenceIndex); + SequenceI ds = alignedSeq.getDatasetSequence(); for (SearchResultMatchI m : results.getResults()) { SequenceI seq = m.getSequence(); @@ -1291,8 +1326,8 @@ public class SeqPanel extends JPanel if (seq == ds) { int start = m.getStart(); - setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1), - start); + setStatusMessage(alignedSeq.getName(), sequenceIndex, + seq.getCharAt(start - 1), start); return; } } diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index 1c4e6a6..7451646 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -1023,10 +1023,7 @@ public class SequenceFetcher extends JPanel implements Runnable } } - if (preferredFeatureColours != null) - { - af.getViewport().applyFeaturesStyle(preferredFeatureColours); - } + af.getViewport().applyFeaturesStyle(preferredFeatureColours); if (Cache.getDefault("HIDE_INTRONS", true)) { af.hideFeatureColumns(SequenceOntologyI.EXON, false); diff --git a/src/jalview/gui/Slider.java b/src/jalview/gui/Slider.java new file mode 100644 index 0000000..714e770 --- /dev/null +++ b/src/jalview/gui/Slider.java @@ -0,0 +1,119 @@ +package jalview.gui; + +import javax.swing.JSlider; + +/** + * A modified {@code javax.swing.JSlider} that + *
        + *
      • supports float valued numbers (by scaling up integer values)
      • + *
      • rescales 'true' value range to avoid negative values, as these are not + * rendered correctly by some look and feel libraries
      • + *
      + * + * @author gmcarstairs + */ +@SuppressWarnings("serial") +public class Slider extends JSlider +{ + /* + * the number of nominal positions the slider represents + * (higher number = more fine-grained positioning) + */ + private static final int SCALE_TICKS = 1000; + + /* + * 'true' value corresponding to zero on the slider + */ + private float trueMin; + + /* + * 'true' value corresponding to slider maximum + */ + private float trueMax; + + /* + * scaleFactor applied to true value range to give a + * slider range of 0 - 100 + */ + private float sliderScaleFactor; + + /** + * Constructor that rescales min - max to 0 - 100 for the slider + * + * @param min + * @param max + * @param value + */ + public Slider(float min, float max, float value) + { + super(); + setSliderModel(min, max, value); + } + + /** + * Sets the min-max range and current value of the slider, with rescaling from + * true values to slider range as required + * + * @param min + * @param max + * @param value + */ + public void setSliderModel(float min, float max, float value) + { + trueMin = min; + trueMax = max; + setMinimum(0); + sliderScaleFactor = SCALE_TICKS / (max - min); + int sliderMax = (int) ((max - min) * sliderScaleFactor); + setMaximum(sliderMax); + setSliderValue(value); + } + + /** + * Answers the value of the slider position (descaled to 'true' value) + * + * @return + */ + public float getSliderValue() + { + /* + * convert slider max to 'true max' in case of rounding errors + */ + int value = getValue(); + return value == getMaximum() ? trueMax + : value / sliderScaleFactor + trueMin; + } + + /** + * Sets the slider value (scaled from the true value to the slider range) + * + * @param value + */ + public void setSliderValue(float value) + { + setValue(Math.round((value - trueMin) * sliderScaleFactor)); + } + + /** + * Answers the value of the slider position as a percentage between minimum and + * maximum of its range + * + * @return + */ + public float getSliderPercentageValue() + { + return (getValue() - getMinimum()) * 100f + / (getMaximum() - getMinimum()); + } + + /** + * Sets the slider position for a given percentage value of its min-max range + * + * @param pct + */ + public void setSliderPercentageValue(float pct) + { + float pc = pct / 100f * getMaximum(); + setValue((int) pc); + } +} diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index 574cf04..f7c93db 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -814,7 +814,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI if (c != null && c instanceof AlignFrame) { AlignFrame af = (AlignFrame) c; - new Finder(af.viewport, af.alignPanel); + new Finder(af.alignPanel); } } }; diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index ca4f84e..6c9c892 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -159,6 +159,7 @@ public class TreePanel extends GTreePanel { getViewport().removePropertyChangeListener(listener); } + releaseReferences(); } }); @@ -168,6 +169,17 @@ public class TreePanel extends GTreePanel } /** + * Ensure any potentially large object references are nulled + */ + public void releaseReferences() + { + this.tree = null; + this.treeCanvas.tree = null; + this.treeCanvas.nodeHash = null; + this.treeCanvas.nameHash = null; + } + + /** * @return */ protected PropertyChangeListener addAlignmentListener() diff --git a/src/jalview/io/BackupFiles.java b/src/jalview/io/BackupFiles.java index 0150579..2acd6cc 100644 --- a/src/jalview/io/BackupFiles.java +++ b/src/jalview/io/BackupFiles.java @@ -20,19 +20,24 @@ */ package jalview.io; -import jalview.bin.Cache; -import jalview.gui.Desktop; -import jalview.gui.JvOptionPane; -import jalview.util.MessageManager; - import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; +import jalview.bin.Cache; +import jalview.gui.Desktop; +import jalview.gui.JvOptionPane; +import jalview.util.MessageManager; +import jalview.util.Platform; + /* * BackupFiles used for manipulating (naming rolling/deleting) backup/version files when an alignment or project file is saved. * User configurable options are: @@ -95,6 +100,10 @@ public class BackupFiles private static final SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); + private static final String newTempFileSuffix = "_newfile"; + + private static final String oldTempFileSuffix = "_oldfile_tobedeleted"; + public BackupFiles(String filename) { this(new File(filename)); @@ -106,7 +115,8 @@ public class BackupFiles { classInit(); this.file = file; - BackupFilesPresetEntry bfpe = BackupFilesPresetEntry.getSavedBackupEntry(); + BackupFilesPresetEntry bfpe = BackupFilesPresetEntry + .getSavedBackupEntry(); this.suffix = bfpe.suffix; this.noMax = bfpe.keepAll; this.max = bfpe.rollMax; @@ -121,30 +131,48 @@ public class BackupFiles { String tempfilename = file.getName(); File tempdir = file.getParentFile(); - temp = File.createTempFile(tempfilename, TEMP_FILE_EXT + "_newfile", - tempdir); + Cache.log.trace( + "BACKUPFILES [file!=null] attempting to create temp file for " + + tempfilename + " in dir " + tempdir); + temp = File.createTempFile(tempfilename, + TEMP_FILE_EXT + newTempFileSuffix, tempdir); + Cache.log.debug( + "BACKUPFILES using temp file " + temp.getAbsolutePath()); } else { + Cache.log.trace( + "BACKUPFILES [file==null] attempting to create default temp file " + + DEFAULT_TEMP_FILE + " with extension " + + TEMP_FILE_EXT); temp = File.createTempFile(DEFAULT_TEMP_FILE, TEMP_FILE_EXT); } } catch (IOException e) { - System.out.println( - "Could not create temp file to save into (IOException)"); + Cache.log + .error("Could not create temp file to save to (IOException)"); + Cache.log.error(e.getMessage()); + Cache.log.debug(Cache.getStackTraceString(e)); } catch (Exception e) { - System.out.println("Exception ctreating temp file for saving"); + Cache.log.error("Exception ctreating temp file for saving"); + Cache.log.debug(Cache.getStackTraceString(e)); } this.setTempFile(temp); } public static void classInit() { - setEnabled(Cache.getDefault(ENABLED, true)); + Cache.initLogger(); + Cache.log.trace("BACKUPFILES classInit"); + boolean e = Cache.getDefault(ENABLED, true); + setEnabled(e); + Cache.log.trace("BACKUPFILES " + (e ? "enabled" : "disabled")); BackupFilesPresetEntry bfpe = BackupFilesPresetEntry .getSavedBackupEntry(); + Cache.log.trace("BACKUPFILES preset scheme " + bfpe.toString()); setConfirmDelete(bfpe.confirmDelete); + Cache.log.trace("BACKUPFILES confirm delete " + bfpe.confirmDelete); } public static void setEnabled(boolean flag) @@ -188,9 +216,10 @@ public class BackupFiles path = this.getTempFile().getCanonicalPath(); } catch (IOException e) { - System.out.println( + Cache.log.error( "IOException when getting Canonical Path of temp file '" + this.getTempFile().getName() + "'"); + Cache.log.debug(Cache.getStackTraceString(e)); } return path; } @@ -209,7 +238,7 @@ public class BackupFiles public boolean renameTempFile() { - return tempFile.renameTo(file); + return moveFileToFile(tempFile, file); } // roll the backupfiles @@ -226,24 +255,35 @@ public class BackupFiles || suffix.length() == 0) { // nothing to do + Cache.log.debug("BACKUPFILES rollBackupFiles nothing to do." + ", " + + "filename: " + (file != null ? file.getName() : "null") + + ", " + "file exists: " + file.exists() + ", " + "enabled: " + + enabled + ", " + "max: " + max + ", " + "suffix: '" + suffix + + "'"); return true; } + Cache.log.trace("BACKUPFILES rollBackupFiles starting"); + String dir = ""; File dirFile; try { dirFile = file.getParentFile(); dir = dirFile.getCanonicalPath(); + Cache.log.trace("BACKUPFILES dir: " + dir); } catch (Exception e) { - System.out.println( + Cache.log.error( "Could not get canonical path for file '" + file + "'"); + Cache.log.error(e.getMessage()); + Cache.log.debug(Cache.getStackTraceString(e)); return false; } String filename = file.getName(); String basename = filename; + Cache.log.trace("BACKUPFILES filename is " + filename); boolean ret = true; // Create/move backups up one @@ -255,9 +295,13 @@ public class BackupFiles File[] backupFiles = dirFile.listFiles(bff); int nextIndexNum = 0; + Cache.log + .trace("BACKUPFILES backupFiles.length: " + backupFiles.length); if (backupFiles.length == 0) { // No other backup files. Just need to move existing file to backupfile_1 + Cache.log.trace( + "BACKUPFILES no existing backup files, setting index to 1"); nextIndexNum = 1; } else @@ -270,7 +314,7 @@ public class BackupFiles if (reverseOrder) { // backup style numbering - + Cache.log.trace("BACKUPFILES rolling files in reverse order"); int tempMax = noMax ? -1 : max; // noMax == true means no limits @@ -287,7 +331,7 @@ public class BackupFiles tempMax = i; } } - + File previousFile = null; File fileToBeDeleted = null; for (int n = tempMax; n > 0; n--) @@ -302,6 +346,7 @@ public class BackupFiles // no "oldest" file to delete previousFile = backupfile_n; fileToBeDeleted = null; + Cache.log.trace("BACKUPFILES No oldest file to delete"); continue; } @@ -312,19 +357,23 @@ public class BackupFiles File replacementFile = backupfile_n; long fileToBeDeletedLMT = fileToBeDeleted.lastModified(); long replacementFileLMT = replacementFile.lastModified(); + Cache.log.trace("BACKUPFILES fileToBeDeleted is " + + fileToBeDeleted.getAbsolutePath()); + Cache.log.trace("BACKUPFILES replacementFile is " + + backupfile_n.getAbsolutePath()); try { File oldestTempFile = nextTempFile(fileToBeDeleted.getName(), dirFile); - + if (fileToBeDeletedLMT > replacementFileLMT) { String fileToBeDeletedLMTString = sdf .format(fileToBeDeletedLMT); String replacementFileLMTString = sdf .format(replacementFileLMT); - System.out.println("WARNING! I am set to delete backupfile " + Cache.log.warn("WARNING! I am set to delete backupfile " + fileToBeDeleted.getName() + " has modification time " + fileToBeDeletedLMTString @@ -335,6 +384,11 @@ public class BackupFiles boolean delete = confirmNewerDeleteFile(fileToBeDeleted, replacementFile, true); + Cache.log.trace("BACKUPFILES " + + (delete ? "confirmed" : "not") + " deleting file " + + fileToBeDeleted.getAbsolutePath() + + " which is newer than " + + replacementFile.getAbsolutePath()); if (delete) { @@ -343,21 +397,27 @@ public class BackupFiles } else { - fileToBeDeleted.renameTo(oldestTempFile); + Cache.log.debug("BACKUPFILES moving " + + fileToBeDeleted.getAbsolutePath() + " to " + + oldestTempFile.getAbsolutePath()); + moveFileToFile(fileToBeDeleted, oldestTempFile); } } else { - fileToBeDeleted.renameTo(oldestTempFile); + Cache.log.debug("BACKUPFILES going to move " + + fileToBeDeleted.getAbsolutePath() + " to " + + oldestTempFile.getAbsolutePath()); + moveFileToFile(fileToBeDeleted, oldestTempFile); addDeleteFile(oldestTempFile); } } catch (Exception e) { - System.out.println( + Cache.log.error( "Error occurred, probably making new temp file for '" + fileToBeDeleted.getName() + "'"); - e.printStackTrace(); + Cache.log.error(Cache.getStackTraceString(e)); } // reset @@ -372,7 +432,9 @@ public class BackupFiles { if (previousFile != null) { - ret = ret && backupfile_n.renameTo(previousFile); + // using boolean '&' instead of '&&' as don't want moveFileToFile + // attempt to be conditional (short-circuit) + ret = ret & moveFileToFile(backupfile_n, previousFile); } } @@ -382,19 +444,37 @@ public class BackupFiles // index to use for the latest backup nextIndexNum = 1; } - else + else // not reverse numbering { // version style numbering (with earliest file deletion if max files // reached) bfTreeMap.values().toArray(backupFiles); + StringBuilder bfsb = new StringBuilder(); + for (int i = 0; i < backupFiles.length; i++) + { + if (bfsb.length() > 0) + { + bfsb.append(", "); + } + bfsb.append(backupFiles[i].getName()); + } + Cache.log.trace("BACKUPFILES backupFiles: " + bfsb.toString()); // noMax == true means keep all backup files if ((!noMax) && bfTreeMap.size() >= max) { + Cache.log.trace("BACKUPFILES noMax: " + noMax + ", " + "max: " + + max + ", " + "bfTreeMap.size(): " + bfTreeMap.size()); // need to delete some files to keep number of backups to designated - // max - int numToDelete = bfTreeMap.size() - max + 1; + // max. + // Note that if the suffix is not numbered then do not delete any + // backup files later or we'll delete the new backup file (there can + // be only one). + int numToDelete = suffix.indexOf(NUM_PLACEHOLDER) > -1 + ? bfTreeMap.size() - max + 1 + : 0; + Cache.log.trace("BACKUPFILES numToDelete: " + numToDelete); // the "replacement" file is the latest backup file being kept (it's // not replacing though) File replacementFile = numToDelete < backupFiles.length @@ -407,6 +487,9 @@ public class BackupFiles File fileToBeDeleted = backupFiles[i]; boolean delete = true; + Cache.log.trace( + "BACKUPFILES fileToBeDeleted: " + fileToBeDeleted); + boolean newer = false; if (replacementFile != null) { @@ -421,14 +504,13 @@ public class BackupFiles String replacementFileLMTString = sdf .format(replacementFileLMT); - System.out - .println("WARNING! I am set to delete backupfile '" - + fileToBeDeleted.getName() - + "' has modification time " + Cache.log.warn("WARNING! I am set to delete backupfile '" + + fileToBeDeleted.getName() + + "' has modification time " + fileToBeDeletedLMTString - + " which is newer than the oldest backupfile being kept '" + + " which is newer than the oldest backupfile being kept '" + replacementFile.getName() - + "' with modification time " + + "' with modification time " + replacementFileLMTString); delete = confirmNewerDeleteFile(fileToBeDeleted, @@ -437,17 +519,23 @@ public class BackupFiles { // User has confirmed delete -- no need to add it to the list fileToBeDeleted.delete(); + Cache.log.debug("BACKUPFILES deleting fileToBeDeleted: " + + fileToBeDeleted); delete = false; } else { // keeping file, nothing to do! + Cache.log.debug("BACKUPFILES keeping fileToBeDeleted: " + + fileToBeDeleted); } } } if (delete) { addDeleteFile(fileToBeDeleted); + Cache.log.debug("BACKUPFILES addDeleteFile(fileToBeDeleted): " + + fileToBeDeleted); } } @@ -462,10 +550,17 @@ public class BackupFiles String latestBackupFilename = dir + File.separatorChar + BackupFilenameParts.getBackupFilename(nextIndexNum, basename, suffix, digits); - ret |= file.renameTo(new File(latestBackupFilename)); - + Cache.log.trace("BACKUPFILES Moving old file [" + file + + "] to latestBackupFilename [" + latestBackupFilename + "]"); + // using boolean '&' instead of '&&' as don't want moveFileToFile attempt to + // be conditional (short-circuit) + ret = ret & moveFileToFile(file, new File(latestBackupFilename)); + Cache.log.debug( + "BACKUPFILES moving " + file + " to " + latestBackupFilename + + " was " + (ret ? "" : "NOT ") + "successful"); if (tidyUp) { + Cache.log.debug("BACKUPFILES tidying up files"); tidyUpFiles(); } @@ -521,7 +616,7 @@ public class BackupFiles saveFile = nextTempFile(ftbd.getName(), ftbd.getParentFile()); } catch (Exception e) { - System.out.println( + Cache.log.error( "Error when confirming to keep backup file newer than other backup files."); e.printStackTrace(); } @@ -529,19 +624,27 @@ public class BackupFiles "label.newerdelete_replacement_line", new String[] { ftbd.getName(), rf.getName(), ftbdLMT, rfLMT, ftbdSize, rfSize })); + // "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})."" messageSB.append("\n\n"); messageSB.append(MessageManager.formatMessage( "label.confirm_deletion_or_rename", new String[] { ftbd.getName(), saveFile.getName() })); + // "Confirm deletion of ''{0}'' or rename to ''{1}''?" String[] options = new String[] { MessageManager.getString("label.delete"), MessageManager.getString("label.rename") }; - confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop, - messageSB.toString(), - MessageManager.getString("label.backupfiles_confirm_delete"), - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE, - null, options, options[0]); + 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]); } else { @@ -549,22 +652,29 @@ public class BackupFiles .formatMessage("label.newerdelete_line", new String[] { ftbd.getName(), rf.getName(), ftbdLMT, rfLMT, ftbdSize, rfSize })); + // "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})." messageSB.append("\n\n"); messageSB.append(MessageManager .formatMessage("label.confirm_deletion", new String[] { ftbd.getName() })); + // "Confirm deletion of ''{0}''?" String[] options = new String[] { MessageManager.getString("label.delete"), MessageManager.getString("label.keep") }; - confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop, - messageSB.toString(), - MessageManager.getString("label.backupfiles_confirm_delete"), - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE, - null, options, options[0]); + 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]); } - // return should be TRUE if file is to be deleted return (confirmButton == JvOptionPane.YES_OPTION); } @@ -580,6 +690,8 @@ public class BackupFiles messageSB = new StringBuilder(); messageSB.append(MessageManager .getString("label.backupfiles_confirm_delete_old_files")); + // "Delete the following older backup files? (see the Backups tab in + // Preferences for more options)" for (int i = 0; i < deleteFiles.size(); i++) { File df = deleteFiles.get(i); @@ -590,13 +702,17 @@ public class BackupFiles new String[] { sdf.format(df.lastModified()), Long.toString(df.length()) })); + // "(modified {0}, size {1})" } - int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop, - messageSB.toString(), - MessageManager - .getString("label.backupfiles_confirm_delete"), - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE); + int confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION + : JvOptionPane.showConfirmDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_delete"), + // "Confirm delete" + JvOptionPane.YES_NO_OPTION, + JvOptionPane.WARNING_MESSAGE); doDelete = (confirmButton == JvOptionPane.YES_OPTION); } @@ -610,8 +726,10 @@ public class BackupFiles for (int i = 0; i < deleteFiles.size(); i++) { File fileToDelete = deleteFiles.get(i); + Cache.log.trace("BACKUPFILES about to delete fileToDelete:" + + fileToDelete); fileToDelete.delete(); - System.out.println("DELETING '" + fileToDelete.getName() + "'"); + Cache.log.warn("deleted '" + fileToDelete.getName() + "'"); } } @@ -621,8 +739,7 @@ public class BackupFiles } private TreeMap sortBackupFilesAsTreeMap( - File[] backupFiles, - String basename) + File[] backupFiles, String basename) { // sort the backup files (based on integer found in the suffix) using a // precomputed Hashmap for speed @@ -647,7 +764,7 @@ public class BackupFiles boolean rename = false; if (write) { - roll = this.rollBackupFiles(false); + roll = this.rollBackupFiles(false); // tidyUpFiles at the end rename = this.renameTempFile(); } @@ -661,7 +778,9 @@ public class BackupFiles if (!okay) { StringBuilder messageSB = new StringBuilder(); - messageSB.append(MessageManager.getString( "label.backupfiles_confirm_save_file_backupfiles_roll_wrong")); + messageSB.append(MessageManager.getString( + "label.backupfiles_confirm_save_file_backupfiles_roll_wrong")); + // "Something possibly went wrong with the backups of this file." if (rename) { if (messageSB.length() > 0) @@ -670,6 +789,7 @@ public class BackupFiles } messageSB.append(MessageManager.getString( "label.backupfiles_confirm_save_new_saved_file_ok")); + // "The new saved file seems okay." } else { @@ -679,13 +799,22 @@ public class BackupFiles } messageSB.append(MessageManager.getString( "label.backupfiles_confirm_save_new_saved_file_not_ok")); + // "The new saved file might not be okay." } - - int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop, - messageSB.toString(), - MessageManager - .getString("label.backupfiles_confirm_save_file"), - JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE); + if (messageSB.length() > 0) + { + messageSB.append("\n"); + } + messageSB + .append(MessageManager.getString("label.continue_operation")); + + int confirmButton = Platform.isHeadless() ? JvOptionPane.OK_OPTION + : JvOptionPane.showConfirmDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_save_file"), + // "Confirm save file" + JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE); okay = confirmButton == JvOptionPane.OK_OPTION; } if (okay) @@ -709,7 +838,7 @@ public class BackupFiles dirFile = file.getParentFile(); } catch (Exception e) { - System.out.println( + Cache.log.error( "Could not get canonical path for file '" + file + "'"); return new TreeMap<>(); } @@ -751,13 +880,49 @@ public class BackupFiles int pos = deleteFiles.indexOf(fileToBeDeleted); if (pos > -1) { + Cache.log.debug("BACKUPFILES not adding file " + + fileToBeDeleted.getAbsolutePath() + + " to the delete list (already at index" + pos + ")"); return true; } else { + Cache.log.debug("BACKUPFILES adding file " + + fileToBeDeleted.getAbsolutePath() + " to the delete list"); deleteFiles.add(fileToBeDeleted); } return ret; } + public static boolean moveFileToFile(File oldFile, File newFile) + { + Cache.initLogger(); + boolean ret = false; + Path oldPath = Paths.get(oldFile.getAbsolutePath()); + Path newPath = Paths.get(newFile.getAbsolutePath()); + try + { + // delete destination file - not usually necessary but Just In Case... + Cache.log.trace("BACKUPFILES deleting " + newFile.getAbsolutePath()); + newFile.delete(); + Cache.log.trace("BACKUPFILES moving " + oldFile.getAbsolutePath() + + " to " + newFile.getAbsolutePath()); + Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING); + ret = true; + Cache.log.trace("BACKUPFILES move seems to have succeeded"); + } catch (IOException e) + { + Cache.log.warn("Could not move file '" + oldPath.toString() + "' to '" + + newPath.toString() + "'"); + Cache.log.error(e.getMessage()); + Cache.log.debug(Cache.getStackTraceString(e)); + ret = false; + } catch (Exception e) + { + Cache.log.error(e.getMessage()); + Cache.log.debug(Cache.getStackTraceString(e)); + ret = false; + } + return ret; + } } diff --git a/src/jalview/io/BackupFilesPresetEntry.java b/src/jalview/io/BackupFilesPresetEntry.java index 69130d0..ff8a5e6 100644 --- a/src/jalview/io/BackupFilesPresetEntry.java +++ b/src/jalview/io/BackupFilesPresetEntry.java @@ -20,13 +20,13 @@ */ package jalview.io; -import jalview.bin.Cache; -import jalview.util.MessageManager; - import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; +import jalview.bin.Cache; +import jalview.util.MessageManager; + public class BackupFilesPresetEntry { diff --git a/src/jalview/io/EmblFlatFile.java b/src/jalview/io/EmblFlatFile.java new file mode 100644 index 0000000..bfae4ed --- /dev/null +++ b/src/jalview/io/EmblFlatFile.java @@ -0,0 +1,900 @@ +package jalview.io; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import jalview.bin.Cache; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; +import jalview.datamodel.FeatureProperties; +import jalview.datamodel.Mapping; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.util.DBRefUtils; +import jalview.util.DnaUtils; +import jalview.util.MapList; +import jalview.util.MappingUtils; + +/** + * A class that provides selective parsing of the EMBL flatfile format. + *

      + * The initial implementation is limited to extracting fields used by Jalview + * after fetching an EMBL or EMBLCDS entry: + * + *

      + * accession, version, sequence, xref
      + * and (for CDS feature) location, protein_id, product, codon_start, translation
      + * 
      + * + * For a complete parser, it may be best to adopt that provided in + * https://github.com/enasequence/sequencetools/tree/master/src/main/java/uk/ac/ebi/embl/flatfile + * (but note this has a dependency on the Apache Commons library) + * + * @author gmcarstairs + * @see ftp://ftp.ebi.ac.uk/pub/databases/ena/sequence/release/doc/usrman.txt + * @see ftp://ftp.ebi.ac.uk/pub/databases/embl/doc/FT_current.html + */ +public class EmblFlatFile extends AlignFile // FileParse +{ + private static final String QUOTE = "\""; + + private static final String DOUBLED_QUOTE = QUOTE + QUOTE; + + /** + * A data bean class to hold values parsed from one CDS Feature (FT) + */ + class CdsData + { + String translation; // from CDS feature /translation + + String cdsLocation; // CDS /location raw value + + int codonStart = 1; // from CDS /codon_start + + String proteinName; // from CDS /product; used for protein description + + String proteinId; // from CDS /protein_id + + List xrefs = new ArrayList<>(); // from CDS /db_xref qualifiers + + Map cdsProps = new Hashtable<>(); // CDS other qualifiers + } + + private static final String WHITESPACE = "\\s+"; + + private String sourceDb; + + /* + * values parsed from the EMBL flatfile record + */ + private String accession; // from ID (first token) + + private String version; // from ID (second token) + + private String description; // from (first) DE line + + private int length = 128; // from ID (7th token), with usable default + + private List dbrefs; // from DR + + private String sequenceString; // from SQ lines + + /* + * parsed CDS data fields, keyed by protein_id + */ + private Map cds; + + /** + * Constructor + * + * @param fp + * @param sourceId + * @throws IOException + */ + public EmblFlatFile(FileParse fp, String sourceId) throws IOException + { + super(false, fp); // don't parse immediately + this.sourceDb = sourceId; + dbrefs = new ArrayList<>(); + + /* + * using TreeMap gives CDS sequences in alphabetical, so readable, order + */ + cds = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + + /** + * Parses the flatfile, and if successful, saves as an annotated sequence + * which may be retrieved by calling {@code getSequence()} + * + * @throws IOException + */ + @Override + public void parse() throws IOException + { + String line = nextLine(); + while (line != null) + { + if (line.startsWith("ID")) + { + line = parseID(line); + } + else if (line.startsWith("DE")) + { + line = parseDE(line); + } + else if (line.startsWith("DR")) + { + line = parseDR(line); + } + else if (line.startsWith("SQ")) + { + line = parseSQ(); + } + else if (line.startsWith("FT")) + { + line = parseFT(line); + } + else + { + line = nextLine(); + } + } + buildSequence(); + } + + /** + * Extracts and saves the primary accession and version (SV value) from an ID + * line, or null if not found. Returns the next line after the one processed. + * + * @param line + * @throws IOException + */ + String parseID(String line) throws IOException + { + String[] tokens = line.substring(2).split(";"); + + /* + * first is primary accession + */ + String token = tokens[0].trim(); + if (!token.isEmpty()) + { + this.accession = token; + } + + /* + * second token is 'SV versionNo' + */ + if (tokens.length > 1) + { + token = tokens[1].trim(); + if (token.startsWith("SV")) + { + String[] bits = token.trim().split(WHITESPACE); + this.version = bits[bits.length - 1]; + } + } + + /* + * seventh token is 'length BP' + */ + if (tokens.length > 6) + { + token = tokens[6].trim(); + String[] bits = token.trim().split(WHITESPACE); + try + { + this.length = Integer.valueOf(bits[0]); + } catch (NumberFormatException e) + { + Cache.log.error("bad length read in flatfile, line: " + line); + } + } + + return nextLine(); + } + + /** + * Reads sequence description from the first DE line found. Any trailing + * period is discarded. If there are multiple DE lines, only the first (short + * description) is read, the rest are ignored. + * + * @param line + * @return + * @throws IOException + */ + String parseDE(String line) throws IOException + { + String desc = line.substring(2).trim(); + if (desc.endsWith(".")) + { + desc = desc.substring(0, desc.length() - 1); + } + this.description = desc; + + /* + * pass over any additional DE lines + */ + while ((line = nextLine()) != null) + { + if (!line.startsWith("DE")) + { + break; + } + } + + return line; + } + + /** + * Processes one DR line and saves as a DBRefEntry cross-reference. Returns + * the line following the line processed. + * + * @param line + * @throws IOException + */ + String parseDR(String line) throws IOException + { + String[] tokens = line.substring(2).split(";"); + if (tokens.length > 1) + { + /* + * ensure UniProtKB/Swiss-Prot converted to UNIPROT + */ + String db = tokens[0].trim(); + db = DBRefUtils.getCanonicalName(db); + String acc = tokens[1].trim(); + if (acc.endsWith(".")) + { + acc = acc.substring(0, acc.length() - 1); + } + String version = "0"; + if (tokens.length > 2) + { + String secondaryId = tokens[2].trim(); + if (!secondaryId.isEmpty()) + { + // todo: is this right? secondary id is not a version number + // version = secondaryId; + } + } + this.dbrefs.add(new DBRefEntry(db, version, acc)); + } + + return nextLine(); + } + + /** + * Reads and saves the sequence, read from the lines following the SQ line. + * Whitespace and position counters are discarded. Returns the next line + * following the sequence data (the next line that doesn't start with + * whitespace). + * + * @throws IOException + */ + String parseSQ() throws IOException + { + StringBuilder sb = new StringBuilder(this.length); + String line = nextLine(); + while (line != null && line.startsWith(" ")) + { + line = line.trim(); + String[] blocks = line.split(WHITESPACE); + + /* + * omit the last block (position counter) on each line + */ + for (int i = 0; i < blocks.length - 1; i++) + { + sb.append(blocks[i]); + } + line = nextLine(); + } + this.sequenceString = sb.toString(); + + return line; + } + + /** + * Processes an FT line. If it declares a feature type of interest (currently, + * only CDS is processed), processes all of the associated lines (feature + * qualifiers), and returns the next line after that, otherwise simply returns + * the next line. + * + * @param line + * @return + * @throws IOException + */ + String parseFT(String line) throws IOException + { + String[] tokens = line.split(WHITESPACE); + if (tokens.length < 3 || !"CDS".equals(tokens[1])) + { + return nextLine(); + } + + /* + * parse location - which may be over more than one line e.g. EAW51554 + */ + CdsData data = new CdsData(); + StringBuilder sb = new StringBuilder().append(tokens[2]); + line = parseFeatureQualifier(sb, "CDS"); + data.cdsLocation = sb.toString(); + + while (line != null) + { + if (!line.startsWith("FT ")) // 4 spaces + { + // e.g. start of next feature "FT source..." + break; + } + + /* + * extract qualifier, e.g. FT /protein_id="CAA37824.1" + * - the value may extend over more than one line + * - if the value has enclosing quotes, these are removed + * - escaped double quotes ("") are reduced to a single character + */ + int slashPos = line.indexOf('/'); + if (slashPos == -1) + { + Cache.log.error("Unexpected EMBL line ignored: " + line); + line = nextLine(); + continue; + } + int eqPos = line.indexOf('=', slashPos + 1); + if (eqPos == -1) + { + // can happen, e.g. /ribosomal_slippage + // Cache.log.error("Unexpected EMBL line ignored: " + line); + line = nextLine(); + continue; + } + String qualifier = line.substring(slashPos + 1, eqPos); + String value = line.substring(eqPos + 1); + value = removeQuotes(value); + sb = new StringBuilder().append(value); + line = parseFeatureQualifier(sb, qualifier); + String featureValue = sb.toString(); + + if ("protein_id".equals(qualifier)) + { + data.proteinId = featureValue; + } + else if ("codon_start".equals(qualifier)) + { + try + { + data.codonStart = Integer.parseInt(featureValue.trim()); + } catch (NumberFormatException e) + { + Cache.log.error("Invalid codon_start in XML for " + this.accession + + ": " + e.getMessage()); + } + } + else if ("db_xref".equals(qualifier)) + { + String[] parts = featureValue.split(":"); + if (parts.length == 2) + { + String db = parts[0].trim(); + db = DBRefUtils.getCanonicalName(db); + DBRefEntry dbref = new DBRefEntry(db, "0", parts[1].trim()); + data.xrefs.add(dbref); + } + } + else if ("product".equals(qualifier)) + { + data.proteinName = featureValue; + } + else if ("translation".equals(qualifier)) + { + data.translation = featureValue; + } + else if (!"".equals(featureValue)) + { + // throw anything else into the additional properties hash + data.cdsProps.put(qualifier, featureValue); + } + } + + if (data.proteinId != null) + { + this.cds.put(data.proteinId, data); + } + else + { + Cache.log.error("Ignoring CDS feature with no protein_id for " + + sourceDb + ":" + accession); + } + + return line; + } + + /** + * Removes leading or trailing double quotes (") unless doubled, and changes + * any 'escaped' (doubled) double quotes to single characters. As per the + * Feature Table specification for Qualifiers, Free Text. + * + * @param value + * @return + */ + static String removeQuotes(String value) + { + if (value == null) + { + return null; + } + if (value.startsWith(QUOTE) && !value.startsWith(DOUBLED_QUOTE)) + { + value = value.substring(1); + } + if (value.endsWith(QUOTE) && !value.endsWith(DOUBLED_QUOTE)) + { + value = value.substring(0, value.length() - 1); + } + value = value.replace(DOUBLED_QUOTE, QUOTE); + return value; + } + + /** + * Reads the value of a feature (FT) qualifier from one or more lines of the + * file, and returns the next line after that. Values are appended to the + * string buffer, which should be already primed with the value read from the + * first line for the qualifier (with any leading double quote removed). + * Enclosing double quotes are removed, and escaped (repeated) double quotes + * reduced to one only. For example for + * + *
      +   * FT      /note="gene_id=hCG28070.3 
      +   * FT      ""foobar"" isoform=CRA_b"
      +   * the returned value is
      +   * gene_id=hCG28070.3 "foobar" isoform=CRA_b
      +   * 
      + * + * Note the side-effect of this method, to advance data reading to the next + * line after the feature qualifier. + * + * @param sb + * a string buffer primed with the first line of the value + * @param qualifierName + * @return + * @throws IOException + */ + String parseFeatureQualifier(StringBuilder sb, String qualifierName) + throws IOException + { + String line; + while ((line = nextLine()) != null) + { + if (!line.startsWith("FT ")) + { + break; // reached next feature or other input line + } + String[] tokens = line.split(WHITESPACE); + if (tokens.length < 2) + { + Cache.log.error("Ignoring bad EMBL line for " + this.accession + + ": " + line); + break; + } + if (tokens[1].startsWith("/")) + { + break; // next feature qualifier + } + + /* + * heuristic rule: most multi-line value (e.g. /product) are text, + * so add a space for word boundary at a new line; not for translation + */ + if (!"translation".equals(qualifierName) + && !"CDS".equals(qualifierName)) + { + sb.append(" "); + } + + /* + * remove trailing " and unescape doubled "" + */ + String data = removeQuotes(tokens[1]); + sb.append(data); + } + + return line; + } + + /** + * Constructs and saves the sequence from parsed components + */ + void buildSequence() + { + if (this.accession == null || this.sequenceString == null) + { + Cache.log.error("Failed to parse data from EMBL"); + return; + } + + String name = this.accession; + if (this.sourceDb != null) + { + name = this.sourceDb + "|" + name; + } + SequenceI seq = new Sequence(name, this.sequenceString); + seq.setDescription(this.description); + + /* + * add a DBRef to itself + */ + DBRefEntry selfRef = new DBRefEntry(sourceDb, version, accession); + int[] startEnd = new int[] { 1, seq.getLength() }; + selfRef.setMap(new Mapping(null, startEnd, startEnd, 1, 1)); + seq.addDBRef(selfRef); + + for (DBRefEntry dbref : this.dbrefs) + { + seq.addDBRef(dbref); + } + + processCDSFeatures(seq); + + seq.deriveSequence(); + + addSequence(seq); + } + + /** + * Process the CDS features, including generation of cross-references and + * mappings to the protein products (translation) + * + * @param seq + */ + protected void processCDSFeatures(SequenceI seq) + { + /* + * record protein products found to avoid duplication i.e. >1 CDS with + * the same /protein_id [though not sure I can find an example of this] + */ + Map proteins = new HashMap<>(); + for (CdsData data : cds.values()) + { + processCDSFeature(seq, data, proteins); + } + } + + /** + * Processes data for one parsed CDS feature to + *
        + *
      • create a protein product sequence for the translation
      • + *
      • create a cross-reference to protein with mapping from dna
      • + *
      • add a CDS feature to the sequence for each CDS start-end range
      • + *
      • add any CDS dbrefs to the sequence and to the protein product
      • + *
      + * + * @param SequenceI + * dna + * @param proteins + * map of protein products so far derived from CDS data + */ + void processCDSFeature(SequenceI dna, CdsData data, + Map proteins) + { + /* + * parse location into a list of [start, end, start, end] positions + */ + int[] exons = getCdsRanges(this.accession, data.cdsLocation); + + MapList maplist = buildMappingToProtein(dna, exons, data); + + int exonNumber = 0; + + for (int xint = 0; exons != null && xint < exons.length - 1; xint += 2) + { + int exonStart = exons[xint]; + int exonEnd = exons[xint + 1]; + int begin = Math.min(exonStart, exonEnd); + int end = Math.max(exonStart, exonEnd); + exonNumber++; + String desc = String.format("Exon %d for protein EMBLCDS:%s", + exonNumber, data.proteinId); + + SequenceFeature sf = new SequenceFeature("CDS", desc, begin, end, + this.sourceDb); + for (Entry val : data.cdsProps.entrySet()) + { + sf.setValue(val.getKey(), val.getValue()); + } + + sf.setEnaLocation(data.cdsLocation); + boolean forwardStrand = exonStart <= exonEnd; + sf.setStrand(forwardStrand ? "+" : "-"); + sf.setPhase(String.valueOf(data.codonStart - 1)); + sf.setValue(FeatureProperties.EXONPOS, exonNumber); + sf.setValue(FeatureProperties.EXONPRODUCT, data.proteinName); + + dna.addSequenceFeature(sf); + } + + boolean hasUniprotDbref = false; + for (DBRefEntry xref : data.xrefs) + { + dna.addDBRef(xref); + if (xref.getSource().equals(DBRefSource.UNIPROT)) + { + /* + * construct (or find) the sequence for (data.protein_id, data.translation) + */ + SequenceI protein = buildProteinProduct(dna, xref, data, proteins); + Mapping map = new Mapping(protein, maplist); + map.setMappedFromId(data.proteinId); + xref.setMap(map); + + /* + * add DBRefs with mappings from dna to protein and the inverse + */ + DBRefEntry db1 = new DBRefEntry(sourceDb, version, accession); + db1.setMap(new Mapping(dna, maplist.getInverse())); + protein.addDBRef(db1); + + hasUniprotDbref = true; + } + } + + /* + * if we have a product (translation) but no explicit Uniprot dbref + * (example: EMBL M19487 protein_id AAB02592.1) + * then construct mappings to an assumed EMBLCDSPROTEIN accession + */ + if (!hasUniprotDbref) + { + SequenceI protein = proteins.get(data.proteinId); + if (protein == null) + { + protein = new Sequence(data.proteinId, data.translation); + protein.setDescription(data.proteinName); + proteins.put(data.proteinId, protein); + } + // assuming CDSPROTEIN sequence version = dna version (?!) + DBRefEntry db1 = new DBRefEntry(DBRefSource.EMBLCDSProduct, + this.version, data.proteinId); + protein.addDBRef(db1); + + DBRefEntry dnaToEmblProteinRef = new DBRefEntry( + DBRefSource.EMBLCDSProduct, this.version, data.proteinId); + Mapping map = new Mapping(protein, maplist); + map.setMappedFromId(data.proteinId); + dnaToEmblProteinRef.setMap(map); + dna.addDBRef(dnaToEmblProteinRef); + } + + /* + * comment brought forward from EmblXmlSource, lines 447-451: + * TODO: if retrieved from EMBLCDS, add a DBRef back to the parent EMBL + * sequence with the exon map; if given a dataset reference, search + * dataset for parent EMBL sequence if it exists and set its map; + * make a new feature annotating the coding contig + */ + } + + /** + * Computes a mapping from CDS positions in DNA sequence to protein product + * positions, with allowance for stop codon or incomplete start codon + * + * @param dna + * @param exons + * @param data + * @return + */ + MapList buildMappingToProtein(final SequenceI dna, final int[] exons, + final CdsData data) + { + MapList dnaToProteinMapping = null; + int peptideLength = data.translation.length(); + + int[] proteinRange = new int[] { 1, peptideLength }; + if (exons != null && exons.length > 0) + { + /* + * We were able to parse 'location'; do a final + * product length truncation check + */ + int[] cdsRanges = adjustForProteinLength(peptideLength, exons); + dnaToProteinMapping = new MapList(cdsRanges, proteinRange, 3, 1); + } + else + { + /* + * workaround until we handle all 'location' formats fully + * e.g. X53828.1:60..1058 or <123..>289 + */ + Cache.log.error(String.format( + "Implementation Notice: EMBLCDS location '%s'not properly supported yet" + + " - Making up the CDNA region of (%s:%s)... may be incorrect", + data.cdsLocation, sourceDb, this.accession)); + + int completeCodonsLength = 1 - data.codonStart + dna.getLength(); + int mappedDnaEnd = dna.getEnd(); + if (peptideLength * 3 == completeCodonsLength) + { + // this might occur for CDS sequences where no features are marked + Cache.log.warn("Assuming no stop codon at end of cDNA fragment"); + mappedDnaEnd = dna.getEnd(); + } + else if ((peptideLength + 1) * 3 == completeCodonsLength) + { + Cache.log.warn("Assuming stop codon at end of cDNA fragment"); + mappedDnaEnd = dna.getEnd() - 3; + } + + if (mappedDnaEnd != -1) + { + int[] cdsRanges = new int[] { + dna.getStart() + (data.codonStart - 1), mappedDnaEnd }; + dnaToProteinMapping = new MapList(cdsRanges, proteinRange, 3, 1); + } + } + + return dnaToProteinMapping; + } + + /** + * Constructs a sequence for the protein product for the CDS data (if there is + * one), and dbrefs with mappings from CDS to protein and the reverse + * + * @param dna + * @param xref + * @param data + * @param proteins + * @return + */ + SequenceI buildProteinProduct(SequenceI dna, DBRefEntry xref, + CdsData data, Map proteins) + { + /* + * check we have some data to work with + */ + if (data.proteinId == null || data.translation == null) + { + return null; + } + + /* + * Construct the protein sequence (if not already seen) + */ + String proteinSeqName = xref.getSource() + "|" + xref.getAccessionId(); + SequenceI protein = proteins.get(proteinSeqName); + if (protein == null) + { + protein = new Sequence(proteinSeqName, data.translation, 1, + data.translation.length()); + protein.setDescription(data.proteinName != null ? data.proteinName + : "Protein Product from " + sourceDb); + proteins.put(proteinSeqName, protein); + } + + return protein; + } + + /** + * Returns the CDS location as a single array of [start, end, start, end...] + * positions. If on the reverse strand, these will be in descending order. + * + * @param accession + * @param location + * @return + */ + protected int[] getCdsRanges(String accession, String location) + { + if (location == null) + { + return new int[] {}; + } + + try + { + List ranges = DnaUtils.parseLocation(location); + return MappingUtils.listToArray(ranges); + } catch (ParseException e) + { + Cache.log.warn( + String.format("Not parsing inexact CDS location %s in ENA %s", + location, accession)); + return new int[] {}; + } + } + + /** + * Output (print) is not implemented for EMBL flat file format + */ + @Override + public String print(SequenceI[] seqs, boolean jvsuffix) + { + return null; + } + + /** + * Truncates (if necessary) the exon intervals to match 3 times the length of + * the protein; also accepts 3 bases longer (for stop codon not included in + * protein) + * + * @param proteinLength + * @param exon + * an array of [start, end, start, end...] intervals + * @return the same array (if unchanged) or a truncated copy + */ + static int[] adjustForProteinLength(int proteinLength, int[] exon) + { + if (proteinLength <= 0 || exon == null) + { + return exon; + } + int expectedCdsLength = proteinLength * 3; + int exonLength = MappingUtils.getLength(Arrays.asList(exon)); + + /* + * if exon length matches protein, or is shorter, or longer by the + * length of a stop codon (3 bases), then leave it unchanged + */ + if (expectedCdsLength >= exonLength + || expectedCdsLength == exonLength - 3) + { + return exon; + } + + int origxon[]; + int sxpos = -1; + int endxon = 0; + origxon = new int[exon.length]; + System.arraycopy(exon, 0, origxon, 0, exon.length); + int cdspos = 0; + for (int x = 0; x < exon.length; x += 2) + { + cdspos += Math.abs(exon[x + 1] - exon[x]) + 1; + if (expectedCdsLength <= cdspos) + { + // advanced beyond last codon. + sxpos = x; + if (expectedCdsLength != cdspos) + { + // System.err + // .println("Truncating final exon interval on region by " + // + (cdspos - cdslength)); + } + + /* + * shrink the final exon - reduce end position if forward + * strand, increase it if reverse + */ + if (exon[x + 1] >= exon[x]) + { + endxon = exon[x + 1] - cdspos + expectedCdsLength; + } + else + { + endxon = exon[x + 1] + cdspos - expectedCdsLength; + } + break; + } + } + + if (sxpos != -1) + { + // and trim the exon interval set if necessary + int[] nxon = new int[sxpos + 2]; + System.arraycopy(exon, 0, nxon, 0, sxpos + 2); + nxon[sxpos + 1] = endxon; // update the end boundary for the new exon + // set + exon = nxon; + } + return exon; + } +} diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java index 92473ec..7e62f6b 100755 --- a/src/jalview/io/FeaturesFile.java +++ b/src/jalview/io/FeaturesFile.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; @@ -346,7 +347,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI String line; while ((line = nextLine()) != null) { - if (line.toUpperCase().startsWith(ENDFILTERS)) + // TODO: use .trim().equalsIgnoreCase here instead ? + if (line.toUpperCase(Locale.ROOT).startsWith(ENDFILTERS)) { return; } diff --git a/src/jalview/io/FileFormats.java b/src/jalview/io/FileFormats.java index aadcdb9..de83e9b 100644 --- a/src/jalview/io/FileFormats.java +++ b/src/jalview/io/FileFormats.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -102,7 +103,7 @@ public class FileFormats protected void registerFileFormat(FileFormatI format, boolean isIdentifiable) { - String name = format.getName().toUpperCase(); + String name = format.getName().toUpperCase(Locale.ROOT); if (formats.containsKey(name)) { System.err.println("Overwriting file format: " + format.getName()); @@ -121,7 +122,7 @@ public class FileFormats */ public void deregisterFileFormat(String name) { - FileFormatI ff = formats.remove(name.toUpperCase()); + FileFormatI ff = formats.remove(name.toUpperCase(Locale.ROOT)); identifiable.remove(ff); } @@ -174,7 +175,7 @@ public class FileFormats */ public FileFormatI forName(String format) { - return format == null ? null : formats.get(format.toUpperCase()); + return format == null ? null : formats.get(format.toUpperCase(Locale.ROOT)); } /** diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 3afbaad..20db946 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -391,12 +391,9 @@ public class FileLoader implements Runnable .getFeatureColourScheme(); if (viewport != null) { - if (proxyColourScheme != null) - { - viewport.applyFeaturesStyle(proxyColourScheme); - } // append to existing alignment viewport.addAlignment(al, title); + viewport.applyFeaturesStyle(proxyColourScheme); } else { diff --git a/src/jalview/io/FileParse.java b/src/jalview/io/FileParse.java index 7117d0f..3013c0b 100755 --- a/src/jalview/io/FileParse.java +++ b/src/jalview/io/FileParse.java @@ -24,21 +24,25 @@ import jalview.api.AlignExportSettingI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureSettingsModelI; +import jalview.bin.Cache; import jalview.util.MessageManager; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; import java.util.zip.GZIPInputStream; + /** * implements a random access wrapper around a particular datasource, for * passing to identifyFile and AlignFile objects. @@ -56,6 +60,7 @@ public class FileParse public File inFile = null; + /** * a viewport associated with the current file operation. May be null. May * move to different object. @@ -182,29 +187,91 @@ public class FileParse } if (!error) { - if (fileStr.toLowerCase().endsWith(".gz")) + try { - try - { - dataIn = tryAsGzipSource(new FileInputStream(fileStr)); - dataName = fileStr; - return error; - } catch (Exception x) - { - warningMessage = "Failed to resolve as a GZ stream (" - + x.getMessage() + ")"; - // x.printStackTrace(); - } - ; + dataIn = checkForGzipStream(new FileInputStream(fileStr)); + dataName = fileStr; + } catch (Exception x) + { + warningMessage = "Failed to resolve " + fileStr + + " as a data source. (" + x.getMessage() + ")"; + // x.printStackTrace(); + error = true; } - - dataIn = new BufferedReader(new FileReader(fileStr)); - dataName = fileStr; + ; } return error; } - private BufferedReader tryAsGzipSource(InputStream inputStream) + /** + * Recognise the 2-byte magic header indicating a gzipped stream + * + * see + * https://recalll.co/ask/v/topic/java-How-to-check-if-InputStream-is-Gzipped/555aadd62bd27354438b90f6 + * + * @param input + * - input stream that supports mark and contains at least two bytes + * + * @return false if mark not supported or no magic header found + * + * @throws IOException + */ + public static boolean isGzipStream(InputStream input) throws IOException + { + if (!input.markSupported()) + { + Cache.log.error( + "FileParse.izGzipStream: input stream must support mark/reset"); + return false; + } + input.mark(4); + + // get first 2 bytes or return false + byte[] bytes = new byte[2]; + int read = input.read(bytes); + input.reset(); + if (read != bytes.length) + { + return false; + } + + int header = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + return (GZIPInputStream.GZIP_MAGIC == header); + } + + /** + * Returns a Reader for the given input after wrapping it in a buffered input + * stream, and then checking if it needs to be wrapped by a GZipInputStream + * + * @param input + * @return + */ + private BufferedReader checkForGzipStream(InputStream input) throws Exception { + + // NB: stackoverflow https://stackoverflow.com/questions/4818468/how-to-check-if-inputstream-is-gzipped + // could use a PushBackInputStream rather than a BufferedInputStream + + if (!input.markSupported()) { + input = new BufferedInputStream(input,16); + } + if (isGzipStream(input)) { + return getGzipReader(input); + } + // return a buffered reader for the stream. + InputStreamReader isReader= new InputStreamReader(input); + BufferedReader toReadFrom=new BufferedReader(isReader); + return toReadFrom; + } + /** + * Returns a {@code BufferedReader} which wraps the input stream with a + * GZIPInputStream. Throws a {@code ZipException} if a GZIP format error + * occurs or the compression method used is unsupported. + * + * @param inputStream + * @return + * @throws Exception + */ + private BufferedReader getGzipReader(InputStream inputStream) throws Exception { BufferedReader inData = new BufferedReader( @@ -215,44 +282,74 @@ public class FileParse return inData; } - private boolean checkURLSource(String fileStr) + /** + * Tries to read from the given URL. If successful, saves a reader to the + * response in field {@code dataIn}, otherwise (on exception, or HTTP response + * status not 200), throws an exception. + *

      + * If the response status includes + * + *

      +   * Content-Type : application/x-gzip
      +   * 
      + * + * then tries to read as gzipped content. + * + * @param urlStr + * @throws IOException + * @throws MalformedURLException + */ + private void checkURLSource(String urlStr) throws IOException, MalformedURLException { errormessage = "URL NOT FOUND"; - URL url = new URL(fileStr); - // - // GZIPInputStream code borrowed from Aquaria (soon to be open sourced) via - // Kenny Sabir - Exception e = null; - if (fileStr.toLowerCase().endsWith(".gz")) + URL url = new URL(urlStr); + URLConnection _conn = url.openConnection(); + if (_conn instanceof HttpURLConnection) { - try + HttpURLConnection conn = (HttpURLConnection) _conn; + int rc = conn.getResponseCode(); + if (rc != HttpURLConnection.HTTP_OK) { - InputStream inputStream = url.openStream(); - dataIn = tryAsGzipSource(inputStream); - dataName = fileStr; - return false; + throw new IOException( + "Response status from " + urlStr + " was " + rc); + } + } else { + try { + dataIn = checkForGzipStream(_conn.getInputStream()); + dataName=urlStr; + } catch (IOException ex) + { + throw new IOException("Failed to handle non-HTTP URI stream",ex); } catch (Exception ex) { - e = ex; + throw new IOException("Failed to determine type of input stream for given URI",ex); } + return; } - - try - { - dataIn = new BufferedReader(new InputStreamReader(url.openStream())); - } catch (IOException q) + String encoding = _conn.getContentEncoding(); + String contentType = _conn.getContentType(); + boolean isgzipped = "application/x-gzip".equalsIgnoreCase(contentType) + || "gzip".equals(encoding); + Exception e = null; + InputStream inputStream = _conn.getInputStream(); + if (isgzipped) { - if (e != null) + try + { + dataIn = getGzipReader(inputStream); + dataName = urlStr; + } catch (Exception e1) { throw new IOException(MessageManager .getString("exception.failed_to_resolve_gzip_stream"), e); } - throw q; + return; } - // record URL as name of datasource. - dataName = fileStr; - return false; + + dataIn = new BufferedReader(new InputStreamReader(inputStream)); + dataName = urlStr; + return; } /** diff --git a/src/jalview/io/FormatAdapter.java b/src/jalview/io/FormatAdapter.java index 6d3c18a..4d8794f 100755 --- a/src/jalview/io/FormatAdapter.java +++ b/src/jalview/io/FormatAdapter.java @@ -33,6 +33,7 @@ import jalview.datamodel.SequenceI; import jalview.util.Comparison; import java.io.IOException; +import java.util.Locale; /** * Additional formatting methods used by the application in a number of places. @@ -161,7 +162,7 @@ public class FormatAdapter extends AppletFormatAdapter public boolean getCacheSuffixDefault(FileFormatI format) { - return Cache.getDefault(format.getName().toUpperCase() + "_JVSUFFIX", + return Cache.getDefault(format.getName().toUpperCase(Locale.ROOT) + "_JVSUFFIX", true); } diff --git a/src/jalview/io/IdentifyFile.java b/src/jalview/io/IdentifyFile.java index ff959b0..ed1c29f 100755 --- a/src/jalview/io/IdentifyFile.java +++ b/src/jalview/io/IdentifyFile.java @@ -21,6 +21,7 @@ package jalview.io; import java.io.IOException; +import java.util.Locale; /** * DOCUMENT ME! @@ -143,7 +144,7 @@ public class IdentifyFile break; } } - data = data.toUpperCase(); + data = data.toUpperCase(Locale.ROOT); if (data.startsWith(ScoreMatrixFile.SCOREMATRIX)) { @@ -284,7 +285,7 @@ public class IdentifyFile if ((lessThan > -1)) // possible Markup Language data i.e HTML, // RNAML, XML { - String upper = data.toUpperCase(); + String upper = data.toUpperCase(Locale.ROOT); if (upper.substring(lessThan).startsWith(" diff --git a/src/jalview/io/cache/JvCacheableInputBox.java b/src/jalview/io/cache/JvCacheableInputBox.java index beef3e7..71e7c62 100644 --- a/src/jalview/io/cache/JvCacheableInputBox.java +++ b/src/jalview/io/cache/JvCacheableInputBox.java @@ -25,8 +25,8 @@ import jalview.util.MessageManager; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -64,12 +64,19 @@ public class JvCacheableInputBox extends JComboBox return enterWasPressed; } - public JvCacheableInputBox(String newCacheKey) + /** + * Constructor given the key to cached values, and the (approximate) length in + * characters of the input field + * + * @param newCacheKey + * @param length + */ + public JvCacheableInputBox(String newCacheKey, int length) { super(); this.cacheKey = newCacheKey; setEditable(true); - addKeyListener(new KeyListener() + addKeyListener(new KeyAdapter() { @Override @@ -82,23 +89,16 @@ public class JvCacheableInputBox extends JComboBox } // let event bubble up } - - @Override - public void keyReleased(KeyEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void keyPressed(KeyEvent e) + }); + if (length > 0) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { - // TODO Auto-generated method stub - + sb.append("X"); } - }); - setPrototypeDisplayValue( - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + setPrototypeDisplayValue(sb.toString()); + } appCache = AppCache.getInstance(); initCachePopupMenu(); initCache(newCacheKey); diff --git a/src/jalview/io/packed/ParsePackedSet.java b/src/jalview/io/packed/ParsePackedSet.java index 1635682..91575bf 100644 --- a/src/jalview/io/packed/ParsePackedSet.java +++ b/src/jalview/io/packed/ParsePackedSet.java @@ -34,6 +34,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; public class ParsePackedSet { @@ -234,7 +235,7 @@ public class ParsePackedSet String type = args[i++]; final String file = args[i++]; final JvDataType jtype = DataProvider.JvDataType - .valueOf(type.toUpperCase()); + .valueOf(type.toUpperCase(Locale.ROOT)); if (jtype != null) { final FileParse fp; @@ -253,7 +254,7 @@ public class ParsePackedSet else { System.out.println("Couldn't parse source type token '" - + type.toUpperCase() + "'"); + + type.toUpperCase(Locale.ROOT) + "'"); } } if (i < args.length) diff --git a/src/jalview/io/vcf/VCFLoader.java b/src/jalview/io/vcf/VCFLoader.java index a44ee26..d6a530b 100644 --- a/src/jalview/io/vcf/VCFLoader.java +++ b/src/jalview/io/vcf/VCFLoader.java @@ -48,6 +48,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -569,7 +570,7 @@ public class VCFLoader { for (Pattern p : filters) { - if (p.matcher(id.toUpperCase()).matches()) + if (p.matcher(id.toUpperCase(Locale.ROOT)).matches()) { return true; } @@ -663,7 +664,7 @@ public class VCFLoader { try { - patterns.add(Pattern.compile(token.toUpperCase())); + patterns.add(Pattern.compile(token.toUpperCase(Locale.ROOT))); } catch (PatternSyntaxException e) { System.err.println("Invalid pattern ignored: " + token); @@ -912,7 +913,7 @@ public class VCFLoader * RuntimeException throwable by htsjdk */ String msg = String.format("Error reading VCF for %s:%d-%d: %s ", - map.chromosome, vcfStart, vcfEnd); + map.chromosome, vcfStart, vcfEnd,e.getLocalizedMessage()); Cache.log.error(msg); } } diff --git a/src/jalview/jbgui/GFinder.java b/src/jalview/jbgui/GFinder.java index b433570..1600b75 100755 --- a/src/jalview/jbgui/GFinder.java +++ b/src/jalview/jbgui/GFinder.java @@ -28,11 +28,11 @@ import jalview.io.cache.JvCacheableInputBox; import jalview.util.MessageManager; import java.awt.BorderLayout; -import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; -import java.awt.Insets; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JButton; @@ -47,19 +47,26 @@ import javax.swing.text.JTextComponent; public class GFinder extends JPanel { - private static final java.awt.Font VERDANA_12 = new java.awt.Font("Verdana", 0, - 12); + private static final java.awt.Font VERDANA_12 = new Font("Verdana", + Font.PLAIN, 12); private static final String FINDER_CACHE_KEY = "CACHE.FINDER"; - protected JButton createFeatures = new JButton(); + /* + * if more checkboxes are wanted, increase this value + * and add to centrePanel in jbInit() + */ + private static final int PANEL_ROWS = 4; + + protected JButton createFeatures; - protected JvCacheableInputBox searchBox = new JvCacheableInputBox<>( - getCacheKey()); + protected JvCacheableInputBox searchBox; - protected JCheckBox caseSensitive = new JCheckBox(); + protected JCheckBox caseSensitive; - protected JCheckBox searchDescription = new JCheckBox(); + protected JCheckBox searchDescription; + + protected JCheckBox ignoreHidden; public GFinder() { @@ -72,20 +79,98 @@ public class GFinder extends JPanel } } + /** + * Constructs the widgets and adds them to the layout + */ private void jbInit() throws Exception { - BorderLayout mainBorderLayout = new BorderLayout(); - this.setLayout(mainBorderLayout); - mainBorderLayout.setHgap(5); - mainBorderLayout.setVgap(5); + /* + * border layout + * West: 4 rows + * first row 'Find' + * remaining rows empty + * Center: 4 rows + * first row search box + * second row 'match case' checkbox + * third row 'include description' checkbox + * fourth row 'ignore hidden' checkbox + * East: four rows + * first row 'find next' button + * second row 'find all' button + * third row 'new feature' button + * fourth row empty + */ + this.setLayout(new BorderLayout()); + JPanel eastPanel = new JPanel(); + eastPanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(eastPanel, BorderLayout.EAST); + JPanel centrePanel = new JPanel(); + centrePanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(centrePanel, BorderLayout.CENTER); + JPanel westPanel = new JPanel(); + westPanel.setLayout(new GridLayout(PANEL_ROWS, 1)); + this.add(westPanel, BorderLayout.WEST); - JLabel jLabelFind = new JLabel(MessageManager.getString("label.find")); - jLabelFind.setFont(VERDANA_12); + /* + * 'Find' prompt goes top left + */ + JLabel findLabel = new JLabel( + " " + MessageManager.getString("label.find") + " "); + findLabel.setFont(VERDANA_12); + westPanel.add(findLabel); + /* + * search box + */ + searchBox = new JvCacheableInputBox<>(FINDER_CACHE_KEY, 25); + searchBox.setFont(VERDANA_12); + ((JTextComponent) searchBox.getEditor().getEditorComponent()) + .addCaretListener(new CaretListener() + { + @Override + public void caretUpdate(CaretEvent e) + { + textfield_caretUpdate(); + } + }); + searchBox.getEditor().getEditorComponent() + .addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + textfield_keyPressed(e); + } + }); + centrePanel.add(searchBox); + + /* + * search options checkboxes + */ + caseSensitive = new JCheckBox(); + caseSensitive.setHorizontalAlignment(SwingConstants.LEFT); + caseSensitive.setText(MessageManager.getString("label.match_case")); + + searchDescription = new JCheckBox(); + searchDescription + .setText(MessageManager.getString("label.include_description")); + + ignoreHidden = new JCheckBox(); + ignoreHidden.setText(MessageManager.getString("label.ignore_hidden")); + ignoreHidden.setToolTipText( + MessageManager.getString("label.ignore_hidden_tooltip")); + + centrePanel.add(caseSensitive); + centrePanel.add(searchDescription); + centrePanel.add(ignoreHidden); + + /* + * action buttons + */ JButton findAll = new JButton( MessageManager.getString("action.find_all")); findAll.setFont(VERDANA_12); - findAll.addActionListener(new java.awt.event.ActionListener() + findAll.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -93,11 +178,10 @@ public class GFinder extends JPanel findAll_actionPerformed(); } }); - JButton findNext = new JButton( MessageManager.getString("action.find_next")); findNext.setFont(VERDANA_12); - findNext.addActionListener(new java.awt.event.ActionListener() + findNext.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -105,19 +189,11 @@ public class GFinder extends JPanel findNext_actionPerformed(); } }); - - JPanel actionsPanel = new JPanel(); - GridLayout gridLayout1 = new GridLayout(); - actionsPanel.setLayout(gridLayout1); - gridLayout1.setHgap(0); - gridLayout1.setRows(3); - gridLayout1.setVgap(2); - + createFeatures = new JButton(); createFeatures.setEnabled(false); createFeatures.setFont(VERDANA_12); - createFeatures.setMargin(new Insets(0, 0, 0, 0)); createFeatures.setText(MessageManager.getString("label.new_feature")); - createFeatures.addActionListener(new java.awt.event.ActionListener() + createFeatures.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) @@ -125,60 +201,9 @@ public class GFinder extends JPanel createFeatures_actionPerformed(); } }); - searchBox.setFont(new java.awt.Font("Verdana", Font.PLAIN, 12)); - ((JTextComponent) searchBox.getEditor().getEditorComponent()) - .addCaretListener(new CaretListener() - { - @Override - public void caretUpdate(CaretEvent e) - { - textfield_caretUpdate(); - } - }); - searchBox.getEditor().getEditorComponent() - .addKeyListener(new java.awt.event.KeyAdapter() - { - @Override - public void keyPressed(KeyEvent e) - { - textfield_keyPressed(e); - } - }); - - caseSensitive.setHorizontalAlignment(SwingConstants.LEFT); - caseSensitive.setText(MessageManager.getString("label.match_case")); - - searchDescription - .setText(MessageManager.getString("label.include_description")); - - actionsPanel.add(findNext, null); - actionsPanel.add(findAll, null); - actionsPanel.add(createFeatures, null); - this.add(jLabelFind, java.awt.BorderLayout.WEST); - this.add(actionsPanel, java.awt.BorderLayout.EAST); - - JPanel jPanel2 = new JPanel(); - jPanel2.setPreferredSize(new Dimension(10, 1)); - JPanel jPanel3 = new JPanel(); - jPanel3.setPreferredSize(new Dimension(10, 1)); - JPanel jPanel4 = new JPanel(); - jPanel4.setLayout(new BorderLayout()); - this.add(jPanel2, java.awt.BorderLayout.SOUTH); - this.add(jPanel3, java.awt.BorderLayout.NORTH); - this.add(jPanel4, java.awt.BorderLayout.CENTER); - jPanel4.add(searchBox, java.awt.BorderLayout.NORTH); - - JPanel optionsPanel = new JPanel(); - - GridLayout optionsGridLayout = new GridLayout(); - optionsGridLayout.setHgap(0); - optionsGridLayout.setRows(2); - optionsGridLayout.setVgap(2); - optionsPanel.setLayout(optionsGridLayout); - optionsPanel.add(caseSensitive, null); - optionsPanel.add(searchDescription, null); - - jPanel4.add(optionsPanel, java.awt.BorderLayout.WEST); + eastPanel.add(findNext); + eastPanel.add(findAll); + eastPanel.add(createFeatures); } protected void textfield_keyPressed(KeyEvent e) @@ -236,15 +261,4 @@ public class GFinder extends JPanel } } - /** - * Returns unique key used for storing Finder cache items in the cache data - * structure - * - * @return - */ - public String getCacheKey() - { - return FINDER_CACHE_KEY; - } - } diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index adca17e..671bf6b 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -150,6 +150,7 @@ public class AnnotationRenderer */ public void dispose() { + hiddenColumns = null; hconsensus = null; complementConsensus = null; hStrucConsensus = null; diff --git a/src/jalview/schemes/FeatureSettingsAdapter.java b/src/jalview/schemes/FeatureSettingsAdapter.java index b15e4cf..6e8554f 100644 --- a/src/jalview/schemes/FeatureSettingsAdapter.java +++ b/src/jalview/schemes/FeatureSettingsAdapter.java @@ -36,6 +36,12 @@ public class FeatureSettingsAdapter implements FeatureSettingsModelI } @Override + public boolean isFeatureHidden(String type) + { + return false; + } + + @Override public boolean isGroupDisplayed(String group) { return true; diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index b552c21..915293e 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -1020,4 +1020,23 @@ public final class MappingUtils } } } + + /** + * Converts a list of [start, end] ranges to a single array of [start, end, + * start, end ...] + * + * @param ranges + * @return + */ + public static int[] listToArray(List ranges) + { + int[] result = new int[ranges.size() * 2]; + int i = 0; + for (int[] range : ranges) + { + result[i++] = range[0]; + result[i++] = range[1]; + } + return result; + } } diff --git a/src/jalview/util/Platform.java b/src/jalview/util/Platform.java index e8558fa..a5bee7c 100644 --- a/src/jalview/util/Platform.java +++ b/src/jalview/util/Platform.java @@ -29,11 +29,23 @@ import java.awt.event.MouseEvent; */ public class Platform { - private static Boolean isAMac = null, isWindows = null; + private static Boolean isAMac = null, isWindows = null, isLinux = null; private static Boolean isHeadless = null; /** + * added to check LaF for Linux + * + * @return + */ + public static boolean isLinux() + { + return (isLinux == null + ? (isLinux = (System.getProperty("os.name").indexOf("Linux") >= 0)) + : isLinux); + } + + /** * sorry folks - Macs really are different * * @return true if we do things in a special way. @@ -131,7 +143,8 @@ public class Platform { return false; } - return (jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() // .getMenuShortcutKeyMaskEx() + return (jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx() // .getMenuShortcutKeyMaskEx() & jalview.util.ShortcutKeyMaskExWrapper .getModifiersEx(e)) != 0; // getModifiers()) != 0; } @@ -139,8 +152,8 @@ public class Platform } /** - * A (case sensitive) file path comparator that ignores the difference between / - * and \ + * A (case sensitive) file path comparator that ignores the difference between + * / and \ * * @param path1 * @param path2 diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 8dcd1b3..c9a3a80 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -959,6 +959,7 @@ public abstract class AlignmentViewport ranges = null; currentTree = null; selectionGroup = null; + colSel = null; setAlignment(null); } @@ -3044,4 +3045,22 @@ public abstract class AlignmentViewport codingComplement.setUpdateStructures(needToUpdateStructureViews); } } + + @Override + public Iterator getViewAsVisibleContigs(boolean selectedRegionOnly) + { + int start = 0; + int end = 0; + if (selectedRegionOnly && selectionGroup != null) + { + start = selectionGroup.getStartRes(); + end = selectionGroup.getEndRes() + 1; + } + else + { + end = alignment.getWidth(); + } + return (alignment.getHiddenColumns().getVisContigsIterator(start, end, + false)); + } } diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 426ec1f..b348766 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -102,11 +102,11 @@ public abstract class FeatureRendererModel Map featureOrder = null; - protected PropertyChangeSupport changeSupport = new PropertyChangeSupport( - this); - protected AlignViewportI av; + private PropertyChangeSupport changeSupport = new PropertyChangeSupport( + this); + @Override public AlignViewportI getViewport() { @@ -299,13 +299,19 @@ public abstract class FeatureRendererModel { firing = Boolean.TRUE; findAllFeatures(true); // add all new features as visible - changeSupport.firePropertyChange("changeSupport", null, null); + notifyFeaturesChanged(); firing = Boolean.FALSE; } } } @Override + public void notifyFeaturesChanged() + { + changeSupport.firePropertyChange("changeSupport", null, null); + } + + @Override public List findFeaturesAtColumn(SequenceI sequence, int column) { /* diff --git a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java index f44a2d1..d7da519 100644 --- a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java +++ b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java @@ -29,9 +29,9 @@ import java.util.Set; public class FeaturesDisplayed implements FeaturesDisplayedI { - private Set featuresDisplayed = new HashSet(); + private Set featuresDisplayed = new HashSet<>(); - private Set featuresRegistered = new HashSet(); + private Set featuresRegistered = new HashSet<>(); public FeaturesDisplayed(FeaturesDisplayedI featuresDisplayed2) { @@ -93,6 +93,13 @@ public class FeaturesDisplayed implements FeaturesDisplayedI } @Override + public void setHidden(String featureType) + { + featuresDisplayed.remove(featureType); + featuresRegistered.add(featureType); + } + + @Override public boolean isRegistered(String type) { return featuresRegistered.contains(type); diff --git a/src/jalview/ws/dbsources/EmblCdsSource.java b/src/jalview/ws/dbsources/EmblCdsSource.java index a73af61..d02910c 100644 --- a/src/jalview/ws/dbsources/EmblCdsSource.java +++ b/src/jalview/ws/dbsources/EmblCdsSource.java @@ -23,9 +23,7 @@ package jalview.ws.dbsources; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefSource; -import com.stevesoft.pat.Regex; - -public class EmblCdsSource extends EmblXmlSource +public class EmblCdsSource extends EmblFlatfileSource // was EmblXmlSource { public EmblCdsSource() @@ -34,31 +32,12 @@ public class EmblCdsSource extends EmblXmlSource } @Override - public String getAccessionSeparator() - { - return null; - } - - @Override - public Regex getAccessionValidator() - { - return new Regex("^[A-Z]+[0-9]+"); - } - - @Override public String getDbSource() { return DBRefSource.EMBLCDS; } @Override - public String getDbVersion() - { - return "0"; // TODO : this is dynamically set for a returned record - not - // tied to proxy - } - - @Override public AlignmentI getSequenceRecords(String queries) throws Exception { if (queries.indexOf(".") > -1) @@ -68,15 +47,6 @@ public class EmblCdsSource extends EmblXmlSource return getEmblSequenceRecords(DBRefSource.EMBLCDS, queries); } - @Override - public boolean isValidReference(String accession) - { - // most embl CDS refs look like .. - // TODO: improve EMBLCDS regex - return (accession == null || accession.length() < 2) ? false - : getAccessionValidator().search(accession); - } - /** * cDNA for LDHA_CHICK swissprot sequence */ @@ -92,10 +62,4 @@ public class EmblCdsSource extends EmblXmlSource return "EMBL (CDS)"; } - @Override - public int getTier() - { - return 0; - } - } diff --git a/src/jalview/ws/dbsources/EmblFlatfileSource.java b/src/jalview/ws/dbsources/EmblFlatfileSource.java new file mode 100644 index 0000000..6536958 --- /dev/null +++ b/src/jalview/ws/dbsources/EmblFlatfileSource.java @@ -0,0 +1,121 @@ +package jalview.ws.dbsources; + +import java.io.File; +import java.io.IOException; + +import com.stevesoft.pat.Regex; + +import jalview.bin.Cache; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +import jalview.io.DataSourceType; +import jalview.io.EmblFlatFile; +import jalview.io.FileParse; +import jalview.ws.ebi.EBIFetchClient; + +/** + * A class that does partial parsing of an EMBL flatfile. + * + * @author gmcarstairs + * + */ +public abstract class EmblFlatfileSource extends EbiFileRetrievedProxy +{ + private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+"); + + @Override + public String getDbVersion() + { + return "0"; + } + + @Override + public String getAccessionSeparator() + { + return null; + } + + @Override + public Regex getAccessionValidator() + { + return ACCESSION_REGEX; + } + + @Override + public boolean isValidReference(String accession) + { + if (accession == null || accession.length() < 2) + { + return false; + } + return getAccessionValidator().search(accession); + } + + @Override + public AlignmentI getSequenceRecords(String queries) throws Exception + { + return null; + } + + @Override + public int getTier() + { + return 0; + } + + protected AlignmentI getEmblSequenceRecords(String dbName, String query) + throws Exception + { + startQuery(); + EBIFetchClient dbFetch = new EBIFetchClient(); + File reply; + try + { + reply = dbFetch.fetchDataAsFile( + dbName.toLowerCase() + ":" + query.trim(), null, "gz"); + } catch (Exception e) + { + stopQuery(); + throw new Exception( + String.format("EBI EMBL retrieval failed for %s:%s", + dbName.toLowerCase(), query.trim()), + e); + } + return getEmblSequenceRecords(dbName, query, reply); + } + + private AlignmentI getEmblSequenceRecords(String dbName, String query, + File reply) throws IOException + { + AlignmentI al = null; + + if (reply != null && reply.exists()) + { + file = reply.getAbsolutePath(); + FileParse fp = new FileParse(file, DataSourceType.FILE); + EmblFlatFile emblParser = new EmblFlatFile(fp, getDbSource()); + emblParser.parse(); + SequenceI[] seqs = emblParser.getSeqsAsArray(); + if (seqs.length > 0) + { + al = new Alignment(seqs); + } + + if (al == null) + { + Cache.log.error( + "No record found for '" + dbName + ":" + query + "'"); + } + } + + stopQuery(); + return al; + } + + @Override + public boolean isDnaCoding() + { + return true; + } +} diff --git a/src/jalview/ws/dbsources/EmblSource.java b/src/jalview/ws/dbsources/EmblSource.java index 6bbe2e1..df43bc3 100644 --- a/src/jalview/ws/dbsources/EmblSource.java +++ b/src/jalview/ws/dbsources/EmblSource.java @@ -23,13 +23,11 @@ package jalview.ws.dbsources; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefSource; -import com.stevesoft.pat.Regex; - /** * @author JimP * */ -public class EmblSource extends EmblXmlSource +public class EmblSource extends EmblFlatfileSource // was EmblXmlSource { public EmblSource() @@ -40,29 +38,6 @@ public class EmblSource extends EmblXmlSource /* * (non-Javadoc) * - * @see jalview.ws.DbSourceProxy#getAccessionSeparator() - */ - @Override - public String getAccessionSeparator() - { - // TODO Auto-generated method stub - return null; - } - - /* - * (non-Javadoc) - * - * @see jalview.ws.DbSourceProxy#getAccessionValidator() - */ - @Override - public Regex getAccessionValidator() - { - return new Regex("^[A-Z]+[0-9]+"); - } - - /* - * (non-Javadoc) - * * @see jalview.ws.DbSourceProxy#getDbSource() */ @Override @@ -74,18 +49,6 @@ public class EmblSource extends EmblXmlSource /* * (non-Javadoc) * - * @see jalview.ws.DbSourceProxy#getDbVersion() - */ - @Override - public String getDbVersion() - { - // TODO Auto-generated method stub - return "0"; - } - - /* - * (non-Javadoc) - * * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[]) */ @Override @@ -94,21 +57,6 @@ public class EmblSource extends EmblXmlSource return getEmblSequenceRecords(DBRefSource.EMBL, queries); } - /* - * (non-Javadoc) - * - * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String) - */ - @Override - public boolean isValidReference(String accession) - { - // most embl refs look like .. - - return (accession == null || accession.length() < 2) ? false - : getAccessionValidator().search(accession); - - } - /** * return LHD_CHICK coding gene */ @@ -123,10 +71,4 @@ public class EmblSource extends EmblXmlSource { return "EMBL"; // getDbSource(); } - - @Override - public int getTier() - { - return 0; - } } diff --git a/src/jalview/ws/dbsources/EmblXmlSource.java b/src/jalview/ws/dbsources/EmblXmlSource.java index a420d9f..97d7c9f 100644 --- a/src/jalview/ws/dbsources/EmblXmlSource.java +++ b/src/jalview/ws/dbsources/EmblXmlSource.java @@ -20,6 +20,27 @@ */ package jalview.ws.dbsources; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import com.stevesoft.pat.Regex; + import jalview.analysis.SequenceIdMatcher; import jalview.bin.Cache; import jalview.datamodel.Alignment; @@ -35,33 +56,23 @@ import jalview.util.DBRefUtils; import jalview.util.DnaUtils; import jalview.util.MapList; import jalview.util.MappingUtils; -import jalview.util.MessageManager; import jalview.ws.ebi.EBIFetchClient; import jalview.xml.binding.embl.EntryType; import jalview.xml.binding.embl.EntryType.Feature; import jalview.xml.binding.embl.EntryType.Feature.Qualifier; +import jalview.xml.binding.embl.ROOT; import jalview.xml.binding.embl.XrefType; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - +/** + * Provides XML binding and parsing of EMBL or EMBLCDS records retrieved from + * (e.g.) {@code https://www.ebi.ac.uk/ena/data/view/x53828&display=xml}. + * + * @deprecated endpoint withdrawn August 2020 (JAL-3692), use EmblFlatfileSource + */ public abstract class EmblXmlSource extends EbiFileRetrievedProxy { + private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+"); + /* * JAL-1856 Embl returns this text for query not found */ @@ -96,9 +107,10 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy } catch (Exception e) { stopQuery(); - throw new Exception(MessageManager.formatMessage( - "exception.ebiembl_retrieval_failed_on", new String[] - { emprefx.toLowerCase(), query.trim() }), e); + throw new Exception( + String.format("EBI EMBL XML retrieval failed for %s:%s", + emprefx.toLowerCase(), query.trim()), + e); } return getEmblSequenceRecords(emprefx, query, reply); } @@ -180,8 +192,9 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy XMLStreamReader streamReader = XMLInputFactory.newInstance() .createXMLStreamReader(is); javax.xml.bind.Unmarshaller um = jc.createUnmarshaller(); - jalview.xml.binding.embl.ROOT root = (jalview.xml.binding.embl.ROOT) um - .unmarshal(streamReader); + JAXBElement rootElement = um.unmarshal(streamReader, + ROOT.class); + ROOT root = rootElement.getValue(); /* * document root contains either "entry" or "entrySet" @@ -561,6 +574,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy proteinSeq = new Sequence(proteinSeqName, product.getSequenceAsString()); matcher.add(proteinSeq); + proteinSeq.setDescription(product.getDescription()); peptides.add(proteinSeq); } dnaToProteinMapping.setTo(proteinSeq); @@ -614,8 +628,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy && dnaToProteinMapping.getTo() != null) { DBRefEntry dnaToEmblProteinRef = new DBRefEntry( - DBRefSource.EMBLCDSProduct, sequenceVersion, - proteinId); + DBRefSource.EMBLCDSProduct, sequenceVersion, proteinId); dnaToEmblProteinRef.setMap(dnaToProteinMapping); dnaToProteinMapping.setMappedFromId(proteinId); dna.addDBRef(dnaToEmblProteinRef); @@ -644,7 +657,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy { return new int[] {}; } - + try { List ranges = DnaUtils.parseLocation(location); @@ -708,6 +721,40 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy return sf; } + @Override + public String getAccessionSeparator() + { + return null; + } + + @Override + public Regex getAccessionValidator() + { + return ACCESSION_REGEX; + } + + @Override + public String getDbVersion() + { + return "0"; + } + + @Override + public int getTier() + { + return 0; + } + + @Override + public boolean isValidReference(String accession) + { + if (accession == null || accession.length() < 2) + { + return false; + } + return getAccessionValidator().search(accession); + } + /** * Truncates (if necessary) the exon intervals to match 3 times the length of * the protein; also accepts 3 bases longer (for stop codon not included in @@ -726,7 +773,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy } int expectedCdsLength = proteinLength * 3; int exonLength = MappingUtils.getLength(Arrays.asList(exon)); - + /* * if exon length matches protein, or is shorter, or longer by the * length of a stop codon (3 bases), then leave it unchanged @@ -736,7 +783,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy { return exon; } - + int origxon[]; int sxpos = -1; int endxon = 0; @@ -756,7 +803,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy // .println("Truncating final exon interval on region by " // + (cdspos - cdslength)); } - + /* * shrink the final exon - reduce end position if forward * strand, increase it if reverse @@ -772,7 +819,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy break; } } - + if (sxpos != -1) { // and trim the exon interval set if necessary diff --git a/src/jalview/ws/dbsources/Pfam.java b/src/jalview/ws/dbsources/Pfam.java index 8877c34..47e66ac 100644 --- a/src/jalview/ws/dbsources/Pfam.java +++ b/src/jalview/ws/dbsources/Pfam.java @@ -35,6 +35,11 @@ import com.stevesoft.pat.Regex; */ abstract public class Pfam extends Xfam { + /* + * append to URLs to retrieve as a gzipped file + */ + protected static final String GZIPPED = "/gzipped"; + static final String PFAM_BASEURL_KEY = "PFAM_BASEURL"; private static final String DEFAULT_PFAM_BASEURL = "https://pfam.xfam.org"; diff --git a/src/jalview/ws/dbsources/PfamFull.java b/src/jalview/ws/dbsources/PfamFull.java index 0600427..d71892b 100644 --- a/src/jalview/ws/dbsources/PfamFull.java +++ b/src/jalview/ws/dbsources/PfamFull.java @@ -34,7 +34,7 @@ public class PfamFull extends Pfam @Override public String getURLSuffix() { - return "/alignment/full"; + return "/alignment/full" + GZIPPED; } /* diff --git a/src/jalview/ws/dbsources/PfamSeed.java b/src/jalview/ws/dbsources/PfamSeed.java index dff8a17..f64d07f 100644 --- a/src/jalview/ws/dbsources/PfamSeed.java +++ b/src/jalview/ws/dbsources/PfamSeed.java @@ -36,7 +36,7 @@ public class PfamSeed extends Pfam @Override public String getURLSuffix() { - return "/alignment/seed"; + return "/alignment/seed" + GZIPPED; } /* diff --git a/src/jalview/ws/dbsources/Rfam.java b/src/jalview/ws/dbsources/Rfam.java index 1d9d99a..c9ee7fc 100644 --- a/src/jalview/ws/dbsources/Rfam.java +++ b/src/jalview/ws/dbsources/Rfam.java @@ -36,6 +36,11 @@ abstract public class Rfam extends Xfam private static final String DEFAULT_RFAM_BASEURL = "https://rfam.xfam.org"; + /* + * append to URLs to retrieve as a gzipped file + */ + protected static final String GZIPPED = "?gzip=1&download=1"; + @Override protected String getURLPrefix() { diff --git a/src/jalview/ws/dbsources/RfamFull.java b/src/jalview/ws/dbsources/RfamFull.java index d815336..396511c 100644 --- a/src/jalview/ws/dbsources/RfamFull.java +++ b/src/jalview/ws/dbsources/RfamFull.java @@ -36,7 +36,7 @@ public class RfamFull extends Rfam @Override public String getURLSuffix() { - return "/alignment/full"; + return "/alignment/full" + GZIPPED; } /* diff --git a/src/jalview/ws/dbsources/RfamSeed.java b/src/jalview/ws/dbsources/RfamSeed.java index a74e829..eaa574b 100644 --- a/src/jalview/ws/dbsources/RfamSeed.java +++ b/src/jalview/ws/dbsources/RfamSeed.java @@ -36,8 +36,7 @@ public class RfamSeed extends Rfam @Override public String getURLSuffix() { - // to download gzipped file add '?gzip=1' - return "/alignment/stockholm"; + return "/alignment/stockholm" + GZIPPED; } /* diff --git a/src/jalview/ws/dbsources/Xfam.java b/src/jalview/ws/dbsources/Xfam.java index b83f558..f0cb14b 100644 --- a/src/jalview/ws/dbsources/Xfam.java +++ b/src/jalview/ws/dbsources/Xfam.java @@ -36,7 +36,6 @@ import jalview.ws.seqfetcher.DbSourceProxyImpl; */ public abstract class Xfam extends DbSourceProxyImpl { - public Xfam() { super(); diff --git a/src/jalview/ws/ebi/EBIFetchClient.java b/src/jalview/ws/ebi/EBIFetchClient.java index 07a9df4..8ab5fbb 100644 --- a/src/jalview/ws/ebi/EBIFetchClient.java +++ b/src/jalview/ws/ebi/EBIFetchClient.java @@ -27,6 +27,7 @@ import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; @@ -91,7 +92,7 @@ public class EBIFetchClient * the query formatted as db:query1;query2;query3 * @param format * the format wanted - * @param extension + * @param ext * for the temporary file to hold response (without separator) * @return the file holding the response * @throws OutOfMemoryError @@ -201,7 +202,8 @@ public class EBIFetchClient { // long time = System.currentTimeMillis(); String url = buildUrl(ids, database, format); - + InputStream is = null; + BufferedReader br = null; try { URL rcall = new URL(url); @@ -213,7 +215,7 @@ public class EBIFetchClient System.err.println("Warning: response code " + responseCode + " for " + url); } - InputStream is = new BufferedInputStream(conn.getInputStream()); + is = new BufferedInputStream(conn.getInputStream()); if (outFile != null) { FileOutputStream fio = new FileOutputStream(outFile); @@ -228,7 +230,7 @@ public class EBIFetchClient } else { - BufferedReader br = new BufferedReader(new InputStreamReader(is)); + br = new BufferedReader(new InputStreamReader(is)); String rtn; List arl = new ArrayList(); while ((rtn = br.readLine()) != null) @@ -257,6 +259,24 @@ public class EBIFetchClient { // System.err.println("EBIFetch took " + (System.currentTimeMillis() - // time) + " ms"); + if (is != null) + { + try + { + is.close(); + } catch (IOException e) + { + } + } + if (br != null) + { + try + { + br.close(); + } catch (IOException e) + { + } + } } return null; } @@ -275,8 +295,8 @@ public class EBIFetchClient if (database.equalsIgnoreCase(DBRefSource.EMBL) || database.equalsIgnoreCase(DBRefSource.EMBLCDS)) { - url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase() - + (format != null ? "&" + format : ""); + url = "https://www.ebi.ac.uk/ena/browser/api/embl/" + + ids.toLowerCase() + "?download=true&gzip=true"; } else { diff --git a/src/jalview/ws/sifts/SiftsClient.java b/src/jalview/ws/sifts/SiftsClient.java index b5f9653..9790f79 100644 --- a/src/jalview/ws/sifts/SiftsClient.java +++ b/src/jalview/ws/sifts/SiftsClient.java @@ -20,28 +20,6 @@ */ package jalview.ws.sifts; -import jalview.analysis.AlignSeq; -import jalview.analysis.scoremodels.ScoreMatrix; -import jalview.analysis.scoremodels.ScoreModels; -import jalview.api.DBRefEntryI; -import jalview.api.SiftsClientI; -import jalview.datamodel.DBRefEntry; -import jalview.datamodel.DBRefSource; -import jalview.datamodel.SequenceI; -import jalview.io.StructureFile; -import jalview.schemes.ResidueProperties; -import jalview.structure.StructureMapping; -import jalview.util.Comparison; -import jalview.util.DBRefUtils; -import jalview.util.Format; -import jalview.xml.binding.sifts.Entry; -import jalview.xml.binding.sifts.Entry.Entity; -import jalview.xml.binding.sifts.Entry.Entity.Segment; -import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion; -import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue; -import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb; -import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -73,6 +51,28 @@ import javax.xml.stream.XMLStreamReader; import MCview.Atom; import MCview.PDBChain; +import jalview.analysis.AlignSeq; +import jalview.analysis.scoremodels.ScoreMatrix; +import jalview.analysis.scoremodels.ScoreModels; +import jalview.api.DBRefEntryI; +import jalview.api.SiftsClientI; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; +import jalview.datamodel.SequenceI; +import jalview.io.BackupFiles; +import jalview.io.StructureFile; +import jalview.schemes.ResidueProperties; +import jalview.structure.StructureMapping; +import jalview.util.Comparison; +import jalview.util.DBRefUtils; +import jalview.util.Format; +import jalview.xml.binding.sifts.Entry; +import jalview.xml.binding.sifts.Entry.Entity; +import jalview.xml.binding.sifts.Entry.Entity.Segment; +import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion; +import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue; +import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb; +import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail; public class SiftsClient implements SiftsClientI { @@ -123,6 +123,7 @@ public class SiftsClient implements SiftsClientI private enum CoordinateSys { UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe"); + private String name; private CoordinateSys(String name) @@ -140,6 +141,7 @@ public class SiftsClient implements SiftsClientI { NAME_SEC_STRUCTURE("nameSecondaryStructure"), CODE_SEC_STRUCTURE("codeSecondaryStructure"), ANNOTATION("Annotation"); + private String code; private ResidueDetailType(String code) @@ -225,7 +227,7 @@ public class SiftsClient implements SiftsClientI SiftsSettings.getCacheThresholdInDays())) { File oldSiftsFile = new File(siftsFileName + "_old"); - siftsFile.renameTo(oldSiftsFile); + BackupFiles.moveFileToFile(siftsFile, oldSiftsFile); try { siftsFile = downloadSiftsFile(pdbId.toLowerCase()); @@ -234,7 +236,7 @@ public class SiftsClient implements SiftsClientI } catch (IOException e) { e.printStackTrace(); - oldSiftsFile.renameTo(siftsFile); + BackupFiles.moveFileToFile(oldSiftsFile, siftsFile); return new File(siftsFileName); } } @@ -461,7 +463,7 @@ public class SiftsClient implements SiftsClientI SequenceI seq, java.io.PrintStream os) throws SiftsException { List 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); @@ -492,7 +494,7 @@ public class SiftsClient implements SiftsClientI TreeMap resNumMap = new TreeMap(); List segments = entity.getSegment(); SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap, - omitNonObserved, nonObservedShiftIndex,pdbeNonObserved); + omitNonObserved, nonObservedShiftIndex, pdbeNonObserved); processSegments(segments, shp); try { @@ -514,18 +516,20 @@ 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 from=new ArrayList<>(),to=new ArrayList<>(); - int[]_cfrom=null,_cto=null; + List 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) @@ -533,19 +537,23 @@ 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; } - if (_cto==null || pdbpos!=1+_cto[1]) + else + { + _cfrom[1] = seqps; + } + 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; } } @@ -567,8 +575,7 @@ 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(); @@ -699,12 +706,12 @@ 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) @@ -1044,6 +1051,7 @@ public class SiftsClient implements SiftsClientI { return pdbeNonObserved; } + public SequenceI getSeq() { return seq; diff --git a/test/jalview/analysis/FinderTest.java b/test/jalview/analysis/FinderTest.java index 5f64b28..95e6c0d 100644 --- a/test/jalview/analysis/FinderTest.java +++ b/test/jalview/analysis/FinderTest.java @@ -71,8 +71,13 @@ public class FinderTest Cache.applicationProperties.setProperty("PAD_GAPS", Boolean.FALSE.toString()); - String seqData = "seq1seq1/8-18 ABCD--EF-GHIJI\n" + "seq2 A--BCDefHI\n" - + "seq3 --bcdEFH\n" + "seq4 aa---aMMMMMaaa\n"; + //@formatter:off + String seqData = + "seq1/8-18 ABCD--EF-GHIJI\n" + + "seq2 A--BCDefHI\n" + + "seq3 --bcdEFH\n" + + "seq4 aa---aMMMMMaaa\n"; + //@formatter:on af = new FileLoader().LoadFileWaitTillLoaded(seqData, DataSourceType.PASTE); av = af.getViewport(); @@ -95,20 +100,20 @@ public class FinderTest * find next match only */ Finder f = new Finder(av); - f.findNext("E.H", false, false); // 'E, any character, H' + f.findNext("E.H", false, false, false); // 'E, any character, H' // should match seq2 efH only SearchResultsI sr = f.getSearchResults(); - assertEquals(sr.getSize(), 1); + assertEquals(sr.getCount(), 1); List matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 7); f = new Finder(av); - f.findAll("E.H", false, false); // 'E, any character, H' + f.findAll("E.H", false, false, false); // 'E, any character, H' // should match seq2 efH and seq3 EFH sr = f.getSearchResults(); - assertEquals(sr.getSize(), 2); + assertEquals(sr.getCount(), 2); matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); assertSame(matches.get(1).getSequence(), al.getSequenceAt(2)); @@ -129,9 +134,9 @@ public class FinderTest /* * find first match should return seq1 residue 9 */ - f.findNext("9", false, false); + f.findNext("9", false, false, false); SearchResultsI sr = f.getSearchResults(); - assertEquals(sr.getSize(), 1); + assertEquals(sr.getCount(), 1); List matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); assertEquals(matches.get(0).getStart(), 9); @@ -139,11 +144,14 @@ public class FinderTest /* * find all matches should return seq1 and seq4 (others are too short) + * (and not matches in sequence ids) */ f = new Finder(av); - f.findAll("9", false, false); + String name = al.getSequenceAt(0).getName(); + al.getSequenceAt(0).setName("Q9XA0"); + f.findAll("9", false, false, false); sr = f.getSearchResults(); - assertEquals(sr.getSize(), 2); + assertEquals(sr.getCount(), 2); matches = sr.getResults(); assertSame(matches.get(0).getSequence(), al.getSequenceAt(0)); assertSame(matches.get(1).getSequence(), al.getSequenceAt(3)); @@ -151,12 +159,13 @@ public class FinderTest assertEquals(matches.get(0).getEnd(), 9); assertEquals(matches.get(1).getStart(), 9); assertEquals(matches.get(1).getEnd(), 9); + al.getSequenceAt(0).setName(name); /* * parsing of search string as integer is strict */ f = new Finder(av); - f.findNext(" 9", false, false); + f.findNext(" 9", false, false, false); assertTrue(f.getSearchResults().isEmpty()); } @@ -167,41 +176,41 @@ public class FinderTest public void testFindNext() { /* - * start at second sequence; colIndex of -1 + * start at second sequence; residueIndex of -1 * means sequence id / description is searched */ Finder f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", -1); - f.findNext("e", false, false); // matches id + PA.setValue(f, "residueIndex", -1); + f.findNext("e", false, false, false); // matches id assertTrue(f.getSearchResults().isEmpty()); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); - // colIndex is now 0 - for use in next find next + // residueIndex is now 0 - for use in next find next // searching A--BCDefHI - assertEquals(PA.getValue(f, "columnIndex"), 0); + assertEquals(PA.getValue(f, "residueIndex"), 0); f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", 0); - f.findNext("e", false, false); // matches in sequence + PA.setValue(f, "residueIndex", 0); + f.findNext("e", false, false, false); // matches in sequence assertTrue(f.getIdMatches().isEmpty()); - assertEquals(f.getSearchResults().getSize(), 1); + assertEquals(f.getSearchResults().getCount(), 1); List matches = f.getSearchResults().getResults(); assertEquals(matches.get(0).getStart(), 5); assertEquals(matches.get(0).getEnd(), 5); assertSame(matches.get(0).getSequence(), al.getSequenceAt(1)); // still in the second sequence assertEquals(PA.getValue(f, "sequenceIndex"), 1); - // next column position to search from is 7 - assertEquals(PA.getValue(f, "columnIndex"), 7); + // next residue offset to search from is 5 + assertEquals(PA.getValue(f, "residueIndex"), 5); // find next from end of sequence - finds next sequence id f = new Finder(av); PA.setValue(f, "sequenceIndex", 1); - PA.setValue(f, "columnIndex", 7); - f.findNext("e", false, false); + PA.setValue(f, "residueIndex", 7); + f.findNext("e", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(2)); assertTrue(f.getSearchResults().isEmpty()); @@ -223,7 +232,7 @@ public class FinderTest * find first match only */ Finder f = new Finder(av2); - f.findNext("rAF", false, true); + f.findNext("rAF", false, true, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertTrue(f.getSearchResults().isEmpty()); @@ -232,7 +241,7 @@ public class FinderTest * find all matches */ f = new Finder(av2); - f.findAll("rAF", false, true); + f.findAll("rAF", false, true, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1)); @@ -242,7 +251,7 @@ public class FinderTest * case sensitive */ f = new Finder(av2); - f.findAll("RAF", true, true); + f.findAll("RAF", true, true, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertTrue(f.getSearchResults().isEmpty()); @@ -258,12 +267,12 @@ public class FinderTest /* * sequence matches should have no duplicates */ - f.findAll("EFH", false, true); + f.findAll("EFH", false, true, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0)); assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1)); - assertEquals(f.getSearchResults().getSize(), 2); + assertEquals(f.getSearchResults().getCount(), 2); SearchResultMatchI match = f.getSearchResults().getResults().get(0); assertSame(match.getSequence(), al2.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -286,7 +295,7 @@ public class FinderTest * case insensitive; seq1 occurs twice in sequence id but * only one match should be returned */ - f.findAll("SEQ1", false, false); + f.findAll("SEQ1", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al.getSequenceAt(0)); SearchResultsI searchResults = f.getSearchResults(); @@ -296,7 +305,7 @@ public class FinderTest * case sensitive */ f = new Finder(av); - f.findAll("SEQ1", true, false); + f.findAll("SEQ1", true, false, false); searchResults = f.getSearchResults(); assertTrue(searchResults.isEmpty()); @@ -307,11 +316,11 @@ public class FinderTest AlignViewportI av2 = new AlignViewport(al2); al2.addSequence(new Sequence("aBz", "xyzabZpqrAbZ")); f = new Finder(av2); - f.findAll("ABZ", false, false); + f.findAll("ABZ", false, false, false); assertEquals(f.getIdMatches().size(), 1); assertSame(f.getIdMatches().get(0), al2.getSequenceAt(4)); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al2.getSequenceAt(4)); assertEquals(match.getStart(), 4); @@ -328,13 +337,17 @@ public class FinderTest @Test(groups = "Functional") public void testFind_findNext() { + // "seq1/8-18 ABCD--EF-GHIJI\n" + + // "seq2 A--BCDefHI\n" + + // "seq3 --bcdEFH\n" + + // "seq4 aa---aMMMMMaaa\n"; /* * efh should be matched in seq2 only */ FinderI f = new Finder(av); - f.findNext("EfH", false, false); + f.findNext("EfH", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -344,39 +357,39 @@ public class FinderTest * I should be found in seq1 (twice) and seq2 (once) */ f = new Finder(av); - f.findNext("I", false, false); // find next: seq1/16 + f.findNext("I", false, false, false); // find next: seq1/16 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); assertEquals(match.getEnd(), 16); - f.findNext("I", false, false); // find next: seq1/18 + f.findNext("I", false, false, false); // find next: seq1/18 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 18); assertEquals(match.getEnd(), 18); - f.findNext("I", false, false); // find next: seq2/8 + f.findNext("I", false, false, false); // find next: seq2/8 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 8); assertEquals(match.getEnd(), 8); - f.findNext("I", false, false); + f.findNext("I", false, false, false); assertTrue(f.getSearchResults().isEmpty()); /* * find should reset to start of alignment after a failed search */ - f.findNext("I", false, false); // find next: seq1/16 + f.findNext("I", false, false, false); // find next: seq1/16 searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -388,12 +401,12 @@ public class FinderTest * result */ @Test(groups = "Functional") - public void testFind_maximalResultOnly() + public void testFindAll_maximalResultOnly() { Finder f = new Finder(av); - f.findAll("M+", false, false); + f.findAll("M+", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(3)); assertEquals(match.getStart(), 4); // dataset sequence positions @@ -404,12 +417,12 @@ public class FinderTest * Test finding all matches of a sequence pattern in an alignment */ @Test(groups = "Functional") - public void testFind_findAll() + public void testFindAll() { Finder f = new Finder(av); - f.findAll("EfH", false, false); + f.findAll("EfH", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 5); @@ -422,9 +435,9 @@ public class FinderTest /* * find all I should find 2 positions in seq1, 1 in seq2 */ - f.findAll("I", false, false); + f.findAll("I", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 3); + assertEquals(searchResults.getCount(), 3); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -443,16 +456,16 @@ public class FinderTest * Test finding all matches, case-sensitive */ @Test(groups = "Functional") - public void testFind_findAllCaseSensitive() + public void testFindAll_caseSensitive() { Finder f = new Finder(av); /* * BC should match seq1/9-10 and seq2/2-3 */ - f.findAll("BC", true, false); + f.findAll("BC", true, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 9); @@ -466,15 +479,15 @@ public class FinderTest * bc should match seq3/1-2 */ f = new Finder(av); - f.findAll("bc", true, false); + f.findAll("bc", true, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 1); assertEquals(match.getEnd(), 2); - f.findAll("bC", true, false); + f.findAll("bC", true, false, false); assertTrue(f.getSearchResults().isEmpty()); } @@ -482,7 +495,7 @@ public class FinderTest * Test finding next match of a sequence pattern in a selection group */ @Test(groups = "Functional") - public void testFind_inSelection() + public void testFindNext_inSelection() { /* * select sequences 2 and 3, columns 4-6 which contains @@ -497,10 +510,10 @@ public class FinderTest av.setSelectionGroup(sg); FinderI f = new Finder(av); - f.findNext("b", false, false); + f.findNext("b", false, false, false); assertTrue(f.getIdMatches().isEmpty()); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 2); @@ -509,23 +522,23 @@ public class FinderTest /* * a second Find should not return the 'b' in seq3 as outside the selection */ - f.findNext("b", false, false); + f.findNext("b", false, false, false); assertTrue(f.getSearchResults().isEmpty()); assertTrue(f.getIdMatches().isEmpty()); f = new Finder(av); - f.findNext("d", false, false); + f.findNext("d", false, false, false); assertTrue(f.getIdMatches().isEmpty()); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 4); assertEquals(match.getEnd(), 4); - f.findNext("d", false, false); + f.findNext("d", false, false, false); assertTrue(f.getIdMatches().isEmpty()); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 3); @@ -536,7 +549,7 @@ public class FinderTest * Test finding all matches of a search pattern in a selection group */ @Test(groups = "Functional") - public void testFind_findAllInSelection() + public void testFindAll_inSelection() { /* * select sequences 2 and 3, columns 4-6 which contains @@ -554,12 +567,12 @@ public class FinderTest * search for 'e' should match two sequence ids and one residue */ Finder f = new Finder(av); - f.findAll("e", false, false); + f.findAll("e", false, false, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); assertSame(f.getIdMatches().get(1), al.getSequenceAt(2)); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 4); @@ -569,7 +582,7 @@ public class FinderTest * search for 'Q' should match two sequence ids only */ f = new Finder(av); - f.findAll("Q", false, false); + f.findAll("Q", false, false, false); assertEquals(f.getIdMatches().size(), 2); assertSame(f.getIdMatches().get(0), al.getSequenceAt(1)); assertSame(f.getIdMatches().get(1), al.getSequenceAt(2)); @@ -600,10 +613,10 @@ public class FinderTest * search for 'I' should match two sequence positions */ Finder f = new Finder(av); - f.findAll("I", false, false); + f.findAll("I", false, false, false); assertTrue(f.getIdMatches().isEmpty()); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(0)); assertEquals(match.getStart(), 16); @@ -615,7 +628,8 @@ public class FinderTest } /** - * Test that find does not report hidden positions + * Test that find does not report hidden positions, but does report matches that + * span hidden gaps */ @Test(groups = "Functional") public void testFind_withHiddenColumns() @@ -629,43 +643,92 @@ public class FinderTest */ /* - * hide 2-4 (CD- -BC bcd ---) + * hide column 3 only, search for aaa + * should find two matches: aa-[-]-aa and trailing aaa */ HiddenColumns hc = new HiddenColumns(); - hc.hideColumns(2, 4); + hc.hideColumns(3, 3); al.setHiddenColumns(hc); + Finder f = new Finder(av); + f.findAll("aaa", false, false, false); + SearchResultsI searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 2); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 9); + assertEquals(match.getEnd(), 11); + + /* + * hide 2-4 (CD- -BC bcd ---) + */ + hc.hideColumns(2, 4); /* * find all search for D should ignore hidden positions in seq1 and seq3, * find the visible D in seq2 */ - Finder f = new Finder(av); - f.findAll("D", false, false); - SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); - SearchResultMatchI match = searchResults.getResults().get(0); + f = new Finder(av); + f.findAll("D", false, false, false); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 4); assertEquals(match.getEnd(), 4); /* + * search for AD should fail although these are now + * consecutive in the visible columns + */ + f = new Finder(av); + f.findAll("AD", false, false, false); + searchResults = f.getSearchResults(); + assertTrue(searchResults.isEmpty()); + + /* + * find all 'aaa' should find both start and end of seq4 + * (first run includes hidden gaps) + */ + f = new Finder(av); + f.findAll("aaa", false, false, false); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 9); + assertEquals(match.getEnd(), 11); + + /* * hide columns 2-5: - * find all 'aaa' should find end of seq4 only + * find all 'aaa' should match twice in seq4 + * (first match partly hidden, second all visible) */ hc.hideColumns(2, 5); f = new Finder(av); - f.findAll("aaa", false, false); + f.findAll("aaa", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 2); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(3)); + assertEquals(match.getStart(), 1); + assertEquals(match.getEnd(), 3); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(3)); assertEquals(match.getStart(), 9); assertEquals(match.getEnd(), 11); /* * find all 'BE' should not match across hidden columns in seq1 */ - f.findAll("BE", false, false); + f.findAll("BE", false, false, false); assertTrue(f.getSearchResults().isEmpty()); /* @@ -675,9 +738,9 @@ public class FinderTest hc.revealAllHiddenColumns(new ColumnSelection()); hc.hideColumns(8, 13); f = new Finder(av); - f.findNext("H", false, false); + f.findNext("H", false, false, false); searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 1); + assertEquals(searchResults.getCount(), 1); match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(2)); assertEquals(match.getStart(), 6); @@ -718,9 +781,9 @@ public class FinderTest * should match seq2/1, seq2/7, not seq3/6 */ Finder f = new Finder(av); - f.findAll("[AH]", false, false); + f.findAll("[AH]", false, false, false); SearchResultsI searchResults = f.getSearchResults(); - assertEquals(searchResults.getSize(), 2); + assertEquals(searchResults.getCount(), 2); SearchResultMatchI match = searchResults.getResults().get(0); assertSame(match.getSequence(), al.getSequenceAt(1)); assertEquals(match.getStart(), 1); @@ -730,4 +793,123 @@ public class FinderTest assertEquals(match.getStart(), 7); assertEquals(match.getEnd(), 7); } + + @Test(groups = "Functional") + public void testFind_ignoreHiddenColumns() + { + /* + * 0 5 9 + * ABCD--EF-GHI + * A--BCDefHI + * --bcdEFH + * aa---aMMMMMaaa + */ + HiddenColumns hc = new HiddenColumns(); + hc.hideColumns(2, 4); + hc.hideColumns(7, 7); + al.setHiddenColumns(hc); + + /* + * now have + * 015689 + * AB-E-GHI + * A-DeHI + * --EF + * aaaMMMMaaa + */ + Finder f = new Finder(av); + f.findAll("abe", false, false, true); // true = ignore hidden + SearchResultsI searchResults = f.getSearchResults(); + + /* + * match of seq1 ABE made up of AB and E + * note only one match is counted + */ + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + SearchResultMatchI match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 8); // A + assertEquals(match.getEnd(), 9); // B + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + + f = new Finder(av); + f.findNext("a.E", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 8); // A + assertEquals(match.getEnd(), 9); // B + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + + f.findNext("a.E", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 2); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 1); // a + assertEquals(match.getEnd(), 1); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); // D + assertEquals(match.getEnd(), 5); // e + + /* + * find all matching across two hidden column regions + * note one 'match' is returned as three contiguous matches + */ + f.findAll("BEG", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 3); + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 9); // B + assertEquals(match.getEnd(), 9); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 12); // E + assertEquals(match.getEnd(), 12); + match = searchResults.getResults().get(2); + assertSame(match.getSequence(), al.getSequenceAt(0)); + assertEquals(match.getStart(), 14); // G + assertEquals(match.getEnd(), 14); + + /* + * now select columns 0-9 and search for A.*H + * this should match in the second sequence (split as 3 matches) + * but not the first (as H is outside the selection) + */ + SequenceGroup selection = new SequenceGroup(); + selection.setStartRes(0); + selection.setEndRes(9); + al.getSequences().forEach(seq -> selection.addSequence(seq, false)); + av.setSelectionGroup(selection); + f.findAll("A.*H", false, false, true); + searchResults = f.getSearchResults(); + assertEquals(searchResults.getCount(), 1); + assertEquals(searchResults.getResults().size(), 3); + // match made of contiguous matches A, DE, H + match = searchResults.getResults().get(0); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 1); // A + assertEquals(match.getEnd(), 1); + match = searchResults.getResults().get(1); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 4); // D + assertEquals(match.getEnd(), 5); // E + match = searchResults.getResults().get(2); + assertSame(match.getSequence(), al.getSequenceAt(1)); + assertEquals(match.getStart(), 7); // H (there is no G) + assertEquals(match.getEnd(), 7); + } } diff --git a/test/jalview/bin/CacheTest.java b/test/jalview/bin/CacheTest.java index e762dd5..9edfd3b 100644 --- a/test/jalview/bin/CacheTest.java +++ b/test/jalview/bin/CacheTest.java @@ -20,9 +20,10 @@ */ package jalview.bin; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; import static org.testng.AssertJUnit.assertEquals; - -import jalview.gui.JvOptionPane; +import static org.testng.AssertJUnit.assertNotNull; import java.text.SimpleDateFormat; import java.util.Date; @@ -32,6 +33,8 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jalview.gui.JvOptionPane; + public class CacheTest { @@ -70,9 +73,28 @@ public class CacheTest assertEquals(formattedDate, formattedDate2); // currently using Locale.UK to format dates: - assertEquals( - formattedDate2, - SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, - SimpleDateFormat.MEDIUM, Locale.UK).format(now)); + assertEquals(formattedDate2, + SimpleDateFormat + .getDateTimeInstance(SimpleDateFormat.MEDIUM, + SimpleDateFormat.MEDIUM, Locale.UK) + .format(now)); + } + + @Test(groups = "Functional") + public void testVersionChecker() + { + Cache.loadProperties("test/jalview/bin/testProps.jvprops"); + try + { + // 10s sleep to allow VersionChecker thread to run + Thread.sleep(10000); + } catch (Exception e) + { + e.printStackTrace(); + } + String latestVersion = Cache.getProperty("LATEST_VERSION"); + assertNotNull(latestVersion); + assertNotEquals(latestVersion, "test"); + assertTrue(latestVersion.startsWith("2.")); } } diff --git a/test/jalview/bin/CommandLineOperations.java b/test/jalview/bin/CommandLineOperations.java index 59fc79d..a5067be 100644 --- a/test/jalview/bin/CommandLineOperations.java +++ b/test/jalview/bin/CommandLineOperations.java @@ -20,10 +20,17 @@ */ 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; @@ -279,7 +286,7 @@ public class CommandLineOperations 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); @@ -291,6 +298,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(); @@ -343,51 +369,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 }, + 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 }, + 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 }, + 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 }, + 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() }, }; } } diff --git a/test/jalview/bin/testProps.jvprops b/test/jalview/bin/testProps.jvprops new file mode 100644 index 0000000..a1ab82a --- /dev/null +++ b/test/jalview/bin/testProps.jvprops @@ -0,0 +1,2 @@ +VERSION_CHECK=true +LATEST_VERSION=test diff --git a/test/jalview/controller/AlignViewControllerTest.java b/test/jalview/controller/AlignViewControllerTest.java index 7990d21..fde4da1 100644 --- a/test/jalview/controller/AlignViewControllerTest.java +++ b/test/jalview/controller/AlignViewControllerTest.java @@ -225,7 +225,7 @@ public class AlignViewControllerTest * test Match/Find works first */ FinderI f = new Finder(af.getViewport()); - f.findAll("M+", true, false); + f.findAll("M+", true, false, false); assertEquals( "Finder found different set of results to manually created SearchResults", sr, f.getSearchResults()); diff --git a/test/jalview/datamodel/SearchResultsTest.java b/test/jalview/datamodel/SearchResultsTest.java index 349b5d1..838b259 100644 --- a/test/jalview/datamodel/SearchResultsTest.java +++ b/test/jalview/datamodel/SearchResultsTest.java @@ -25,14 +25,14 @@ import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; -import jalview.gui.JvOptionPane; - import java.util.BitSet; import org.junit.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jalview.gui.JvOptionPane; + public class SearchResultsTest { @@ -297,10 +297,74 @@ public class SearchResultsTest SequenceI seq1 = new Sequence("", "abcdefghijklm"); SearchResultsI sr = new SearchResults(); sr.addResult(seq1, 3, 5); - assertEquals(1, sr.getSize()); + assertEquals(1, sr.getCount()); sr.addResult(seq1, 3, 5); - assertEquals(1, sr.getSize()); + assertEquals(1, sr.getCount()); sr.addResult(seq1, 3, 6); - assertEquals(2, sr.getSize()); + assertEquals(2, sr.getCount()); + } + + /** + * Test for method that checks if search results matches a sequence region + */ + @Test(groups = { "Functional" }) + public void testInvolvesSequence() + { + SequenceI dataset = new Sequence("genome", "ATGGCCCTTTAAGCAACATTT"); + // first 'exon': + SequenceI cds1 = new Sequence("cds1/1-12", "ATGGCCCTTTAA"); + cds1.setDatasetSequence(dataset); + // overlapping second 'exon': + SequenceI cds2 = new Sequence("cds2/7-18", "CTTTAAGCAACA"); + cds2.setDatasetSequence(dataset); + // unrelated sequence + SequenceI cds3 = new Sequence("cds3", "ATGGCCCTTTAAGCAACA"); + + SearchResults sr = new SearchResults(); + assertFalse(sr.involvesSequence(cds1)); + + /* + * cds1 and cds2 share the same dataset sequence, but + * only cds1 overlaps match 4:6 (fixes bug JAL-3613) + */ + sr.addResult(dataset, 4, 6); + assertTrue(sr.involvesSequence(cds1)); + assertFalse(sr.involvesSequence(cds2)); + assertFalse(sr.involvesSequence(cds3)); + + /* + * search results overlap cds2 only + */ + sr = new SearchResults(); + sr.addResult(dataset, 18, 18); + assertFalse(sr.involvesSequence(cds1)); + assertTrue(sr.involvesSequence(cds2)); + + /* + * add a search result overlapping cds1 + */ + sr.addResult(dataset, 1, 1); + assertTrue(sr.involvesSequence(cds1)); + assertTrue(sr.involvesSequence(cds2)); + + /* + * single search result overlapping both + */ + sr = new SearchResults(); + sr.addResult(dataset, 10, 12); + assertTrue(sr.involvesSequence(cds1)); + assertTrue(sr.involvesSequence(cds2)); + + /* + * search results matching aligned sequence + */ + sr = new SearchResults(); + sr.addResult(cds1, 10, 12); + assertTrue(sr.involvesSequence(cds1)); + assertFalse(sr.involvesSequence(cds2)); + sr.addResult(cds2, 1, 3); // no start-end overlap + assertFalse(sr.involvesSequence(cds2)); + sr.addResult(cds2, 7, 9); // start-end overlap + assertTrue(sr.involvesSequence(cds2)); } } diff --git a/test/jalview/datamodel/features/SequenceFeaturesTest.java b/test/jalview/datamodel/features/SequenceFeaturesTest.java index 4198a37..56512cd 100644 --- a/test/jalview/datamodel/features/SequenceFeaturesTest.java +++ b/test/jalview/datamodel/features/SequenceFeaturesTest.java @@ -5,8 +5,6 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; -import jalview.datamodel.SequenceFeature; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -15,6 +13,7 @@ import java.util.Set; import org.testng.annotations.Test; +import jalview.datamodel.SequenceFeature; import junit.extensions.PA; public class SequenceFeaturesTest @@ -1052,7 +1051,8 @@ public class SequenceFeaturesTest public void testSortFeatures() { List sfs = new ArrayList<>(); - SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, 80, + SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, + 60, Float.NaN, null); sfs.add(sf1); SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50, @@ -1061,18 +1061,34 @@ public class SequenceFeaturesTest SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60, Float.NaN, null); sfs.add(sf3); + SequenceFeature sf4 = new SequenceFeature("Xfam", "desc", 30, + 80, + Float.NaN, null); + sfs.add(sf4); + SequenceFeature sf5 = new SequenceFeature("Xfam", "desc", 30, + 90, + Float.NaN, null); + sfs.add(sf5); - // sort by end position descending + /* + * sort by end position descending, order unchanged if matched + */ SequenceFeatures.sortFeatures(sfs, false); - assertSame(sfs.get(0), sf1); - assertSame(sfs.get(1), sf3); - assertSame(sfs.get(2), sf2); + assertSame(sfs.get(0), sf5); // end 90 + assertSame(sfs.get(1), sf4); // end 80 + assertSame(sfs.get(2), sf1); // end 60, start 50 + assertSame(sfs.get(3), sf3); // end 60, start 30 + assertSame(sfs.get(4), sf2); // end 50 - // sort by start position ascending + /* + * resort {5, 4, 1, 3, 2} by start position ascending, end descending + */ SequenceFeatures.sortFeatures(sfs, true); - assertSame(sfs.get(0), sf1); - assertSame(sfs.get(1), sf2); - assertSame(sfs.get(2), sf3); + assertSame(sfs.get(0), sf5); // start 30, end 90 + assertSame(sfs.get(1), sf4); // start 30, end 80 + assertSame(sfs.get(2), sf1); // start 30, end 60 + assertSame(sfs.get(3), sf2); // start 40 + assertSame(sfs.get(4), sf3); // start 50 } @Test(groups = "Functional") diff --git a/test/jalview/ext/ensembl/EnsemblGeneTest.java b/test/jalview/ext/ensembl/EnsemblGeneTest.java index 8b1e840..ef12ed4 100644 --- a/test/jalview/ext/ensembl/EnsemblGeneTest.java +++ b/test/jalview/ext/ensembl/EnsemblGeneTest.java @@ -271,18 +271,24 @@ public class EnsemblGeneTest /** * Check behaviour of feature colour scheme for EnsemblGene sequences. - * Currently coded to display exon and sequence_variant (or sub-types) only, - * with sequence_variant in red above exon coloured by label. + * Currently coded to hide all except exon and sequence_variant (or sub-types) + * only, with sequence_variant in red above exon coloured by label. */ @Test(groups = "Functional") public void testGetFeatureColourScheme() { FeatureSettingsModelI fc = new EnsemblGene().getFeatureColourScheme(); - assertTrue(fc.isFeatureDisplayed("exon")); - assertTrue(fc.isFeatureDisplayed("coding_exon")); // subtype of exon - assertTrue(fc.isFeatureDisplayed("sequence_variant")); - assertTrue(fc.isFeatureDisplayed("feature_variant")); // subtype - assertFalse(fc.isFeatureDisplayed("transcript")); + assertFalse(fc.isFeatureDisplayed("exon")); + assertFalse(fc.isFeatureHidden("exon")); + assertFalse(fc.isFeatureDisplayed("coding_exon")); // subtype of exon + assertFalse(fc.isFeatureHidden("coding_exon")); // subtype of exon + assertFalse(fc.isFeatureDisplayed("sequence_variant")); + assertFalse(fc.isFeatureHidden("sequence_variant")); + assertFalse(fc.isFeatureDisplayed("feature_variant")); // subtype + assertFalse(fc.isFeatureHidden("feature_variant")); // subtype + assertTrue(fc.isFeatureHidden("transcript")); + assertTrue(fc.isFeatureHidden("CDS")); + assertEquals(Color.RED, fc.getFeatureColour("sequence_variant") .getColour()); assertEquals(Color.RED, fc.getFeatureColour("feature_variant") diff --git a/test/jalview/gui/FreeUpMemoryTest.java b/test/jalview/gui/FreeUpMemoryTest.java index 24697c0..3a5a9e4 100644 --- a/test/jalview/gui/FreeUpMemoryTest.java +++ b/test/jalview/gui/FreeUpMemoryTest.java @@ -1,17 +1,8 @@ package jalview.gui; -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; -import jalview.analysis.AlignmentGenerator; -import jalview.bin.Cache; -import jalview.bin.Jalview; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.SequenceGroup; -import jalview.io.DataSourceType; -import jalview.io.FileLoader; - import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; @@ -20,20 +11,96 @@ import java.io.PrintStream; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jalview.analysis.AlignmentGenerator; +import jalview.bin.Cache; +import jalview.bin.Jalview; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; import junit.extensions.PA; +/** + * Provides a simple test that memory is released when all windows are closed. + *
        + *
      • generates a reasonably large alignment and loads it
      • + *
      • performs various operations on the alignment
      • + *
      • closes all windows
      • + *
      • requests garbage collection
      • + *
      • asserts that the remaining memory footprint (heap usage) is 'not large' + *
      • + *
      + * If the test fails, this means that reference(s) to large object(s) have + * failed to be garbage collected. In this case: + *
        + *
      • set a breakpoint just before the test assertion in + * {@code checkUsedMemory}
      • + *
      • if the test fails intermittently, make this breakpoint conditional on + * {@code usedMemory > expectedMax}
      • + *
      • run the test to this point (and check that it is about to fail i.e. + * {@code usedMemory > expectedMax})
      • + *
      • use visualvm to obtain a heap + * dump from the suspended process (and kill the test or let it fail)
      • + *
      • inspect the heap dump using visualvm for large objects and their + * referers
      • + *
      • Tips:
      • + *
          + *
        • Perform GC from the Monitor view in visualvm before requesting the heap + * dump - test failure might be simply a delay to GC
        • + *
        • View 'Objects' and filter classes to {@code jalview}. Sort columns by + * Count, or Size, and look for anything suspicious. For example, if the object + * count for {@code Sequence} is non-zero (it shouldn't be), pick any instance, + * and follow the chain of {@code references} to find which class(es) still hold + * references to sequence objects
        • + *
        • If this chain is impracticably long, re-run the test with a smaller + * alignment (set width=100, height=10 in {@code generateAlignment()}), to + * capture a heap which is qualitatively the same, but much smaller, so easier + * to analyse; note this requires an unconditional breakpoint
        • + *
        + *
      + *

      + *

      Fixing memory leaks

      + *

      + * Experience shows that often a reference is retained (directly or indirectly) + * by a Swing (or related) component (for example a {@code MouseListener} or + * {@code ActionListener}). There are two possible approaches to fixing: + *

        + *
      • Purist: ensure that all listeners and similar objects are removed when no + * longer needed. May be difficult, to achieve and to maintain as code + * changes.
      • + *
      • Pragmatic: null references to potentially large objects from Jalview + * application classes when no longer needed, typically when a panel is closed. + * This ensures that even if the JVM keeps a reference to a panel or viewport, + * it does not retain a large heap footprint. This is the approach taken in, for + * example, {@code AlignmentPanel.closePanel()} and + * {@code AnnotationPanel.dispose()}.
      • + *
      • Adjust code if necessary; for example an {@code ActionListener} should + * act on {@code av.getAlignment()} and not directly on {@code alignment}, as + * the latter pattern could leave persistent references to the alignment
      • + *
      + * Add code to 'null unused large object references' until the test passes. For + * a final sanity check, capture the heap dump for a passing test, and satisfy + * yourself that only 'small' or 'harmless' {@code jalview} object instances + * (such as enums or singletons) are left in the heap. + */ public class FreeUpMemoryTest { private static final int ONE_MB = 1000 * 1000; + /* + * 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 */ @BeforeClass(alwaysRun = true) public void setUp() { - Jalview.main(new String[] { "-nonews", "-props", - "test/jalview/testProps.jvprops" }); + Jalview.main( + 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); @@ -42,98 +109,79 @@ public class FreeUpMemoryTest Cache.applicationProperties.setProperty("SHOW_IDENTITY", True); } - /** - * A simple test that memory is released when all windows are closed. - *
        - *
      • generates a reasonably large alignment and loads it
      • - *
      • performs various operations on the alignment
      • - *
      • closes all windows
      • - *
      • requests garbage collection
      • - *
      • asserts that the remaining memory footprint (heap usage) is 'not large' - *
      • - *
      - * 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 = 35L; - 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); - checkUsedMemory(expectedMin); + checkUsedMemory(MAX_RESIDUAL_HEAP); } - private static long getUsedMemory() + /** + * Returns the current total used memory (available memory - free memory), + * rounded down to the nearest MB + * + * @return + */ + private static int getUsedMemory() { - long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB; - long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB; + long availableMemory = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); long usedMemory = availableMemory - freeMemory; - return usedMemory; + + return (int) (usedMemory / ONE_MB); } + /** * Requests garbage collection and then checks whether remaining memory in use * is less than the expected value (in Megabytes) * * @param expectedMax */ - protected void checkUsedMemory(long expectedMax) + protected void checkUsedMemory(int expectedMax) { /* - * request garbage collection and wait for it to run; + * request garbage collection and wait for it to run (up to 3 times); * NB there is no guarantee when, or whether, it will do so - * wait time depends on JRE/processor, generous allowance here */ - System.gc(); - waitFor(1500); - - /* - * a second gc() call should not be necessary - but it is! - * the test passes with it, and fails without it - */ - System.gc(); - waitFor(1500); - - /* - * check used memory is 'reasonably low' - */ - long usedMemory = getUsedMemory(); - /* - * sanity check - fails if any frame was added after - * closeAll_actionPerformed - */ - assertEquals(Desktop.instance.getAllFrames().length, 0); + long usedMemory = 0L; + Long minUsedMemory = null; + int gcCount = 0; + while (gcCount < 3) + { + gcCount++; + System.gc(); + waitFor(1500); + usedMemory = getUsedMemory(); + if (minUsedMemory == null || usedMemory < minUsedMemory) + { + minUsedMemory = usedMemory; + } + if (usedMemory < expectedMax) + { + break; + } + } /* - * if this assertion fails - * - set a breakpoint here - * - run jvisualvm to inspect a heap dump of Jalview - * - identify large objects in the heap and their referers + * if this assertion fails (reproducibly!) + * - set a breakpoint here, conditional on (usedMemory > expectedMax) + * - run VisualVM to inspect the heap usage, and run GC from VisualVM to check + * it is not simply delayed garbage collection causing the test failure + * - take a heap dump and identify large objects in the heap and their referers * - fix code as necessary to null the references on close */ - System.out.println("Used memory after gc = " + usedMemory + "MB"); - assertTrue(usedMemory < expectedMax, String.format( + System.out.println("(Minimum) Used memory after " + gcCount + + " call(s) to gc() = " + minUsedMemory + "MB (should be <=" + + expectedMax + ")"); + assertTrue(usedMemory <= expectedMax, String.format( "Used memory %d should be less than %d (Recommend running test manually to verify)", - usedMemory, - expectedMax)); + usedMemory, expectedMax)); } /** @@ -193,7 +241,7 @@ public class FreeUpMemoryTest * wait until Tree and PCA have been computed */ while (af.viewport.getCurrentTree() == null - && dialog.getPcaPanel().isWorking()) + || dialog.getPcaPanel().isWorking()) { waitFor(10); } @@ -237,6 +285,7 @@ public class FreeUpMemoryTest int width = 100000; int height = 100; ag.generate(width, height, 0, 10, 15); + ps.close(); return f; } } diff --git a/test/jalview/gui/SeqCanvasTest.java b/test/jalview/gui/SeqCanvasTest.java index 73aeb79..213a1c9 100644 --- a/test/jalview/gui/SeqCanvasTest.java +++ b/test/jalview/gui/SeqCanvasTest.java @@ -21,8 +21,14 @@ package jalview.gui; 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.AlignmentI; +import jalview.datamodel.SearchResults; +import jalview.datamodel.SearchResultsI; import jalview.io.DataSourceType; import jalview.io.FileLoader; @@ -338,4 +344,27 @@ public class SeqCanvasTest "wrappedRepeatHeightPx"); assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())); } + + @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( + av.getAlignment().getSequenceAt(1).getDatasetSequence(), 50, + 80); + af.alignPanel.highlightSearchResults(highlight); + af.avc.markHighlightedColumns(false, false, false); + assertNotNull(av.getSearchResults(), + "No highlight was created on alignment"); + assertFalse(av.getColumnSelection().isEmpty(), + "No selection was created from highlight"); + af.deselectAllSequenceMenuItem_actionPerformed(null); + assertTrue(av.getColumnSelection().isEmpty(), + "No Selection should be present after deselecting all."); + assertNull(av.getSearchResults(), + "No higlighted search results should be present after deselecting all."); + } } diff --git a/test/jalview/io/BackupFilesTest.java b/test/jalview/io/BackupFilesTest.java index 64cf902..1386bfe 100644 --- a/test/jalview/io/BackupFilesTest.java +++ b/test/jalview/io/BackupFilesTest.java @@ -209,6 +209,7 @@ public class BackupFilesTest boolean noMax) { Cache.loadProperties("test/jalview/io/testProps.jvprops"); + Cache.initLogger(); BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits, reverse, noMax, rollMax, false); diff --git a/test/jalview/io/EmblFlatFileTest.java b/test/jalview/io/EmblFlatFileTest.java new file mode 100644 index 0000000..5d8ef21 --- /dev/null +++ b/test/jalview/io/EmblFlatFileTest.java @@ -0,0 +1,347 @@ +package jalview.io; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertSame; +import static org.testng.AssertJUnit.fail; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import jalview.bin.Cache; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.Mapping; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.datamodel.features.SequenceFeatures; +import jalview.util.MapList; + +public class EmblFlatFileTest +{ + @BeforeClass(alwaysRun = true) + public void setUp() + { + Cache.initLogger(); + } + + /** + * A fairly tough test, using J03321 (circular DNA), which has 8 CDS features, + * one of them reverse strand + * + * @throws MalformedURLException + * @throws IOException + */ + @Test(groups = "Functional") + public void testParse() throws MalformedURLException, IOException + { + File dataFile = new File("test/jalview/io/J03321.embl.txt"); + FileParse fp = new FileParse(dataFile.getAbsolutePath(), DataSourceType.FILE); + EmblFlatFile parser = new EmblFlatFile(fp, "EmblTest"); + parser.parse(); + List seqs = parser.getSeqs(); + + assertEquals(seqs.size(), 1); + SequenceI seq = seqs.get(0); + assertEquals(seq.getName(), "EmblTest|J03321"); + assertEquals(seq.getLength(), 7502); + assertEquals(seq.getDescription(), + "Chlamydia trachomatis plasmid pCHL1, complete sequence"); + + /* + * should be 9 CDS features (one is a 'join' of two exons) + */ + Set featureTypes = seq.getFeatures().getFeatureTypes(); + assertEquals(featureTypes.size(), 1); + assertTrue(featureTypes.contains("CDS")); + + /* + * inspect some features (sorted just for convenience of test assertions) + */ + List features = seq.getFeatures() + .getAllFeatures("CDS"); + SequenceFeatures.sortFeatures(features, true); + assertEquals(features.size(), 9); + + SequenceFeature sf = features.get(0); + assertEquals(sf.getBegin(), 1); + assertEquals(sf.getEnd(), 437); + assertEquals(sf.getDescription(), + "Exon 2 for protein EMBLCDS:AAA91567.1"); + assertEquals(sf.getFeatureGroup(), "EmblTest"); + assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)"); + assertEquals(sf.getPhase(), "0"); + assertEquals(sf.getStrand(), 1); + assertEquals(sf.getValue("note"), "pGP7-D"); + // this is the second exon of circular CDS! + assertEquals(sf.getValue("exon number"), 2); + assertEquals(sf.getValue("product"), "hypothetical protein"); + assertEquals(sf.getValue("transl_table"), "11"); + + sf = features.get(1); + assertEquals(sf.getBegin(), 488); + assertEquals(sf.getEnd(), 1480); + assertEquals(sf.getDescription(), + "Exon 1 for protein EMBLCDS:AAA91568.1"); + assertEquals(sf.getFeatureGroup(), "EmblTest"); + assertEquals(sf.getEnaLocation(), "complement(488..1480)"); + assertEquals(sf.getPhase(), "0"); + assertEquals(sf.getStrand(), -1); // reverse strand! + assertEquals(sf.getValue("note"), "pGP8-D"); + assertEquals(sf.getValue("exon number"), 1); + assertEquals(sf.getValue("product"), "hypothetical protein"); + + sf = features.get(7); + assertEquals(sf.getBegin(), 6045); + assertEquals(sf.getEnd(), 6788); + assertEquals(sf.getDescription(), + "Exon 1 for protein EMBLCDS:AAA91574.1"); + assertEquals(sf.getFeatureGroup(), "EmblTest"); + assertEquals(sf.getEnaLocation(), "6045..6788"); + assertEquals(sf.getPhase(), "0"); + assertEquals(sf.getStrand(), 1); + assertEquals(sf.getValue("note"), "pGP6-D (gtg start codon)"); + assertEquals(sf.getValue("exon number"), 1); + assertEquals(sf.getValue("product"), "hypothetical protein"); + + /* + * CDS at 7022-7502 is the first exon of the circular CDS + */ + sf = features.get(8); + assertEquals(sf.getBegin(), 7022); + assertEquals(sf.getEnd(), 7502); + assertEquals(sf.getDescription(), + "Exon 1 for protein EMBLCDS:AAA91567.1"); + assertEquals(sf.getFeatureGroup(), "EmblTest"); + assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)"); + assertEquals(sf.getPhase(), "0"); + assertEquals(sf.getStrand(), 1); + assertEquals(sf.getValue("note"), "pGP7-D"); + assertEquals(sf.getValue("exon number"), 1); + assertEquals(sf.getValue("product"), "hypothetical protein"); + + /* + * Verify DBRefs, whether declared in the file or added by Jalview. + * There are 4 'direct' (DR) dbrefs, and numerous CDS /db_xref entries + * (some e.g. INTERPRO are duplicates). Jalview adds a dbref to 'self'. + * Sample a few here. Note DBRefEntry constructor capitalises source. + */ + List dbrefs = Arrays.asList(seq.getDBRefs()); + + assertEquals(dbrefs.size(), 32); + // xref to 'self': + DBRefEntry selfRef = new DBRefEntry("EMBLTEST", "1", "J03321"); + int[] range = new int[] { 1, seq.getLength() }; + selfRef.setMap(new Mapping(null, range, range, 1, 1)); + assertTrue(dbrefs.contains(selfRef)); + + // 1st DR line; note trailing period is removed + assertTrue(dbrefs.contains(new DBRefEntry("MD5", "0", + "d4c4942a634e3df4995fd5ac75c26a61"))); + // the 4th DR line: + assertTrue( + dbrefs.contains(new DBRefEntry("EUROPEPMC", "0", "PMC87941"))); + // from the first CDS feature + assertTrue(dbrefs.contains(new DBRefEntry("GOA", "0", "P0CE19"))); + // from the last CDS feature + assertTrue( + dbrefs.contains(new DBRefEntry("INTERPRO", "0", "IPR005350"))); + + /* + * verify mappings to, and sequences for, UNIPROT proteins + */ + int uniprotCount = 0; + List ranges; + for (DBRefEntry dbref : dbrefs) + { + if ("UNIPROT".equals(dbref.getSource())) + { + uniprotCount++; + Mapping mapping = dbref.getMap(); + assertNotNull(mapping); + MapList map = mapping.getMap(); + String mappedToName = mapping.getTo().getName(); + if ("UNIPROT|P0CE16".equals(mappedToName)) + { + assertEquals((ranges = map.getFromRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1579); + assertEquals(ranges.get(0)[1], 2934); + assertEquals((ranges = map.getToRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1); + assertEquals(ranges.get(0)[1], 451); + // CDS /product carries over as protein product description + assertEquals(mapping.getTo().getDescription(), + "hypothetical protein"); + } + else if ("UNIPROT|P0CE17".equals(mappedToName)) + { + assertEquals((ranges = map.getFromRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 2928); + assertEquals(ranges.get(0)[1], 3992); + assertEquals((ranges = map.getToRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1); + assertEquals(ranges.get(0)[1], 354); + } + else if ("UNIPROT|P0CE18".equals(mappedToName)) + { + assertEquals((ranges = map.getFromRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 4054); + assertEquals(ranges.get(0)[1], 4848); + assertEquals((ranges = map.getToRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1); + assertEquals(ranges.get(0)[1], 264); + } + else if ("UNIPROT|P0CE19".equals(mappedToName)) + { + // join(7022..7502,1..437) + assertEquals((ranges = map.getFromRanges()).size(), 2); + assertEquals(ranges.get(0)[0], 7022); + assertEquals(ranges.get(0)[1], 7502); + assertEquals(ranges.get(1)[0], 1); + assertEquals(ranges.get(1)[1], 437); + assertEquals((ranges = map.getToRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1); + assertEquals(ranges.get(0)[1], 305); + } + else if ("UNIPROT|P0CE20".equals(mappedToName)) + { + // complement(488..1480) + assertEquals((ranges = map.getFromRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1480); + assertEquals(ranges.get(0)[1], 488); + assertEquals((ranges = map.getToRanges()).size(), 1); + assertEquals(ranges.get(0)[0], 1); + assertEquals(ranges.get(0)[1], 330); + } + else if (!"UNIPROT|P0CE23".equals(mappedToName) + && !"UNIPROT|P10559".equals(mappedToName) + && !"UNIPROT|P10560".equals(mappedToName)) + { + fail("Unexpected UNIPROT dbref to " + mappedToName); + } + } + } + assertEquals(uniprotCount, 8); + } + + @Test(groups = "Functional") + public void testParse_codonStartNot1() + { + // TODO verify CDS-to-protein mapping for CDS with /codon_start=2 + // example: https://www.ebi.ac.uk/ena/browser/api/embl/EU498516 + } + + /** + * Test for the case that the EMBL CDS has no UNIPROT xref. In this case + * Jalview should synthesize an xref to EMBLCDSPROTEIN in the hope this will + * allow Get Cross-References. + * + * @throws IOException + */ + @Test(groups = "Functional") + public void testParse_noUniprotXref() throws IOException + { + // MN908947 cut down to 40BP, one CDS, length 5 peptide for test purposes + // plus an additional (invented) test case: + // - multi-line /product qualifier including escaped quotes + String data = "ID MN908947; SV 3; linear; genomic RNA; STD; VRL; 20 BP.\n" + + "DE Severe acute respiratory syndrome coronavirus 2 isolate Wuhan-Hu-1,\n" + + "FT CDS 3..17\n" + + "FT /protein_id=\"QHD43415.1\"\n" + + "FT /product=\"orf1ab polyprotein\n" + + "FT \"\"foobar\"\" \"\n" + + "FT /translation=\"MRKLD\n" + + "SQ Sequence 7496 BP; 2450 A; 1290 C; 1434 G; 2322 T; 0 other;\n" + + " ggatGcgtaa gttagacgaa attttgtctt tgcgcacaga 40\n"; + FileParse fp = new FileParse(data, DataSourceType.PASTE); + EmblFlatFile parser = new EmblFlatFile(fp, "EmblTest"); + parser.parse(); + List seqs = parser.getSeqs(); + assertEquals(seqs.size(), 1); + SequenceI seq = seqs.get(0); + List dbrefs = Arrays.asList(seq.getDBRefs()); + + /* + * dna should have dbref to itself, and to inferred EMBLCDSPROTEIN:QHD43415.1 + */ + assertEquals(dbrefs.size(), 2); + + // dbref to self + DBRefEntry dbref = dbrefs.get(0); + assertEquals(dbref.getSource(), "EMBLTEST"); + assertEquals(dbref.getAccessionId(), "MN908947"); + Mapping mapping = dbref.getMap(); + assertNull(mapping.getTo()); + MapList map = mapping.getMap(); + assertEquals(map.getFromLowest(), 1); + assertEquals(map.getFromHighest(), 40); + assertEquals(map.getToLowest(), 1); + assertEquals(map.getToHighest(), 40); + assertEquals(map.getFromRatio(), 1); + assertEquals(map.getToRatio(), 1); + + // dbref to inferred EMBLCDSPROTEIN: + dbref = dbrefs.get(1); + assertEquals(dbref.getSource(), "EMBLCDSPROTEIN"); + assertEquals(dbref.getAccessionId(), "QHD43415.1"); + mapping = dbref.getMap(); + SequenceI mapTo = mapping.getTo(); + assertEquals(mapTo.getName(), "QHD43415.1"); + // the /product qualifier transfers to protein product description + assertEquals(mapTo.getDescription(), "orf1ab polyprotein \"foobar\""); + assertEquals(mapTo.getSequenceAsString(), "MRKLD"); + map = mapping.getMap(); + assertEquals(map.getFromLowest(), 3); + assertEquals(map.getFromHighest(), 17); + assertEquals(map.getToLowest(), 1); + assertEquals(map.getToHighest(), 5); + assertEquals(map.getFromRatio(), 3); + assertEquals(map.getToRatio(), 1); + } + + @Test(groups = "Functional") + public void testAdjustForProteinLength() + { + int[] exons = new int[] { 11, 15, 21, 25, 31, 38 }; // 18 bp + + // exact length match: + assertSame(exons, EmblFlatFile.adjustForProteinLength(6, exons)); + + // match if we assume exons include stop codon not in protein: + assertSame(exons, EmblFlatFile.adjustForProteinLength(5, exons)); + + // truncate last exon by 6bp + int[] truncated = EmblFlatFile.adjustForProteinLength(4, exons); + assertEquals("[11, 15, 21, 25, 31, 32]", Arrays.toString(truncated)); + + // remove last exon and truncate preceding by 1bp (so 3bp in total) + truncated = EmblFlatFile.adjustForProteinLength(3, exons); + assertEquals("[11, 15, 21, 24]", Arrays.toString(truncated)); + + // exact removal of exon case: + exons = new int[] { 11, 15, 21, 27, 33, 38 }; // 18 bp + truncated = EmblFlatFile.adjustForProteinLength(4, exons); + assertEquals("[11, 15, 21, 27]", Arrays.toString(truncated)); + + // what if exons are too short for protein? + truncated = EmblFlatFile.adjustForProteinLength(7, exons); + assertSame(exons, truncated); + } + + @Test(groups = "Functional") + public void testRemoveQuotes() + { + assertNull(EmblFlatFile.removeQuotes(null)); + assertEquals(EmblFlatFile.removeQuotes("No quotes here"), "No quotes here"); + assertEquals(EmblFlatFile.removeQuotes("\"Enclosing quotes\""), "Enclosing quotes"); + assertEquals(EmblFlatFile.removeQuotes("\"Escaped \"\"quotes\"\" example\""), "Escaped \"quotes\" example"); + } +} diff --git a/test/jalview/io/FileIOTester.java b/test/jalview/io/FileIOTester.java index f6480a6..04d01b0 100644 --- a/test/jalview/io/FileIOTester.java +++ b/test/jalview/io/FileIOTester.java @@ -20,16 +20,23 @@ */ package jalview.io; -import jalview.gui.JvOptionPane; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import org.testng.AssertJUnit; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jalview.bin.Cache; +import jalview.gui.JvOptionPane; + /** * @author jimp * @@ -50,6 +57,7 @@ public class FileIOTester @BeforeClass(alwaysRun = true) public static void setUpBeforeClass() throws Exception { + Cache.initLogger(); } /** @@ -88,8 +96,9 @@ public class FileIOTester public void testStarsInFasta1() throws IOException { String uri; - FileParse fp = new FileParse(uri = STARS_FA_FILE1.getAbsoluteFile() - .toString(), DataSourceType.FILE); + FileParse fp = new FileParse( + uri = STARS_FA_FILE1.getAbsoluteFile().toString(), + DataSourceType.FILE); assertValidFormat(FileFormat.Fasta, uri, fp); } @@ -97,8 +106,9 @@ public class FileIOTester public void testStarsInFasta2() throws IOException { String uri; - FileParse fp = new FileParse(uri = STARS_FA_FILE2.getAbsoluteFile() - .toString(), DataSourceType.FILE); + FileParse fp = new FileParse( + uri = STARS_FA_FILE2.getAbsoluteFile().toString(), + DataSourceType.FILE); assertValidFormat(FileFormat.Fasta, uri, fp); } @@ -106,8 +116,9 @@ public class FileIOTester public void testGzipIo() throws IOException { String uri; - FileParse fp = new FileParse(uri = ALIGN_FILE.getAbsoluteFile().toURI() - .toString(), DataSourceType.URL); + FileParse fp = new FileParse( + uri = ALIGN_FILE.getAbsoluteFile().toURI().toString(), + DataSourceType.URL); assertValidFormat(FileFormat.Fasta, uri, fp); } @@ -115,17 +126,36 @@ public class FileIOTester public void testGziplocalFileIO() throws IOException { String filepath; - FileParse fp = new FileParse(filepath = ALIGN_FILE.getAbsoluteFile() - .toString(), DataSourceType.FILE); + FileParse fp = new FileParse( + filepath = ALIGN_FILE.getAbsoluteFile().toString(), + DataSourceType.FILE); assertValidFormat(FileFormat.Fasta, filepath, fp); } @Test(groups = { "Functional" }) + public void testIsGzipInputStream() throws IOException + { + InputStream is = new FileInputStream(ALIGN_FILE); + + /* + * first try fails - FileInputStream does not support mark/reset + */ + assertFalse(FileParse.isGzipStream(is)); + + /* + * wrap in a BufferedInputStream and try again + */ + is = new BufferedInputStream(is, 16); + assertTrue(FileParse.isGzipStream(is)); + } + + @Test(groups = { "Functional" }) public void testNonGzipURLIO() throws IOException { String uri; - FileParse fp = new FileParse(uri = NOTGZALIGN_FILE.getAbsoluteFile() - .toURI().toString(), DataSourceType.URL); + FileParse fp = new FileParse( + uri = NOTGZALIGN_FILE.getAbsoluteFile().toURI().toString(), + DataSourceType.URL); assertValidFormat(FileFormat.Fasta, uri, fp); } @@ -133,8 +163,9 @@ public class FileIOTester public void testNonGziplocalFileIO() throws IOException { String filepath; - FileParse fp = new FileParse(filepath = NOTGZALIGN_FILE - .getAbsoluteFile().toString(), DataSourceType.FILE); + FileParse fp = new FileParse( + filepath = NOTGZALIGN_FILE.getAbsoluteFile().toString(), + DataSourceType.FILE); assertValidFormat(FileFormat.Fasta, filepath, fp); } } diff --git a/test/jalview/io/J03321.embl.txt b/test/jalview/io/J03321.embl.txt new file mode 100644 index 0000000..b0bc512 --- /dev/null +++ b/test/jalview/io/J03321.embl.txt @@ -0,0 +1,306 @@ +ID J03321; SV 1; circular; genomic DNA; STD; PRO; 7502 BP. +XX +AC J03321; +XX +DT 27-JUL-1990 (Rel. 24, Created) +DT 10-APR-2020 (Rel. 144, Last updated, Version 9) +XX +DE Chlamydia trachomatis plasmid pCHL1, complete sequence. +XX +KW . +XX +OS Chlamydia trachomatis +OC Bacteria; Chlamydiae; Chlamydiales; Chlamydiaceae; +OC Chlamydia/Chlamydophila group; Chlamydia. +OG Plasmid pCHL1 +XX +RN [1] +RP 1-7502 +RX DOI; 10.1016/0147-619X(90)90034-A. +RX PUBMED; 2194229. +RA Comanducci M., Ricci S., Cevenini R., Ratti G.; +RT "Diversity of the Chlamydia trachomatis common plasmid in biovars with +RT different pathogenicity"; +RL Plasmid 23(2):149-154(1990). +XX +RN [2] +RP 1-7502 +RA Comanducci M., Ricci S., Cevenini R., Ratti G.; +RT ; +RL Submitted (23-JUN-2010) to the INSDC. +RL Sclavo Research Centre, Siena, Italy +XX +DR MD5; d4c4942a634e3df4995fd5ac75c26a61. +DR BioSample; SAMN14225621. +DR EuropePMC; PMC4450983; 26031715. +DR EuropePMC; PMC87941; 11283058. +XX +CC Draft entry and computer-readable sequence kindly submitted by +CC G.Ratti, 28-MAR-1990. +XX +XX ! first CDS location below split across two lines for test purposes ! +FH Key Location/Qualifiers +FH +FT source 1..7502 +FT /organism="Chlamydia trachomatis" +FT /plasmid="pCHL1" +FT /isolate="G0/86" +FT /serotype="D" +FT /mol_type="genomic DNA" +FT /isolation_source="trachoma" +FT /db_xref="taxon:813" +FT CDS join(7022..7502, +FT 1..437) +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP7-D" +FT /db_xref="GOA:P0CE19" +FT /db_xref="InterPro:IPR002104" +FT /db_xref="InterPro:IPR011010" +FT /db_xref="InterPro:IPR013762" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE19" +FT /protein_id="AAA91567.1" +FT /translation="MGSMAFHKSRLFLTFGDASEIWLSTLSYLTRKNYASGINFLVSLE +FT ILDLSETLIKAISLDHSESLFKIKSLDVFNGKVVSEASKQARAACYISFTKFLYRLTKG +FT YIKPAIPLKDFGNTTFFKIRDKIKTESISKQEWTVFFEALRIVNYRDYLIGKLIVQGIR +FT KLDEILSLRTDDLFFASNQISFRIKKRQNKETKILITFPISLMEELQKYTCGRNGRVFV +FT SKIGIPVTTSQVAHNFRLAEFHSAMKIKITPRVLRASALIHLKQIGLKDEEIMRISCLS +FT SRQSVCSYCSGEEVIPLVQTPTIL" +FT CDS complement(488..1480) +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP8-D" +FT /db_xref="GOA:P0CE20" +FT /db_xref="InterPro:IPR002104" +FT /db_xref="InterPro:IPR011010" +FT /db_xref="InterPro:IPR013762" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE20" +FT /protein_id="AAA91568.1" +FT /translation="MGKGILSLQQEMSLEYSEKSYQEVLKIRQESYWKRMKSFSLFEVI +FT MHWTASLNKHTCRSYRGSFLSLEKIGLLSLDMNLQEFSLLNHNLILDAIKKVSSAKTSW +FT TEGTKQVRAASYISLTRFLNRMTQGIVAIAQPSKQENSRTFFKTREIVKTDAMNSLQTA +FT SFLKELKKINARDWLIAQTMLQGGKRSSEVLSLEISQICFQQATISFSQLKNRQTEKRI +FT IITYPQKFMHFLQEYIGQRRGFVFVTRSGKMVGLRQIARTFSQAGLQAAIPFKITPHVL +FT RATAVTEYKRLGCSDSDIMKVTGHATAKMIFAYDKSSREDNASKKMALI" +FT CDS 1579..2934 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP1-D" +FT /db_xref="GOA:P0CE16" +FT /db_xref="InterPro:IPR003593" +FT /db_xref="InterPro:IPR007693" +FT /db_xref="InterPro:IPR007694" +FT /db_xref="InterPro:IPR027417" +FT /db_xref="InterPro:IPR036185" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE16" +FT /protein_id="AAA91569.1" +FT /translation="MKTRSEIENRMQDIEYALLGKALIFEDSTEYILRQLANYEFKCSH +FT HKNIFIVFKHLKDNGLPITVDSAWEELLRRRIKDMDKSYLGLMLHDALSNDKLRSVSHT +FT VFLDDLSVCSAEENLSNFIFRSFNEYNENPLRRSPFLLLERIKGRLDSAIAKTFSIRSA +FT RGRSIYDIFSQSEIGVLARIKKRRVAFSENQNSFFDGFPTGYKDIDDKGVILAKGNFVI +FT IAARPSIGKTALAIDMAINLAVTQQRRVGFLSLEMSAGQIVERIIANLTGISGEKLQRG +FT DLSKEELFRVEEAGETVRESHFYICSDSQYKLNLIANQIRLLRKEDRVDVIFIDYLQLI +FT NSSVGENRQNEIADISRTLRGLASELNIPIVCLSQLSRKVEDRANKVPMLSDLRDSGQI +FT EQDADVILFINRKESSSNCEITVGKNRHGSVFSSVLHFDPKISKFSAIKKVW" +FT CDS 2928..3992 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP2-D" +FT /db_xref="InterPro:IPR040719" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE17" +FT /protein_id="AAA91570.1" +FT /translation="MVNYSNCHFIKSPIHLENQKFGRRPGQSIKISPKLAQNGMVEVIG +FT LDFLSSHYHALAAIQRLLTATNYKGNTKGVVLSRESNSFQFEGWIPRIRFTKTEFLEAY +FT GVKRYKTSRNKYEFSGKEAETALEALYHLGHQPFLIVATRTRWTNGTQIVDRYQTLSPI +FT IRIYEGWEGLTDEENIDIDLTPFNSPPTRKHKGFVVEPCPILVDQIESYFVIKPANVYQ +FT EIKMRFPNASKYAYTFIDWVITAAAKKRRKLTKDNSWPENLLLNVNVKSLAYILRMNRY +FT ICTRNWKKIELAIDKCIEIAIQLGWLSRRKRIEFLDSSKLSKKEILYLNKERFEEITKK +FT SKEQMEQLEQESIN" +FT CDS 4054..4848 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP3-D" +FT /db_xref="InterPro:IPR008444" +FT /db_xref="InterPro:IPR033758" +FT /db_xref="InterPro:IPR038264" +FT /db_xref="PDB:6GJT" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE18" +FT /protein_id="AAA91571.1" +FT /translation="MGNSGFYLYNTENCVFADNIKVGQMTEPLKDQQIILGTTSTPVAA +FT KMTASDGISLTVSNNSSTNASITIGLDAEKAYQLILEKLGDQILDGIADTIVDSTVQDI +FT LDKIKTDPSLGLLKAFNNFPITNKIQCNGLFTPSNIETLLGGTEIGKFTVTPKSSGSMF +FT LVSADIIASRMEGGVVLALVREGDSKPCAISYGYSSGIPNLCSLRTSITNTGLTPTTYS +FT LRVGGLESGVVWVNALSNGNDILGITNTSNVSFLEVIPQTNA" +FT CDS 4918..5226 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP4-D" +FT /db_xref="UniProtKB/Swiss-Prot:P0CE23" +FT /protein_id="AAA91572.1" +FT /translation="MQNKRKVRDDFIKIVKDVKKDFPELDLKIRVNKEKVTFLNSPLEL +FT YHKSVSLILGLLQQIENSLGLFPDSPVLEKLEDNSLKLKKALIMLILSRKDMFSKAE" +FT CDS 5317..6048 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP5-D (gtg start codon)" +FT /db_xref="GOA:P10559" +FT /db_xref="InterPro:IPR025669" +FT /db_xref="InterPro:IPR027417" +FT /db_xref="UniProtKB/Swiss-Prot:P10559" +FT /protein_id="AAA91573.1" +FT /translation="MGCNLAQFLGKKVLLADLDPQSNLSSGLGASVRSDQKGLHDIVYT +FT SNDLKSIICETKKDSVDLIPASFSSEQFRELDIHRGPSNNLKLFLNEYCAPFYDICIID +FT TPPSLGGLTKEAFVAGDKLIACLTPEPFSILGLQKIREFLSSVGKPEEEHILGIALSFW +FT DDRNSTNQMYIDIIESIYKNKLFSTKIRRDISLSRSLLKEDSVANVYPNSRAAEDILKL +FT THEIANILHIEYERDYSQRTT" +FT CDS 6045..6788 +FT /codon_start=1 +FT /transl_table=11 +FT /product="hypothetical protein" +FT /note="pGP6-D (gtg start codon)" +FT /db_xref="InterPro:IPR005350" +FT /db_xref="UniProtKB/Swiss-Prot:P10560" +FT /protein_id="AAA91574.1" +FT /translation="MNKLKKEADVFFKKNQTAASLDFKKTLPSIELFSATLNSEESQSL +FT DRLFLSESQNYSDEEFYQEDILAVKLLTGQIKSIQKQHVLLLGEKIYNARKILSKDHFS +FT STTFSSWIELVFRTKSSAYNALAYYELFINLPNQTLQKEFQSIPYKSAYILAARKGDLK +FT TKVDVIGKVCGMSNSSAIRVLDQFLPSSRNKDVRETIDKSDSEKNRQLSDFLIEILRIM +FT CSGVSLSSYNENLLQQLFELFKQKS" +FT repeat_region 6857..6945 +FT /note="four tandem 22bp repeats" +XX +SQ Sequence 7502 BP; 2460 A; 1285 C; 1433 G; 2324 T; 0 other; + ggatccgtaa gttagacgaa attttgtctt tgcgcacaga cgatctattt tttgcatcca 60 + atcagatttc ctttcgcatt aaaaaaagac agaataaaga aaccaaaatt ctaatcacat 120 + ttcctatcag cttaatggaa gagttgcaaa aatacacttg tgggagaaat gggagagtat 180 + ttgtttctaa aatagggatt cctgtaacaa caagtcaggt tgcgcataat tttaggcttg 240 + cagagttcca tagtgctatg aaaataaaaa ttactcccag agtacttcgt gcaagcgctt 300 + tgattcattt aaagcaaata ggattaaaag atgaggaaat catgcgtatt tcctgtcttt 360 + catcgagaca aagtgtgtgt tcttattgtt ctggggaaga ggtaattcct ctagtacaaa 420 + cacccacaat attgtgatat aattaaaatt atattcatat tctgttgcca gaaaaaacac 480 + ctttaggcta tattagagcc atcttctttg aagcgttgtc ttctcgagaa gatttatcgt 540 + acgcaaatat catctttgcg gttgcgtgtc ctgtgacctt cattatgtcg gagtctgagc 600 + accctaggcg tttgtactcc gtcacagcgg ttgctcgaag cacgtgcggg gttattttaa 660 + aagggattgc agcttgtagt cctgcttgag agaacgtgcg ggcgatttgc cttaacccca 720 + ccatttttcc ggagcgagtt acgaagacaa aacctcttcg ttgaccgatg tactcttgta 780 + gaaagtgcat aaacttctga ggataagtta taataatcct cttttctgtc tgacggttct 840 + taagctggga gaaagaaatg gtagcttgtt ggaaacaaat ctgactaatc tccaagctta 900 + agacttcaga ggagcgttta cctccttgga gcattgtctg ggcgatcaac caatcccggg 960 + cattgatttt ttttagctct tttaggaagg atgctgtttg caaactgttc atcgcatccg 1020 + tttttactat ttccctggtt ttaaaaaatg ttcgactatt ttcttgttta gaaggttgcg 1080 + ctatagcgac tattccttga gtcatcctgt ttaggaatct tgttaaggaa atatagcttg 1140 + ctgctcgaac ttgtttagta ccttcggtcc aagaagtctt ggcagaggaa acttttttaa 1200 + tcgcatctag gattagatta tgatttaaaa gggaaaactc ttgcagattc atatccaagg 1260 + acaatagacc aatcttttct aaagacaaaa aagatcctcg atatgatcta caagtatgtt 1320 + tgttgagtga tgcggtccaa tgcataataa cttcgaataa ggagaagctt ttcatgcgtt 1380 + tccaatagga ttcttggcga atttttaaaa cttcctgata agacttttca ctatattcta 1440 + acgacatttc ttgctgcaaa gataaaatcc ctttacccat gaaatccctc gtgatataac 1500 + ctatccgtaa aatgtcctga ttagtgaaat aatcaggttg ttaacaggat agcacgctcg 1560 + gtattttttt atataaacat gaaaactcgt tccgaaatag aaaatcgcat gcaagatatc 1620 + gagtatgcgt tgttaggtaa agctctgata tttgaagact ctactgagta tattctgagg 1680 + cagcttgcta attatgagtt taagtgttct catcataaaa acatattcat agtatttaaa 1740 + cacttaaaag acaatggatt acctataact gtagactcgg cttgggaaga gcttttgcgg 1800 + cgtcgtatca aagatatgga caaatcgtat ctcgggttaa tgttgcatga tgctttatca 1860 + aatgacaagc ttagatccgt ttctcatacg gttttcctcg atgatttgag cgtgtgtagc 1920 + gctgaagaaa atttgagtaa tttcattttc cgctcgttta atgagtacaa tgaaaatcca 1980 + ttgcgtagat ctccgtttct attgcttgag cgtataaagg gaaggcttga tagtgctata 2040 + gcaaagactt tttctattcg cagcgctaga ggccggtcta tttatgatat attctcacag 2100 + tcagaaattg gagtgctggc tcgtataaaa aaaagacgag tagcgttctc tgagaatcaa 2160 + aattctttct ttgatggctt cccaacagga tacaaggata ttgatgataa aggagttatc 2220 + ttagctaaag gtaatttcgt gattatagca gctagaccat ctatagggaa aacagcttta 2280 + gctatagaca tggcgataaa tcttgcggtt actcaacagc gtagagttgg tttcctatct 2340 + ctagaaatga gcgcaggtca aattgttgag cggattattg ctaatttaac aggaatatct 2400 + ggtgaaaaat tacaaagagg ggatctctct aaagaagaat tattccgagt agaagaagct 2460 + ggagaaacgg ttagagaatc acatttttat atctgcagtg atagtcagta taagcttaac 2520 + ttaatcgcga atcagatccg gttgctgaga aaagaagatc gagtagacgt aatatttatc 2580 + gattacttgc agttgatcaa ctcatcggtt ggagaaaatc gtcaaaatga aatagcagat 2640 + atatctagaa ccttaagagg tttagcctca gagctaaaca ttcctatagt ttgtttatcc 2700 + caactatcta gaaaagttga ggatagagca aataaagttc ccatgctttc agatttgcga 2760 + gacagcggtc aaatagagca agacgcagat gtgattttgt ttatcaatag gaaggaatcg 2820 + tcttctaatt gtgagataac tgttgggaaa aatagacatg gatcggtttt ctcttcggta 2880 + ttacatttcg atccaaaaat tagtaaattc tccgctatta aaaaagtatg gtaaattata 2940 + gtaactgcca cttcatcaaa agtcctatcc accttgaaaa tcagaagttt ggaagaagac 3000 + ctggtcaatc tattaagata tctcccaaat tggctcaaaa tgggatggta gaagttatag 3060 + gtcttgattt tctttcatct cattaccatg cattagcagc tatccaaaga ttactgaccg 3120 + caacgaatta caaggggaac acaaaagggg ttgttttatc cagagaatca aatagttttc 3180 + aatttgaagg atggatacca agaatccgtt ttacaaaaac tgaattctta gaggcttatg 3240 + gagttaagcg gtataaaaca tccagaaata agtatgagtt tagtggaaaa gaagctgaaa 3300 + ctgctttaga agccttatac catttaggac atcaaccgtt tttaatagtg gcaactagaa 3360 + ctcgatggac taatggaaca caaatagtag accgttacca aactctttct ccgatcatta 3420 + ggatttacga aggatgggaa ggtttaactg acgaagaaaa tatagatata gacttaacac 3480 + cttttaattc accacctaca cggaaacata aagggttcgt tgtagagcca tgtcctatct 3540 + tggtagatca aatagaatcc tactttgtaa tcaagcctgc aaatgtatac caagaaataa 3600 + aaatgcgttt cccaaatgca tcaaagtatg cttacacatt tatcgactgg gtgattacag 3660 + cagctgcgaa aaagagacga aaattaacta aggataattc ttggccagaa aacttgttat 3720 + taaacgttaa cgttaaaagt cttgcatata ttttaaggat gaatcggtac atctgtacaa 3780 + ggaactggaa aaaaatcgag ttagctatcg ataaatgtat agaaatcgcc attcagcttg 3840 + gctggttatc tagaagaaaa cgcattgaat ttctggattc ttctaaactc tctaaaaaag 3900 + aaattctata tctaaataaa gagcgctttg aagaaataac taagaaatct aaagaacaaa 3960 + tggaacaatt agaacaagaa tctattaatt aatagcaagc ttgaaactaa aaacctaatt 4020 + tatttaaagc tcaaaataaa aaagagtttt aaaatgggaa attctggttt ttatttgtat 4080 + aacactgaaa actgcgtctt tgctgataat atcaaagttg ggcaaatgac agagccgctc 4140 + aaggaccagc aaataatcct tgggacaaca tcaacacctg tcgcagccaa aatgacagct 4200 + tctgatggaa tatctttaac agtctccaat aattcatcaa ccaatgcttc tattacaatt 4260 + ggtttggatg cggaaaaagc ttaccagctt attctagaaa agttgggaga tcaaattctt 4320 + gatggaattg ctgatactat tgttgatagt acagtccaag atattttaga caaaatcaaa 4380 + acagaccctt ctctaggttt gttgaaagct tttaacaact ttccaatcac taataaaatt 4440 + caatgcaacg ggttattcac tcccagtaac attgaaactt tattaggagg aactgaaata 4500 + ggaaaattca cagtcacacc caaaagctct gggagcatgt tcttagtctc agcagatatt 4560 + attgcatcaa gaatggaagg cggcgttgtt ctagctttgg tacgagaagg tgattctaag 4620 + ccctgcgcga ttagttatgg atactcatca ggcattccta atttatgtag tctaagaacc 4680 + agtattacta atacaggatt gactccgaca acgtattcat tacgtgtagg cggtttagaa 4740 + agcggtgtgg tatgggttaa tgccctttct aatggcaatg atattttagg aataacaaat 4800 + acttctaatg tatctttttt agaggtaata cctcaaacaa acgcttaaac aatttttatt 4860 + ggatttttct tataggtttt atatttagag aaaacagttc gaattacggg gtttgttatg 4920 + caaaataaaa gaaaagtgag ggacgatttt attaaaattg ttaaagatgt gaaaaaagat 4980 + ttccccgaat tagacctaaa aatacgagta aacaaggaaa aagtaacttt cttaaattct 5040 + cccttagaac tctaccataa aagtgtctca ctaattctag gactgcttca acaaatagaa 5100 + aactctttag gattattccc agactctcct gttcttgaaa aattagagga taacagttta 5160 + aagctaaaaa aggctttgat tatgcttatc ttgtctagaa aagacatgtt ttccaaggct 5220 + gaatagacaa cttactctaa cgttggagtt gatttgcaca ccttagtttt ttgctctttt 5280 + aagggaggaa ctggaaaaac aacactttct ctaaacgtgg gatgcaactt ggcccaattt 5340 + ttagggaaaa aagtgttact tgctgaccta gacccgcaat ccaatttatc ttctggattg 5400 + ggggctagtg tcagaagtga ccaaaaaggc ttgcacgaca tagtatacac atcaaacgat 5460 + ttaaaatcaa tcatttgcga aacaaaaaaa gatagtgtgg acctaattcc tgcatcattt 5520 + tcatccgaac agtttagaga attggatatt catagaggac ctagtaacaa cttaaagtta 5580 + tttctgaatg agtactgcgc tcctttttat gacatctgca taatagacac tccacctagc 5640 + ctaggagggt taacgaaaga agcttttgtt gcaggagaca aattaattgc ttgtttaact 5700 + ccagaacctt tttctattct agggttacaa aagatacgtg aattcttaag ttcggtcgga 5760 + aaacctgaag aagaacacat tcttggaata gctttgtctt tttgggatga tcgtaactcg 5820 + actaaccaaa tgtatataga cattatcgag tctatttaca aaaacaagct tttttcaaca 5880 + aaaattcgtc gagatatttc tctcagccgt tctcttctta aagaagattc tgtagctaat 5940 + gtctatccaa attctagggc cgcagaagat attctgaagt taacgcatga aatagcaaat 6000 + attttgcata tcgaatatga acgagattac tctcagagga caacgtgaac aaactaaaaa 6060 + aagaagcgga tgtctttttt aaaaaaaatc aaactgccgc ttctctagat tttaagaaga 6120 + cgcttccctc cattgaacta ttctcagcaa ctttgaattc tgaggaaagt cagagtttgg 6180 + atcgattatt tttatcagag tcccaaaact attcggatga agaattttat caagaagaca 6240 + tcctagcggt aaaactgctt actggtcaga taaaatccat acagaagcaa cacgtacttc 6300 + ttttaggaga aaaaatctat aatgctagaa aaatcctgag taaggatcac ttctcctcaa 6360 + caactttttc atcttggata gagttagttt ttagaactaa gtcttctgct tacaatgctc 6420 + ttgcatatta cgagcttttt ataaacctcc ccaaccaaac tctacaaaaa gagtttcaat 6480 + cgatccccta taaatccgca tatattttgg ccgctagaaa aggcgattta aaaaccaagg 6540 + tcgatgtgat agggaaagta tgtggaatgt cgaactcatc ggcgataagg gtgttggatc 6600 + aatttcttcc ttcatctaga aacaaagacg ttagagaaac gatagataag tctgattcag 6660 + agaagaatcg ccaattatct gatttcttaa tagagatact tcgcatcatg tgttccggag 6720 + tttctttgtc ctcctataac gaaaatcttc tacaacagct ttttgaactt tttaagcaaa 6780 + agagctgatc ctccgtcagc tcatatatat atatctatta tatatatata tttagggatt 6840 + tgatttcacg agagagattt gcaactcttg gtggtagact ttgcaactct tggtggtaga 6900 + ctttgcaact cttggtggta gactttgcaa ctcttggtgg tagacttggt cataatggac 6960 + ttttgttaaa aaatttatta aaatcttaga gctccgattt tgaatagctt tggttaagaa 7020 + aatgggctcg atggctttcc ataaaagtag attgttttta acttttgggg acgcgtcgga 7080 + aatttggtta tctactttat cttatctaac tagaaaaaat tatgcgtctg ggattaactt 7140 + tcttgtttct ttagagattc tggatttatc ggaaaccttg ataaaggcta tttctcttga 7200 + ccacagcgaa tctttgttta aaatcaagtc tctagatgtt tttaatggaa aagttgtttc 7260 + agaggcatct aaacaggcta gagcggcatg ctacatatct ttcacaaagt ttttgtatag 7320 + attgaccaag ggatatatta aacccgctat tccattgaaa gattttggaa acactacatt 7380 + ttttaaaatc cgagacaaaa tcaaaacaga atcgatttct aagcaggaat ggacagtttt 7440 + ttttgaagcg ctccggatag tgaattatag agactattta atcggtaaat tgattgtaca 7500 + ag 7502 +// diff --git a/test/jalview/io/cache/JvCacheableInputBoxTest.java b/test/jalview/io/cache/JvCacheableInputBoxTest.java index 010a4b2..9b899e2 100644 --- a/test/jalview/io/cache/JvCacheableInputBoxTest.java +++ b/test/jalview/io/cache/JvCacheableInputBoxTest.java @@ -13,8 +13,8 @@ public class JvCacheableInputBoxTest private static final String TEST_CACHE_KEY = "CACHE.UNIT_TEST"; - private JvCacheableInputBox cacheBox = new JvCacheableInputBox( - TEST_CACHE_KEY); + private JvCacheableInputBox cacheBox = new JvCacheableInputBox<>( + TEST_CACHE_KEY, 20); @BeforeClass(alwaysRun = true) private void setUpCache() diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java index 097ccd4..dd789d6 100644 --- a/test/jalview/util/MappingUtilsTest.java +++ b/test/jalview/util/MappingUtilsTest.java @@ -24,6 +24,7 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.fail; import jalview.api.AlignViewportI; import jalview.commands.EditCommand; @@ -1284,4 +1285,33 @@ public class MappingUtilsTest assertEquals(1, ranges.size()); assertEquals(9, ranges.get(0)[1]); } + + @Test(groups = "Functional") + public void testListToArray() + { + List ranges = new ArrayList<>(); + + int[] result = MappingUtils.listToArray(ranges); + assertEquals(result.length, 0); + ranges.add(new int[] {24, 12}); + result = MappingUtils.listToArray(ranges); + assertEquals(result.length, 2); + assertEquals(result[0], 24); + assertEquals(result[1], 12); + ranges.add(new int[] {-7, 30}); + result = MappingUtils.listToArray(ranges); + assertEquals(result.length, 4); + assertEquals(result[0], 24); + assertEquals(result[1], 12); + assertEquals(result[2], -7); + assertEquals(result[3], 30); + try + { + MappingUtils.listToArray(null); + fail("Expected exception"); + } catch (NullPointerException e) + { + // expected + } + } } diff --git a/test/jalview/ws/dbsources/EmblSourceTest.java b/test/jalview/ws/dbsources/EmblXmlSourceTest.java similarity index 93% rename from test/jalview/ws/dbsources/EmblSourceTest.java rename to test/jalview/ws/dbsources/EmblXmlSourceTest.java index d450495..5f288a8 100644 --- a/test/jalview/ws/dbsources/EmblSourceTest.java +++ b/test/jalview/ws/dbsources/EmblXmlSourceTest.java @@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; +import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.SequenceI; @@ -40,9 +41,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class EmblSourceTest +public class EmblXmlSourceTest { // adapted from http://www.ebi.ac.uk/ena/data/view/X07547&display=xml @@ -95,16 +97,49 @@ public class EmblSourceTest + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT" + "
      "; + private EmblXmlSource testee; + + @BeforeClass(alwaysRun = true) + public void setUp() + { + testee = new EmblXmlSource() + { + + @Override + public String getDbSource() + { + return null; + } + + @Override + public String getDbName() + { + return null; + } + + @Override + public String getTestQuery() + { + return null; + } + + @Override + public AlignmentI getSequenceRecords(String queries) throws Exception + { + return null; + } + }; + } + @Test(groups = "Functional") public void testGetCdsRanges() { - EmblSource testee = new EmblSource(); - /* * Make a (CDS) Feature with 5 locations */ Feature cds = new Feature(); - cds.setLocation("join(10..20,complement(30..40),50..60,70..80,complement(110..120))"); + cds.setLocation( + "join(10..20,complement(30..40),50..60,70..80,complement(110..120))"); int[] exons = testee.getCdsRanges("EMBL", cds); assertEquals("[10, 20, 40, 30, 50, 60, 70, 80, 120, 110]", @@ -116,10 +151,9 @@ public class EmblSourceTest { // not the whole sequence but enough for this test... List peptides = new ArrayList<>(); - List entries = EmblSourceTest.getEmblEntries(); + List entries = getEmblEntries(); assertEquals(1, entries.size()); EntryType entry = entries.get(0); - EmblSource testee = new EmblSource(); String sourceDb = "EMBL"; SequenceI dna = testee.getSequence(sourceDb, entry, peptides); @@ -165,8 +199,9 @@ public class EmblSourceTest 3, 1); MapList cds2Map = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 }, 3, 1); - MapList cds3Map = new MapList(new int[] { 4, 6, 10, 15 }, new int[] { - 1, 3 }, 3, 1); + MapList cds3Map = new MapList(new int[] { 4, 6, 10, 15 }, + new int[] + { 1, 3 }, 3, 1); DBRefEntry[] dbrefs = dna.getDBRefs(); assertEquals(7, dbrefs.length); @@ -222,10 +257,12 @@ public class EmblSourceTest * - to EMBLCDS (with 1:3 mapping) * - direct (no mapping) to other protein accessions */ - MapList proteinToCdsMap1 = new MapList(new int[] { 1, 4 }, new int[] { - 1, 12 }, 1, 3); - MapList proteinToCdsMap2 = new MapList(new int[] { 1, 3 }, new int[] { - 1, 9 }, 1, 3); + MapList proteinToCdsMap1 = new MapList(new int[] { 1, 4 }, + new int[] + { 1, 12 }, 1, 3); + MapList proteinToCdsMap2 = new MapList(new int[] { 1, 3 }, + new int[] + { 1, 9 }, 1, 3); // dbrefs for first CDS EMBL product CAA30420.1 dbrefs = peptides.get(0).getDBRefs(); @@ -339,10 +376,10 @@ public class EmblSourceTest @Test(groups = { "Functional" }) public void testGetEmblEntries() { - List entries = EmblSourceTest.getEmblEntries(); + List entries = getEmblEntries(); assertEquals(1, entries.size()); EntryType entry = entries.get(0); - + assertEquals("X07547", entry.getAccession()); assertEquals("C. trachomatis plasmid", entry.getDescription()); assertEquals("STD", entry.getDataClass()); @@ -359,7 +396,7 @@ public class EmblSourceTest assertEquals(2, entry.getKeyword().size()); assertEquals("plasmid", entry.getKeyword().get(0)); assertEquals("unidentified reading frame", entry.getKeyword().get(1)); - + /* * dbrefs */ @@ -372,7 +409,7 @@ public class EmblSourceTest assertEquals("MD5", dbref.getDb()); assertEquals("ac73317", dbref.getId()); assertNull(dbref.getSecondaryId()); - + /* * three sequence features for CDS */ @@ -403,7 +440,7 @@ public class EmblSourceTest q = ef.getQualifier().get(2); assertEquals("translation", q.getName()); assertEquals("MLCF", q.getValue()); - + /* * second CDS */ @@ -422,7 +459,7 @@ public class EmblSourceTest q = ef.getQualifier().get(1); assertEquals("translation", q.getName()); assertEquals("MSSS", q.getValue()); - + /* * third CDS */ @@ -438,16 +475,14 @@ public class EmblSourceTest q = ef.getQualifier().get(1); assertEquals("translation", q.getName()); assertEquals("MSS", q.getValue()); - + /* * Sequence - raw data before removal of newlines */ String seq = entry.getSequence(); - assertEquals( - "GGTATGTCCTCTAGTACAAAC\n" - + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT", - seq); - + assertEquals("GGTATGTCCTCTAGTACAAAC\n" + + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT", seq); + /* * getSequence() converts empty DBRefEntry.version to "0" */ @@ -455,9 +490,9 @@ public class EmblSourceTest assertNull(entry.getFeature().get(0).getXref().get(1).getSecondaryId()); } - static List getEmblEntries() + List getEmblEntries() { - return new EmblSource() + return testee .getEmblEntries(new ByteArrayInputStream(TESTDATA.getBytes())); } } diff --git a/test/jalview/ws/dbsources/PfamFullTest.java b/test/jalview/ws/dbsources/PfamFullTest.java index f5cc640..23cceb2 100644 --- a/test/jalview/ws/dbsources/PfamFullTest.java +++ b/test/jalview/ws/dbsources/PfamFullTest.java @@ -38,7 +38,7 @@ public class PfamFullTest @Test(groups = "Functional") public void testGetURL() { - String path = "pfam.xfam.org/family/ABC/alignment/full"; + String path = "pfam.xfam.org/family/ABC/alignment/full/gzipped"; // with default value for domain String url = new PfamFull().getURL(" abc "); diff --git a/test/jalview/ws/dbsources/PfamSeedTest.java b/test/jalview/ws/dbsources/PfamSeedTest.java index 355ef0c..451810b 100644 --- a/test/jalview/ws/dbsources/PfamSeedTest.java +++ b/test/jalview/ws/dbsources/PfamSeedTest.java @@ -38,7 +38,7 @@ public class PfamSeedTest @Test(groups = "Functional") public void testGetURL() { - String path = "pfam.xfam.org/family/ABC/alignment/seed"; + String path = "pfam.xfam.org/family/ABC/alignment/seed/gzipped"; // with default value for domain String url = new PfamSeed().getURL(" abc "); diff --git a/test/jalview/ws/dbsources/RfamFullTest.java b/test/jalview/ws/dbsources/RfamFullTest.java index 2d1497f..87b963f 100644 --- a/test/jalview/ws/dbsources/RfamFullTest.java +++ b/test/jalview/ws/dbsources/RfamFullTest.java @@ -38,7 +38,7 @@ public class RfamFullTest @Test(groups = "Functional") public void testGetURL() { - String path = "rfam.xfam.org/family/ABC/alignment/full"; + String path = "rfam.xfam.org/family/ABC/alignment/full?gzip=1&download=1"; // with default value for domain String url = new RfamFull().getURL(" abc "); diff --git a/test/jalview/ws/dbsources/RfamSeedTest.java b/test/jalview/ws/dbsources/RfamSeedTest.java index 745ba2e..1165d1f 100644 --- a/test/jalview/ws/dbsources/RfamSeedTest.java +++ b/test/jalview/ws/dbsources/RfamSeedTest.java @@ -38,7 +38,7 @@ public class RfamSeedTest @Test(groups = "Functional") public void testGetURL() { - String path = "rfam.xfam.org/family/ABC/alignment/stockholm"; + String path = "rfam.xfam.org/family/ABC/alignment/stockholm?gzip=1&download=1"; // with default value for domain String url = new RfamSeed().getURL(" abc "); diff --git a/test/jalview/ws/ebi/EBIFetchClientTest.java b/test/jalview/ws/ebi/EBIFetchClientTest.java index c559966..c3fae6c 100644 --- a/test/jalview/ws/ebi/EBIFetchClientTest.java +++ b/test/jalview/ws/ebi/EBIFetchClientTest.java @@ -47,13 +47,13 @@ public class EBIFetchClientTest /* * EMBL */ - assertEquals("https://www.ebi.ac.uk/ena/data/view/x53838&display=xml", + assertEquals("https://www.ebi.ac.uk/ena/browser/api/embl/x53838?download=true&gzip=true", EBIFetchClient.buildUrl("X53838", "EMBL", "display=xml")); /* * EMBLCDS */ - assertEquals("https://www.ebi.ac.uk/ena/data/view/caa37824&display=xml", + assertEquals("https://www.ebi.ac.uk/ena/browser/api/embl/caa37824?download=true&gzip=true", EBIFetchClient.buildUrl("CAA37824", "EMBL", "display=xml")); /* diff --git a/utils/clover/lib/clover-ant-4.4.1.jar b/utils/clover/lib/clover-ant-4.4.1.jar new file mode 100644 index 0000000..4f92395 Binary files /dev/null and b/utils/clover/lib/clover-ant-4.4.1.jar differ diff --git a/utils/cmd-nox.sh b/utils/cmd-nox.sh new file mode 100755 index 0000000..bd615bd --- /dev/null +++ b/utils/cmd-nox.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +CMD=$(basename $0) +CMD=${CMD%-nox.sh} + +echo "Running '$CMD' headlessly" + +xvfb-run -s "-screen 0 1280x800x16" -e /dev/stdout -a $CMD ${@} diff --git a/utils/gradle-nox.sh b/utils/gradle-nox.sh new file mode 120000 index 0000000..4abb9a2 --- /dev/null +++ b/utils/gradle-nox.sh @@ -0,0 +1 @@ +cmd-nox.sh \ No newline at end of file