Merge branch 'merge/JAL-3628+JAL-3608+JAL-3609+JAL-3541+Release_2_11_1_Branch' into...
authorJim Procter <jprocter@issues.jalview.org>
Wed, 5 Aug 2020 14:47:31 +0000 (15:47 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 5 Aug 2020 14:47:31 +0000 (15:47 +0100)
14 files changed:
build.gradle
gradle.properties
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/Console.java
src/jalview/io/BackupFiles.java
src/jalview/io/BackupFilesPresetEntry.java
src/jalview/ws/sifts/SiftsClient.java
utils/clover/lib/clover-ant-4.4.1.jar [new file with mode: 0644]
utils/cmd-nox.sh [new file with mode: 0755]
utils/gradle-nox.sh [new symlink]

index 2c7f138..2060073 100644 (file)
@@ -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
     }
@@ -409,8 +413,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 +457,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 +467,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 +487,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 +639,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 +853,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 +871,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 +949,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 +992,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 +1001,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 +1029,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 +1038,7 @@ task syncLib(type: Sync) {
 task syncResources(type: Sync) {
   from resourceDir
   include "**/*.*"
-  into "${classesDir}"
+  into "${resourceClassesDir}"
   preserve {
     include "**"
   }
@@ -938,18 +1055,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
   }
@@ -972,6 +1088,11 @@ test {
   targetCompatibility = compile_target_compatibility
   jvmArgs += additional_compiler_args
 
+  doFirst {
+    if (useClover) {
+      println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
+    }
+  }
 }
 
 
@@ -997,22 +1118,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" ]
@@ -1033,12 +1154,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")
   }
 }
 
@@ -1048,13 +1169,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*/**"
@@ -1064,20 +1185,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
   }
@@ -1093,7 +1214,7 @@ task makeDist {
   dependsOn cleanPackageDir
   dependsOn syncJars
   dependsOn jar
-  outputs.dir("${jalviewDir}/${packageDir}")
+  outputs.dir("${jalviewDir}/${package_dir}")
 }
 
 
@@ -1114,7 +1235,7 @@ shadowJar {
   manifest {
     attributes 'Implementation-Version': JALVIEW_VERSION
   }
-  mainClassName = shadowJarMainClass
+  mainClassName = shadow_jar_main_class
   mergeServiceFiles()
   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
   minimize()
@@ -1206,7 +1327,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
@@ -1301,7 +1422,7 @@ task getdownWebsite() {
   }
 
   if (buildDist) {
-    inputs.dir("${jalviewDir}/${packageDir}")
+    inputs.dir("${jalviewDir}/${package_dir}")
   }
   outputs.dir(getdownWebsiteDir)
   outputs.dir(getdownFilesDir)
index 782a495..dbf124b 100644 (file)
@@ -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
 
index 1c8cc2a..8e8c2ce 100644 (file)
@@ -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}
@@ -1093,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
@@ -1345,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
@@ -1411,3 +1412,7 @@ label.include_linked_features = Include {0} features
 label.include_linked_tooltip = Include visible {0} features<br>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
+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
index 1f1afdc..15339e4 100644 (file)
@@ -1346,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
@@ -1412,3 +1413,7 @@ label.include_linked_features = Incluir caracter
 label.include_linked_tooltip = Incluir características de {0}<br>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
+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
index 751461f..6e44b0c 100755 (executable)
@@ -27,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;
@@ -440,8 +442,7 @@ 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
+    // to be done
     // Dont do this check if running in headless mode
 
     if (jnlpVersion == null && getDefault("VERSION_CHECK", true)
@@ -1104,20 +1105,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);
@@ -1126,11 +1130,18 @@ public class Cache
     appendIfNotNull(sb, "Launcher version: ",
             System.getProperty("launcher_version"), "\n", null);
     LookAndFeel laf = UIManager.getLookAndFeel();
-    String lafName = laf == null?"Not obtained":laf.getName();
-    String lafClass = laf == null?"unknown":laf.getClass().getName();
-    appendIfNotNull(sb, "LookAndFeel: ", lafName+" ("+lafClass+")", "\n", null);
-    // 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"))) {
+    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, "Java home: ", System.getProperty("java.home"),
@@ -1149,4 +1160,12 @@ 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();
+  }
 }
index 2e33474..35b21df 100755 (executable)
@@ -297,90 +297,7 @@ public class Jalview
 
     desktop = null;
 
-    // property laf = "crossplatform", "system", "gtk", "metal" 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)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "system":
-      lafSet = setSystemLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "gtk":
-      lafSet = setGtkLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "metal":
-      lafSet = setMetalLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "nimbus":
-      lafSet = setNimbusLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "quaqua":
-      lafSet = setQuaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "vaqua":
-      lafSet = setVaquaLookAndFeel();
-    {
-      System.err.println("Could not set requested laf=" + laf);
-    }
-      break;
-    case "mac":
-      lafSet = setMacLookAndFeel();
-      if (!lafSet)
-      {
-        System.err.println("Could not set requested laf=" + laf);
-      }
-      break;
-    case "none":
-      break;
-    default:
-      System.err.println("Requested laf=" + laf + " not implemented");
-    }
-    if (!lafSet)
-    {
-      setSystemLookAndFeel();
-      if (Platform.isLinux()) {
-        setMetalLookAndFeel();
-      }
-      if (Platform.isAMac())
-      {
-        setMacLookAndFeel();
-      }
-    }
+    setLookAndFeel();
 
     /*
      * configure 'full' SO model if preferences say to, else use the default (SO
@@ -402,11 +319,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();
       }
 
@@ -801,6 +720,101 @@ 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()
   {
     boolean set = false;
@@ -811,8 +825,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -826,8 +841,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
@@ -853,8 +869,9 @@ public class Jalview
       set = true;
     } catch (Exception ex)
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
+      Cache.log.error("Unexpected Look and Feel Exception");
+      Cache.log.error(ex.getMessage());
+      Cache.log.debug(Cache.getStackTraceString(ex));
     }
     return set;
   }
index d03b2c7..e4aa736 100644 (file)
  */
 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!
  * 
@@ -1169,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
@@ -1198,39 +1200,66 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       else
       {
         // create backupfiles object and get new temp filename destination
+        Cache.log.debug("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.debug(
+                  "ALIGNFRAME setting PrintWriter to " + tempFilePath);
+          PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
+
+          Cache.log.debug(
+                  "ALIGNFRAME about to write to temp file " + tempFilePath);
 
           out.print(output);
+          Cache.log.debug("ALIGNFRAME about to close file");
           out.close();
+          Cache.log.debug("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 else 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.debug("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;
@@ -2737,8 +2766,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"));
     }
 
     /*
@@ -3367,8 +3396,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);
   }
 
@@ -5695,6 +5724,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   private Rectangle lastFeatureSettingsBounds = null;
+
   @Override
   public void setFeatureSettingsGeometry(Rectangle bounds)
   {
index 4c019a6..a493640 100644 (file)
  */
 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,8 @@ public class Console extends WindowAdapter
 
   private int MIN_HEIGHT = 250;
 
+  private JComboBox logLevelCombo = new JComboBox();
+
   public Console()
   {
     // create all components and add them
@@ -115,17 +129,104 @@ 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);
+    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);
+    logLevelLabel.setToolTipText(
+            MessageManager.getString("label.log_level_tooltip"));
+    logLevelCombo.setToolTipText(
+            MessageManager.getString("label.log_level_tooltip"));
+
+    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 +252,50 @@ public class Console extends WindowAdapter
     textAppender.start();
   }
 
+  private void setChosenLogLevelCombo()
+  {
+    Level currentLogLevel = Cache.log == null ? Level.INFO
+            : Cache.log.getLevel();
+    logLevelCombo.setSelectedItem(currentLogLevel);
+    if (!logLevelCombo.getSelectedItem().equals(currentLogLevel)) // currentLogLevel
+                                                                  // not in list
+    {
+      if (currentLogLevel != null && currentLogLevel 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(currentLogLevel))
+          {
+            logLevelCombo.insertItemAt(currentLogLevel, i);
+            added = true;
+            break;
+          }
+        }
+        if (!added) // lower priority than others or some confusion -- add to
+                    // end of list
+        {
+          logLevelCombo.addItem(currentLogLevel);
+        }
+        logLevelCombo.setSelectedItem(currentLogLevel);
+      }
+      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 +789,21 @@ public class Console extends WindowAdapter
     frame.setVisible(selected);
     if (selected == true)
     {
+      setChosenLogLevelCombo();
       redirectStreams();
       updateConsole = true;
       frame.toFront();
     }
     else
     {
+      // reset log level to user preference
+      if (Cache.log != null)
+      {
+        String userLogLevel = Cache.getDefault("logs.Jalview.level",
+                Level.INFO.toString());
+        Cache.log.setLevel(Level.toLevel(userLogLevel));
+      }
+
       unredirectStreams();
       updateConsole = false;
     }
index 0150579..e943b23 100644 (file)
  */
 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,47 @@ public class BackupFiles
       {
         String tempfilename = file.getName();
         File tempdir = file.getParentFile();
-        temp = File.createTempFile(tempfilename, TEMP_FILE_EXT + "_newfile",
-                tempdir);
+        Cache.log.debug(
+                "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.debug(
+                "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.log.debug("BACKUPFILES classInit");
+    boolean e = Cache.getDefault(ENABLED, true);
+    setEnabled(e);
+    Cache.log.debug("BACKUPFILES " + (e ? "enabled" : "disabled"));
     BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
             .getSavedBackupEntry();
+    Cache.log.debug("BACKUPFILES preset scheme " + bfpe.toString());
     setConfirmDelete(bfpe.confirmDelete);
+    Cache.log.debug("BACKUPFILES confirm delete " + bfpe.confirmDelete);
   }
 
   public static void setEnabled(boolean flag)
@@ -188,9 +215,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 +237,7 @@ public class BackupFiles
 
   public boolean renameTempFile()
   {
-    return tempFile.renameTo(file);
+    return moveFileToFile(tempFile, file);
   }
 
   // roll the backupfiles
@@ -226,24 +254,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.debug("BACKUPFILES rollBackupFiles starting");
+
     String dir = "";
     File dirFile;
     try
     {
       dirFile = file.getParentFile();
       dir = dirFile.getCanonicalPath();
+      Cache.log.debug("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.debug("BACKUPFILES filename is " + filename);
     boolean ret = true;
     // Create/move backups up one
 
@@ -255,9 +294,13 @@ public class BackupFiles
     File[] backupFiles = dirFile.listFiles(bff);
     int nextIndexNum = 0;
 
+    Cache.log
+            .debug("BACKUPFILES backupFiles.length: " + backupFiles.length);
     if (backupFiles.length == 0)
     {
       // No other backup files. Just need to move existing file to backupfile_1
+      Cache.log.debug(
+              "BACKUPFILES no existing backup files, setting index to 1");
       nextIndexNum = 1;
     }
     else
@@ -270,7 +313,7 @@ public class BackupFiles
       if (reverseOrder)
       {
         // backup style numbering
-
+        Cache.log.debug("BACKUPFILES rolling files in reverse order");
 
         int tempMax = noMax ? -1 : max;
         // noMax == true means no limits
@@ -287,7 +330,7 @@ public class BackupFiles
             tempMax = i;
           }
         }
-        
+
         File previousFile = null;
         File fileToBeDeleted = null;
         for (int n = tempMax; n > 0; n--)
@@ -302,6 +345,7 @@ public class BackupFiles
             // no "oldest" file to delete
             previousFile = backupfile_n;
             fileToBeDeleted = null;
+            Cache.log.debug("BACKUPFILES No oldest file to delete");
             continue;
           }
 
@@ -312,19 +356,23 @@ public class BackupFiles
             File replacementFile = backupfile_n;
             long fileToBeDeletedLMT = fileToBeDeleted.lastModified();
             long replacementFileLMT = replacementFile.lastModified();
+            Cache.log.debug("BACKUPFILES fileToBeDeleted is "
+                    + fileToBeDeleted.getAbsolutePath());
+            Cache.log.debug("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 +383,11 @@ public class BackupFiles
 
                 boolean delete = confirmNewerDeleteFile(fileToBeDeleted,
                         replacementFile, true);
+                Cache.log.debug("BACKUPFILES "
+                        + (delete ? "confirmed" : "not") + " deleting file "
+                        + fileToBeDeleted.getAbsolutePath()
+                        + " which is newer than "
+                        + replacementFile.getAbsolutePath());
 
                 if (delete)
                 {
@@ -343,21 +396,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 +431,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 +443,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.debug("BACKUPFILES backupFiles: " + bfsb.toString());
 
         // noMax == true means keep all backup files
         if ((!noMax) && bfTreeMap.size() >= max)
         {
+          Cache.log.debug("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.debug("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 +486,9 @@ public class BackupFiles
             File fileToBeDeleted = backupFiles[i];
             boolean delete = true;
 
+            Cache.log.debug(
+                    "BACKUPFILES fileToBeDeleted: " + fileToBeDeleted);
+
             boolean newer = false;
             if (replacementFile != null)
             {
@@ -421,14 +503,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 +518,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 +549,17 @@ public class BackupFiles
     String latestBackupFilename = dir + File.separatorChar
             + BackupFilenameParts.getBackupFilename(nextIndexNum, basename,
                     suffix, digits);
-    ret |= file.renameTo(new File(latestBackupFilename));
-
+    Cache.log.debug("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 +615,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 +623,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 +651,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 +689,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 +701,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 +725,10 @@ public class BackupFiles
         for (int i = 0; i < deleteFiles.size(); i++)
         {
           File fileToDelete = deleteFiles.get(i);
+          Cache.log.debug(
+                  "BACKUPFILES deleting fileToDelete:" + fileToDelete);
           fileToDelete.delete();
-          System.out.println("DELETING '" + fileToDelete.getName() + "'");
+          Cache.log.warn("deleting '" + fileToDelete.getName() + "'");
         }
       }
 
@@ -621,8 +738,7 @@ public class BackupFiles
   }
 
   private TreeMap<Integer, File> 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 +763,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 +777,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 +788,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 +798,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 +837,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 +879,48 @@ 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)
+  {
+    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.debug("BACKUPFILES deleting " + newFile.getAbsolutePath());
+      newFile.delete();
+      Cache.log.debug("BACKUPFILES moving " + oldFile.getAbsolutePath()
+              + " to " + newFile.getAbsolutePath());
+      Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING);
+      ret = true;
+      Cache.log.debug("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;
+  }
 }
index 69130d0..0734665 100644 (file)
  */
 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
 {
 
@@ -135,6 +135,8 @@ public class BackupFilesPresetEntry
   {
     String savedPresetString = Cache
             .getDefault(BackupFilesPresetEntry.SAVEDCONFIG, null);
+    Cache.log.debug("BACKUPFILESPRESETENTRY saved preset string is "
+            + savedPresetString);
     BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
             .createBackupFilesPresetEntry(savedPresetString);
     if (savedPreset == null)
index b5f9653..9790f79 100644 (file)
  */
 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<Integer> omitNonObserved = new ArrayList<>();
-    int nonObservedShiftIndex = 0,pdbeNonObserved=0;
+    int nonObservedShiftIndex = 0, pdbeNonObserved = 0;
     // System.out.println("Generating mappings for : " + entityId);
     Entity entity = null;
     entity = getEntityById(entityId);
@@ -492,7 +494,7 @@ public class SiftsClient implements SiftsClientI
     TreeMap<Integer, String> resNumMap = new TreeMap<Integer, String>();
     List<Segment> segments = entity.getSegment();
     SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap,
-            omitNonObserved, nonObservedShiftIndex,pdbeNonObserved);
+            omitNonObserved, nonObservedShiftIndex, pdbeNonObserved);
     processSegments(segments, shp);
     try
     {
@@ -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<int[]> from=new ArrayList<>(),to=new ArrayList<>();
-    int[]_cfrom=null,_cto=null;
+    List<int[]> from = new ArrayList<>(), to = new ArrayList<>();
+    int[] _cfrom = null, _cto = null;
     String matchedSeq = originalSeq;
-    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb sequence that starts <-1
+    if (seqStart != UNASSIGNED) // fixme! seqStart can map to -1 for a pdb
+                                // sequence that starts <-1
     {
-      for (int seqps:keys)
+      for (int seqps : keys)
       {
         int pdbpos = mapping.get(seqps)[PDBE_POS];
         if (pdbpos == UNASSIGNED)
@@ -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/utils/clover/lib/clover-ant-4.4.1.jar b/utils/clover/lib/clover-ant-4.4.1.jar
new file mode 100644 (file)
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 (executable)
index 0000000..bd615bd
--- /dev/null
@@ -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 (symlink)
index 0000000..4abb9a2
--- /dev/null
@@ -0,0 +1 @@
+cmd-nox.sh
\ No newline at end of file