diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..07a707b7ef --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,54 @@ +name: Nightly Integration Tests + +# The integration tests reach out to CATH, ECOD, RCSB, EBI, UniProt and others. +# That makes them valuable - they are how we find out that an upstream service +# has changed a URL, a format or a redirect - but it also makes them unsuitable +# as a gate on pull requests, because an outage anywhere blocks every +# contributor. Running them on a schedule keeps the coverage while decoupling it +# from people's ability to merge. +on: + schedule: + # 03:17 UTC daily. Off the hour deliberately: scheduled jobs that ask for + # exactly midnight queue behind everybody else's. + - cron: '17 3 * * *' + # Also runnable by hand, e.g. to confirm an upstream service is back. + workflow_dispatch: + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + integrationtest: + runs-on: ubuntu-latest + # These tests download large files from servers we do not control; the + # default 6 hour limit is far more than they need and far more than we want + # to spend if one of them hangs. + timeout-minutes: 90 + strategy: + matrix: + # One JDK only. The point of this run is to exercise the network paths, + # not the language level, which the pull request build already covers + # across 11, 17 and 21. + java: [21] + fail-fast: false + name: Integration tests, JDK ${{ matrix.java }} + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'oracle' + java-version: ${{ matrix.java }} + - name: Build and run integration tests + run: mvn verify --no-transfer-progress + - name: Upload surefire reports + # Kept on failure so an upstream break can be diagnosed after the fact: + # GitHub expires run logs after 90 days, and these reports carry the + # stack traces that say which service misbehaved. + if: failure() + uses: actions/upload-artifact@v4 + with: + name: surefire-reports + path: '**/target/surefire-reports/**' + retention-days: 30 diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index a0d31ee08a..340418cf68 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -30,8 +30,14 @@ jobs: with: distribution: 'oracle' java-version: ${{ matrix.java }} - - name: Build, test and integration test - run: mvn verify --no-transfer-progress + - name: Build and test (no integration tests) + # Integration tests are excluded here and run nightly instead, see + # nightly.yml. They depend on CATH, ECOD, RCSB, EBI and others being up + # and responsive, so running them on every pull request means a third + # party having a bad day blocks contributors, and a real regression + # cannot be told apart from the resulting noise. Master Build already + # excludes them for the same reason. + run: mvn verify -pl '!biojava-integrationtest' --no-transfer-progress # Note that 11 is not available in openjdk. So we need to do it with the Zulu distribution (see https://github.com/actions/setup-java) # When we drop 11, it will be safe to drop the copy-pasted workflow excerpt below @@ -54,5 +60,11 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} - - name: Build, test and integration test - run: mvn verify --no-transfer-progress + - name: Build and test (no integration tests) + # Integration tests are excluded here and run nightly instead, see + # nightly.yml. They depend on CATH, ECOD, RCSB, EBI and others being up + # and responsive, so running them on every pull request means a third + # party having a bad day blocks contributors, and a real regression + # cannot be told apart from the resulting noise. Master Build already + # excludes them for the same reason. + run: mvn verify -pl '!biojava-integrationtest' --no-transfer-progress diff --git a/CHANGELOG.md b/CHANGELOG.md index 74b52303b0..659ec4cdf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,29 @@ BioJava Changelog ----------------- +BioJava 7.2.6 +============================== +### Fixed +* Parsing of PDBx/mmCIF with empty database_PDB_rev.date + +BioJava 7.2.5 +============================== +### Fixed +* Fix NPE in Structure.toMMCIF() for some PDB entries (e.g. 2G10) +* Maven plugin duplication in main pom.xml +* Fixes for SonarQube S1155 +* Some library upgrades + +BioJava 7.2.4 +============================== +### Fixed +* Edge case in quaternary symmetry calculation #1120 + +BioJava 7.2.3 +============================== +### Fixed +* Don't use label_seq_id in mmCIF output for non-polymers #1116 + BioJava 7.2.2 ============================== ### Fixed diff --git a/README.md b/README.md index af128cd92c..5c4b8f6ed2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Welcome to ![Build](https://github.com/biojava/biojava/actions/workflows/master.yml/badge.svg) -[![Version](http://img.shields.io/badge/version-7.2.2-blue.svg?style=flat)](https://github.com/biojava/biojava/releases/tag/biojava-7.2.2) [![License](http://img.shields.io/badge/license-LGPL_2.1-blue.svg?style=flat)](https://github.com/biojava/biojava/blob/master/LICENSE) [![Join the chat at https://gitter.im/biojava/biojava](https://badges.gitter.im/biojava/biojava.svg)](https://gitter.im/biojava/biojava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Version](http://img.shields.io/badge/version-7.2.5-blue.svg?style=flat)](https://github.com/biojava/biojava/releases/tag/biojava-7.2.5) [![License](http://img.shields.io/badge/license-LGPL_2.1-blue.svg?style=flat)](https://github.com/biojava/biojava/blob/master/LICENSE) [![Join the chat at https://gitter.im/biojava/biojava](https://badges.gitter.im/biojava/biojava.svg)](https://gitter.im/biojava/biojava?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) BioJava is an open-source project dedicated to providing a Java framework for **processing biological data**. It provides analytical and statistical routines, parsers for common file formats, reference implementations of popular algorithms, and allows the manipulation of sequences and 3D structures. The goal of the biojava project is to facilitate rapid application development for bioinformatics. @@ -29,7 +29,7 @@ If you are using Maven you can add the BioJava repository by adding the followin org.biojava biojava-core - 7.2.2 + 7.2.5 diff --git a/biojava-aa-prop/pom.xml b/biojava-aa-prop/pom.xml index b2b642b661..22bc632906 100644 --- a/biojava-aa-prop/pom.xml +++ b/biojava-aa-prop/pom.xml @@ -2,7 +2,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT 4.0.0 biojava-aa-prop @@ -70,12 +70,12 @@ org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT org.biojava biojava-structure - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT diff --git a/biojava-alignment/pom.xml b/biojava-alignment/pom.xml index fe75c181c7..7ff8f29cf4 100644 --- a/biojava-alignment/pom.xml +++ b/biojava-alignment/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-alignment biojava-alignment @@ -47,7 +47,7 @@ org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile diff --git a/biojava-core/pom.xml b/biojava-core/pom.xml index 0836ffe54c..4826cf9d17 100644 --- a/biojava-core/pom.xml +++ b/biojava-core/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT 4.0.0 biojava-core diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/alignment/SimpleAlignedSequence.java b/biojava-core/src/main/java/org/biojava/nbio/core/alignment/SimpleAlignedSequence.java index 331480bbef..99b70c036d 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/alignment/SimpleAlignedSequence.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/alignment/SimpleAlignedSequence.java @@ -411,7 +411,7 @@ private void setLocation(List steps) { } // combine sublocations into 1 Location - if (sublocations.size() == 0) { + if (sublocations.isEmpty()) { location = null; } else if (sublocations.size() == 1) { location = sublocations.get(0); diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/GeneSequence.java b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/GeneSequence.java index f0f2662fea..638e4e68d9 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/GeneSequence.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/GeneSequence.java @@ -119,7 +119,7 @@ public void addIntronsUsingExons() throws Exception { if (intronAdded) { //going to assume introns are correct return; } - if (exonSequenceList.size() == 0) { + if (exonSequenceList.isEmpty()) { return; } ExonComparator exonComparator = new ExonComparator(); diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/location/InsdcParser.java b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/location/InsdcParser.java index e49bd22216..2d43a481bf 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/location/InsdcParser.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/location/InsdcParser.java @@ -260,7 +260,9 @@ private List parseLocationString(String string, int versus) { l.setPartialOn3prime(true); } - if (!(accession == null || "".equals(accession))) l.setAccession(new AccessionID(accession)); + if (accession != null && !"".equals(accession)) { + l.setAccession(new AccessionID(accession)); + } boundedLocationsCollection.add(l); diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/storage/SequenceAsStringHelper.java b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/storage/SequenceAsStringHelper.java index c2b02debee..4acd8969f1 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/storage/SequenceAsStringHelper.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/storage/SequenceAsStringHelper.java @@ -44,7 +44,7 @@ public class SequenceAsStringHelper { */ public String getSequenceAsString(List parsedCompounds, CompoundSet compoundSet, Integer bioBegin, Integer bioEnd, Strand strand) { // TODO Optimise/cache. - if(parsedCompounds.size() == 0) + if(parsedCompounds.isEmpty()) return ""; StringBuilder builder = new StringBuilder(); if (strand.equals(Strand.NEGATIVE)) { diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/util/Equals.java b/biojava-core/src/main/java/org/biojava/nbio/core/util/Equals.java index e8f78243ed..7e2c7128c9 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/util/Equals.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/util/Equals.java @@ -49,7 +49,13 @@ public static boolean equal(boolean one, boolean two) { * @see #classEqual(Object, Object) */ public static boolean equal(Object one, Object two) { - return one == null && two == null || !(one == null || two == null) && (one == two || one.equals(two)); + if (one == two) { + return true; + } + if (one == null || two == null) { + return false; + } + return one.equals(two); } /** @@ -84,6 +90,12 @@ public static boolean equal(Object one, Object two) { * equal at the class level */ public static boolean classEqual(Object one, Object two) { - return one == two || !(one == null || two == null) && one.getClass() == two.getClass(); + if (one == two) { + return true; + } + if (one == null || two == null) { + return false; + } + return one.getClass() == two.getClass(); } } diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/util/FileDownloadUtils.java b/biojava-core/src/main/java/org/biojava/nbio/core/util/FileDownloadUtils.java index 0b132b180e..5b8c656658 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/util/FileDownloadUtils.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/util/FileDownloadUtils.java @@ -21,9 +21,9 @@ */ package org.biojava.nbio.core.util; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -32,11 +32,15 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,10 +51,45 @@ public class FileDownloadUtils { private static final String HASH_EXT = ".hash"; private static final Logger logger = LoggerFactory.getLogger(FileDownloadUtils.class); + /** Buffer used when streaming a file through a {@link MessageDigest}. */ + private static final int DIGEST_BUFFER_SIZE = 64 * 1024; + + /** A bare hex digest, optionally followed by whitespace and a file name (the + * layout written by md5sum, sha1sum and friends). */ + private static final Pattern BARE_HEX_HASH = Pattern.compile("^([0-9a-fA-F]{32,128})(?:[\\s*].*)?$"); + + /** The BSD layout, e.g. MD5 (file.txt) = d41d8cd9.... */ + private static final Pattern BSD_HASH = Pattern.compile("^\\w+\\s*\\(.*\\)\\s*=\\s*([0-9a-fA-F]{32,128})$"); + public enum Hash{ MD5, SHA1, SHA256, UNKNOWN } + /** + * What to do with the ETag response header when no explicit hash + * URL is available. + *

+ * Some archives — notably files.wwpdb.org and + * files.rcsb.org — return the MD5 digest of the content as + * the bare ETag value, which lets us record a real checksum + * without a second request. Others (the EBI servers, for instance) return a + * <modification-time>-<size> form instead; because that + * always contains a -, it can never be mistaken for a hex digest. + * + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public enum ETagPolicy { + /** Never look at the ETag header. */ + IGNORE, + /** Record the ETag as a checksum when it is a bare hex digest + * of a length matching one of the supported algorithms. */ + USE_IF_HEX_DIGEST, + /** As {@link #USE_IF_HEX_DIGEST}, but log a warning when the header is + * missing or is not a usable digest. */ + REQUIRE + } + /** * Gets the file extension of a file, excluding '.'. * If the file name has no extension the file name is returned. @@ -95,44 +134,158 @@ public static void downloadFile(URL url, File destination) throws IOException { int maxTries = 10; int timeout = 60000; //60 sec - File tempFile = Files.createTempFile(getFilePrefix(destination), "." + getFileExtension(destination)).toFile(); + File tempFile = createTempFileFor(destination); - // Took following recipe from stackoverflow: - // http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java - // It seems to be the most efficient way to transfer a file - // See: http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html - ReadableByteChannel rbc = null; - FileOutputStream fos = null; - while (true) { - try { - URLConnection connection = prepareURLConnection(url.toString(), timeout); - connection.connect(); - InputStream inputStream = connection.getInputStream(); - - rbc = Channels.newChannel(inputStream); - fos = new FileOutputStream(tempFile); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - break; - } catch (SocketTimeoutException e) { - if (++count == maxTries) throw e; - } finally { - if (rbc != null) { - rbc.close(); - } - if (fos != null) { - fos.close(); + try { + while (true) { + try { + URLConnection connection = prepareURLConnection(url.toString(), timeout); + connection.connect(); + checkHttpStatus(connection); + try (InputStream inputStream = connection.getInputStream()) { + // Files.copy loops until end of stream. FileChannel.transferFrom(), used + // here previously, is not guaranteed to drain a socket-backed channel in + // a single call and could silently truncate a download. + Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + break; + } catch (SocketTimeoutException e) { + if (++count == maxTries) throw e; } } + + logger.debug("Copying temp file [{}] to final location [{}]", tempFile, destination); + Files.copy(tempFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); + } finally { + // on every path, including failure: the temp file used to leak whenever + // the download threw. + deleteQuietly(tempFile); } + } + + /** + * Downloads a file and writes its validation metadata in a single pass over a + * single connection. + *

+ * This is preferable to calling {@link #createValidationFiles(URL, File, URL, Hash)} + * followed by {@link #downloadFile(URL, File)}: those open separate connections, + * so the Content-Length recorded in the .size file + * comes from a different response than the bytes actually written. If the + * resource changes between the two requests, the cache entry is left + * permanently failing validation. + *

+ * The content is streamed to a temporary file and only moved into place once + * the declared length and (where available) the checksum have been confirmed, + * so a failed download never leaves a partial file at destination. + * + * @param url the remote file to download + * @param destination the local file to download into. Its parent directory must exist. + * @param hashURL URL of a file containing the expected hash. May be null. + * @param hash the hashing algorithm matching hashURL. Ignored when + * hashURL is null. + * @param eTagPolicy what to do with an ETag response header when + * hashURL is null. May be null, + * which is treated as {@link ETagPolicy#IGNORE}. + * @throws HttpStatusException if the server answered with a non-2xx status + * @throws IOException if the transfer failed, or the transferred content did not + * match the length or checksum the server declared + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static void downloadFileWithValidation(URL url, File destination, URL hashURL, Hash hash, + ETagPolicy eTagPolicy) throws IOException { + int timeout = 60000; //60 sec + ETagPolicy policy = eTagPolicy == null ? ETagPolicy.IGNORE : eTagPolicy; + + File tempFile = createTempFileFor(destination); + try { + URLConnection connection = prepareURLConnection(url.toString(), timeout); + connection.connect(); + checkHttpStatus(connection); - logger.debug("Copying temp file [{}] to final location [{}]", tempFile, destination); - Files.copy(tempFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); + long declaredSize = connection.getContentLengthLong(); + String eTag = connection.getHeaderField("ETag"); - // delete the tmp file - tempFile.delete(); + // Only digest when we have something to compare against; hashing every + // download would cost CPU for no benefit. + Hash eTagHash = policy == ETagPolicy.IGNORE ? Hash.UNKNOWN : hashFromETag(eTag); + if (policy == ETagPolicy.REQUIRE && eTagHash == Hash.UNKNOWN) { + logger.warn("ETag [{}] of {} is not a usable hex digest; no checksum will be recorded.", eTag, url); + } + + MessageDigest digest = eTagHash == Hash.UNKNOWN ? null : newDigest(eTagHash); + long written; + try (InputStream raw = connection.getInputStream(); + InputStream in = digest == null ? raw : new DigestInputStream(raw, digest)) { + written = Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + if (declaredSize >= 0 && written != declaredSize) { + throw new IOException(String.format( + "Incomplete download of %s: got %d bytes but the server declared %d.", + url, written, declaredSize)); + } + + String actualDigest = digest == null ? null : toHex(digest.digest()); + if (actualDigest != null && !actualDigest.equalsIgnoreCase(normalizeETag(eTag))) { + throw new IOException(String.format( + "Corrupt download of %s: %s of the content is %s but the server's ETag says %s.", + url, eTagHash, actualDigest, normalizeETag(eTag))); + } + + moveIntoPlace(tempFile, destination); + // Sidecars are written only once the content is known good, so a failed + // download can never leave validation metadata describing a file that is + // not there. + writeSizeFile(destination, written); + if (hashURL != null) { + if (hash == null || hash == Hash.UNKNOWN) { + throw new IllegalArgumentException("Hash URL given but algorithm is unknown"); + } + downloadFile(hashURL, hashFileFor(destination, hash)); + } else if (actualDigest != null) { + writeHashFile(destination, eTagHash, actualDigest); + } + } finally { + deleteQuietly(tempFile); + } } - + + /** + * Verifies that an HTTP connection returned a 2xx status. Connections using a + * non-HTTP protocol (file:, ftp:, ...) are left alone. + *

+ * Without this check a 404 error page is written into the cache as though it + * were the requested file — and because the .size sidecar is + * then taken from that same error response, {@link #validateFile(File)} would + * subsequently declare it valid. + * + * @param connection an already connected {@link URLConnection} + * @throws HttpStatusException if the status is outside the 2xx range + * @throws IOException if the status could not be read + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static void checkHttpStatus(URLConnection connection) throws IOException { + if (!(connection instanceof HttpURLConnection)) { + return; + } + HttpURLConnection http = (HttpURLConnection) connection; + int code = http.getResponseCode(); + if (code >= 200 && code < 300) { + return; + } + if (code == 301 || code == 302 || code == 307 || code == 308) { + // The JDK follows redirects automatically, but never across protocols, so + // an http -> https redirect surfaces here and is worth naming explicitly. + logger.warn("{} returned redirect {} to [{}], which was not followed " + + "(the JDK does not follow redirects that change protocol).", + connection.getURL(), code, http.getHeaderField("Location")); + } + throw new HttpStatusException(code, connection.getURL().toString(), http.getResponseMessage()); + } + /** * Creates validation files beside a file to be downloaded.
* Whenever possible, for a file.ext file, it creates @@ -146,9 +299,27 @@ public static void downloadFile(URL url, File destination) throws IOException { * @param hash The Hashing algorithm. Ignored if hashURL is null. */ public static void createValidationFiles(URL url, File localDestination, URL hashURL, Hash hash){ + createValidationFiles(url, localDestination, hashURL, hash, ETagPolicy.USE_IF_HEX_DIGEST); + } + + /** + * Creates validation files beside a file to be downloaded, with explicit control + * over how the ETag response header is treated. + * + * @param url the remote file URL to download + * @param localDestination the local file to download into + * @param hashURL the URL of the hash file to download. Can be null. + * @param hash The Hashing algorithm. Ignored if hashURL is null. + * @param eTagPolicy how to treat the ETag header when hashURL + * is null. May be null, treated as {@link ETagPolicy#IGNORE}. + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static void createValidationFiles(URL url, File localDestination, URL hashURL, Hash hash, + ETagPolicy eTagPolicy){ try { URLConnection resourceConnection = url.openConnection(); - createValidationFiles(resourceConnection, localDestination, hashURL, FileDownloadUtils.Hash.UNKNOWN); + createValidationFiles(resourceConnection, localDestination, hashURL, hash, eTagPolicy); } catch (IOException e) { logger.warn("could not open connection to resource file due to exception: {}", e.getMessage()); } @@ -169,31 +340,246 @@ public static void createValidationFiles(URL url, File localDestination, URL has * @since 7.0.0 */ public static void createValidationFiles(URLConnection resourceUrlConnection, File localDestination, URL hashURL, Hash hash){ + createValidationFiles(resourceUrlConnection, localDestination, hashURL, hash, ETagPolicy.USE_IF_HEX_DIGEST); + } + + /** + * Creates validation files beside a file to be downloaded, with explicit control + * over how the ETag response header is treated. + *

+ * Nothing is written when the connection reports a non-2xx status: previously an + * error page's Content-Length would be recorded as the expected + * size, so the cached error page then passed validation. + * + * @param resourceUrlConnection the remote file URLConnection to download + * @param localDestination the local file to download into + * @param hashURL the URL of the hash file to download. Can be null. + * @param hash The Hashing algorithm. Ignored if hashURL is null. + * @param eTagPolicy how to treat the ETag header when hashURL + * is null. May be null, treated as {@link ETagPolicy#IGNORE}. + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static void createValidationFiles(URLConnection resourceUrlConnection, File localDestination, URL hashURL, + Hash hash, ETagPolicy eTagPolicy){ + try { + checkHttpStatus(resourceUrlConnection); + } catch (IOException e) { + logger.warn("Not writing validation metadata for {}: {}", resourceUrlConnection.getURL(), e.getMessage()); + return; + } + long size = resourceUrlConnection.getContentLengthLong(); if(size == -1) { logger.debug("Could not find expected file size for resource {}. Size validation metadata file won't be available for this download.", resourceUrlConnection.getURL()); } else { logger.debug("Content-Length: {}", size); - File sizeFile = new File(localDestination.getParentFile(), localDestination.getName() + SIZE_EXT); - try (PrintStream sizePrintStream = new PrintStream(sizeFile)) { - sizePrintStream.print(size); - } catch (FileNotFoundException e) { - logger.warn("Could not write size validation metadata file due to exception: {}", e.getMessage()); - } + writeSizeFile(localDestination, size); } - - if(hashURL == null) + + if(hashURL == null) { + ETagPolicy policy = eTagPolicy == null ? ETagPolicy.IGNORE : eTagPolicy; + if (policy != ETagPolicy.IGNORE) { + String eTag = resourceUrlConnection.getHeaderField("ETag"); + Hash eTagHash = hashFromETag(eTag); + if (eTagHash == Hash.UNKNOWN) { + if (policy == ETagPolicy.REQUIRE) { + logger.warn("ETag [{}] of {} is not a usable hex digest; no checksum recorded.", + eTag, resourceUrlConnection.getURL()); + } + } else { + writeHashFile(localDestination, eTagHash, normalizeETag(eTag)); + } + } return; + } - if(hash == Hash.UNKNOWN) + if(hash == null || hash == Hash.UNKNOWN) throw new IllegalArgumentException("Hash URL given but algorithm is unknown"); try { - File hashFile = new File(localDestination.getParentFile(), String.format("%s%s_%s", localDestination.getName(), HASH_EXT, hash)); - downloadFile(hashURL, hashFile); + downloadFile(hashURL, hashFileFor(localDestination, hash)); } catch (IOException e) { logger.warn("Could not write validation hash file due to exception: {}", e.getMessage()); } } + + /** + * Determines which hashing algorithm an ETag header value + * represents, based on the length of the hex digest it contains. + *

+ * Only a value consisting solely of hex characters is considered. The + * <time>-<size> form used by nginx and Apache always + * contains a - and therefore never matches. + * + * @param eTagHeaderValue the raw header value, possibly quoted or weak-prefixed. + * May be null. + * @return the matching algorithm, or {@link Hash#UNKNOWN} if the value is not a + * hex digest of a recognised length + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static Hash hashFromETag(String eTagHeaderValue) { + String value = normalizeETag(eTagHeaderValue); + if (value == null || !value.matches("[0-9a-fA-F]+")) { + return Hash.UNKNOWN; + } + switch (value.length()) { + case 32: return Hash.MD5; + case 40: return Hash.SHA1; + case 64: return Hash.SHA256; + default: return Hash.UNKNOWN; + } + } + + /** + * Strips the weak-validator prefix and surrounding quotes from an + * ETag header value. + * + * @param eTagHeaderValue the raw header value. May be null. + * @return the bare value, or null if the input was null + * or blank + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static String normalizeETag(String eTagHeaderValue) { + if (eTagHeaderValue == null) { + return null; + } + String value = eTagHeaderValue.trim(); + if (value.startsWith("W/")) { + value = value.substring(2).trim(); + } + if (value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } + return value.isEmpty() ? null : value; + } + + /** + * Writes a <name>.hash_<ALGORITHM> sidecar file + * containing the given digest as bare lowercase hex. + * + * @param localDestination the file the digest describes + * @param hash the hashing algorithm + * @param hexDigest the digest, in hex + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static void writeHashFile(File localDestination, Hash hash, String hexDigest) { + if (hash == null || hash == Hash.UNKNOWN || hexDigest == null) { + return; + } + File hashFile = hashFileFor(localDestination, hash); + try (PrintStream out = new PrintStream(hashFile, StandardCharsets.UTF_8.name())) { + out.println(hexDigest.toLowerCase()); + } catch (IOException e) { + logger.warn("Could not write validation hash file due to exception: {}", e.getMessage()); + } + } + + /** + * Computes the digest of a file. + * + * @param file the file to digest + * @param hash the algorithm to use + * @return the digest as bare lowercase hex + * @throws IOException if the file could not be read + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static String computeHash(File file, Hash hash) throws IOException { + try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()), DIGEST_BUFFER_SIZE)) { + return computeHash(in, hash); + } + } + + /** + * Computes the digest of a stream. The stream is read to its end but not closed. + * + * @param in the stream to digest + * @param hash the algorithm to use + * @return the digest as bare lowercase hex + * @throws IOException if the stream could not be read + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static String computeHash(InputStream in, Hash hash) throws IOException { + MessageDigest digest = newDigest(hash); + byte[] buffer = new byte[DIGEST_BUFFER_SIZE]; + int read; + while ((read = in.read(buffer)) != -1) { + digest.update(buffer, 0, read); + } + return toHex(digest.digest()); + } + + /** + * Checks a file against an expected digest. + * + * @param file the file to check + * @param hash the algorithm to use + * @param expectedHex the expected digest, in hex; compared case-insensitively + * @return true if the digests match + * @throws IOException if the file could not be read + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static boolean verifyHash(File file, Hash hash, String expectedHex) throws IOException { + return expectedHex != null && expectedHex.trim().equalsIgnoreCase(computeHash(file, hash)); + } + + /** + * The JDK name of a hashing algorithm, which differs from the enum constant for + * the SHA variants. + * + * @param hash the algorithm + * @return the name to pass to {@link MessageDigest#getInstance(String)} + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + public static String getAlgorithmName(Hash hash) { + switch (hash) { + case MD5: return "MD5"; + case SHA1: return "SHA-1"; + case SHA256: return "SHA-256"; + default: throw new IllegalArgumentException("Hashing algorithm not known: " + hash); + } + } + + /** + * Reads the expected digest out of a .hash_XXXX sidecar file. + *

+ * The file may have been downloaded verbatim from a server, so several common + * layouts are accepted: a bare hex digest, the md5sum style + * <hex>  <filename>, and the BSD style + * MD5 (<filename>) = <hex>. + * + * @param hashFile the sidecar file + * @return the digest in hex, or null if nothing recognisable was found + */ + static String parseHashFile(File hashFile) { + try (Scanner scanner = new Scanner(hashFile, StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.isEmpty()) { + continue; + } + Matcher bare = BARE_HEX_HASH.matcher(line); + if (bare.matches()) { + return bare.group(1); + } + Matcher bsd = BSD_HASH.matcher(line); + if (bsd.matches()) { + return bsd.group(1); + } + return null; // first meaningful line was not a digest + } + } catch (IOException e) { + logger.warn("Could not read hash file [{}]: {}", hashFile, e.getMessage()); + } + return null; + } + /** * Validate a local file based on pre-existing metadata files for size and hash.
@@ -210,45 +596,154 @@ public static void createValidationFiles(URLConnection resourceUrlConnection, Fi * @since 7.0.0 */ public static boolean validateFile(File localFile) { - File sizeFile = new File(localFile.getParentFile(), localFile.getName() + SIZE_EXT); + // getParentFile() is null for a bare relative name such as new File("x.cif"), + // which used to make this method throw a NullPointerException. + File parent = localFile.getAbsoluteFile().getParentFile(); + if (parent == null) { + logger.debug("Cannot determine the parent directory of [{}]; nothing to validate against.", localFile); + return true; + } + + File sizeFile = new File(parent, localFile.getName() + SIZE_EXT); if(sizeFile.exists()) { try (Scanner scanner = new Scanner(sizeFile)) { - long expectedSize = scanner.nextLong(); - long actualSize = localFile.length(); - if (expectedSize != actualSize) { - logger.warn("File [{}] size ({}) does not match expected size ({}).", localFile, actualSize, expectedSize); - return false; + if (!scanner.hasNextLong()) { + // An empty or truncated .size file used to raise an unchecked + // NoSuchElementException that escaped the catch below. + logger.warn("Size metadata file [{}] is empty or malformed; skipping size validation.", sizeFile); + } else { + long expectedSize = scanner.nextLong(); + long actualSize = localFile.length(); + if (expectedSize != actualSize) { + logger.warn("File [{}] size ({}) does not match expected size ({}).", localFile, actualSize, expectedSize); + return false; + } } } catch (FileNotFoundException e) { logger.warn("could not validate size of file [{}] because no size metadata file exists.", localFile); } } - File[] hashFiles = localFile.getParentFile().listFiles(new FilenameFilter() { - final String hashPattern = String.format("%s%s_(%s|%s|%s)", localFile.getName(), HASH_EXT, Hash.MD5, Hash.SHA1, Hash.SHA256); + File[] hashFiles = parent.listFiles(new FilenameFilter() { + final String hashPattern = String.format("%s%s_(%s|%s|%s)", Pattern.quote(localFile.getName()), HASH_EXT, Hash.MD5, Hash.SHA1, Hash.SHA256); @Override public boolean accept(File dir, String name) { return name.matches(hashPattern); } }); - if(hashFiles.length > 0) { - File hashFile = hashFiles[0]; + // listFiles() returns null if the parent is not a directory or cannot be read. + if (hashFiles == null || hashFiles.length == 0) { + return true; + } + + // Verify against every sidecar present, not only the first one found. + for (File hashFile : hashFiles) { String name = hashFile.getName(); String algo = name.substring(name.lastIndexOf('_') + 1); - switch (Hash.valueOf(algo)) { - case MD5: - case SHA1: - case SHA256: - throw new UnsupportedOperationException("Not yet implemented"); - case UNKNOWN: - default: // No need. Already checked above + Hash hash; + try { + hash = Hash.valueOf(algo); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Hashing algorithm not known: " + algo, e); + } + if (hash == Hash.UNKNOWN) { throw new IllegalArgumentException("Hashing algorithm not known: " + algo); } + + String expected = parseHashFile(hashFile); + if (expected == null) { + // A sidecar we cannot read should not condemn an otherwise good download. + logger.warn("Could not read a digest from [{}]; skipping {} validation of [{}].", hashFile, hash, localFile); + continue; + } + try { + if (!verifyHash(localFile, hash, expected)) { + logger.warn("File [{}] {} does not match the expected digest {}.", localFile, hash, expected); + return false; + } + } catch (IOException e) { + logger.warn("Could not compute the {} of [{}]: {}", hash, localFile, e.getMessage()); + return false; + } } - + return true; } + /** + * The <name>.hash_<ALGORITHM> sidecar file for a + * downloaded file. + */ + private static File hashFileFor(File localDestination, Hash hash) { + return new File(localDestination.getAbsoluteFile().getParentFile(), + String.format("%s%s_%s", localDestination.getName(), HASH_EXT, hash)); + } + + /** + * Writes the <name>.size sidecar file. + */ + private static void writeSizeFile(File localDestination, long size) { + File sizeFile = new File(localDestination.getAbsoluteFile().getParentFile(), + localDestination.getName() + SIZE_EXT); + try (PrintStream sizePrintStream = new PrintStream(sizeFile, StandardCharsets.UTF_8.name())) { + sizePrintStream.print(size); + } catch (IOException e) { + logger.warn("Could not write size validation metadata file due to exception: {}", e.getMessage()); + } + } + + private static MessageDigest newDigest(Hash hash) { + try { + return MessageDigest.getInstance(getAlgorithmName(hash)); + } catch (NoSuchAlgorithmException e) { + // MD5, SHA-1 and SHA-256 are required of every Java platform. + throw new IllegalStateException("Required hashing algorithm is unavailable: " + hash, e); + } + } + + private static String toHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(Character.forDigit((b >> 4) & 0xF, 16)); + sb.append(Character.forDigit(b & 0xF, 16)); + } + return sb.toString(); + } + + /** + * Creates a temp file whose name is derived from the destination. + * {@link Files#createTempFile} rejects prefixes shorter than 3 characters, so + * short names are padded. + */ + private static File createTempFileFor(File destination) throws IOException { + String prefix = getFilePrefix(destination); + while (prefix.length() < 3) { + prefix = prefix + "_"; + } + return Files.createTempFile(prefix, "." + getFileExtension(destination)).toFile(); + } + + private static void moveIntoPlace(File tempFile, File destination) throws IOException { + try { + Files.move(tempFile.toPath(), destination.toPath(), + StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + // The temp directory is often on a different filesystem than the cache. + Files.copy(tempFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } + + private static void deleteQuietly(File file) { + if (file == null) { + return; + } + try { + Files.deleteIfExists(file.toPath()); + } catch (IOException e) { + logger.debug("Could not delete temporary file [{}]: {}", file, e.getMessage()); + } + } + /** * Converts path to Unix convention and adds a terminating slash if it was * omitted. diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/util/HttpStatusException.java b/biojava-core/src/main/java/org/biojava/nbio/core/util/HttpStatusException.java new file mode 100644 index 0000000000..c74c6ffb05 --- /dev/null +++ b/biojava-core/src/main/java/org/biojava/nbio/core/util/HttpStatusException.java @@ -0,0 +1,84 @@ +/** + * BioJava development code + * + * This code may be freely distributed and modified under the terms of the GNU + * Lesser General Public Licence. This should be distributed with the code. If + * you do not have a copy, see: + * + * http://www.gnu.org/copyleft/lesser.html + * + * Copyright for this code is held jointly by the individual authors. These + * should be listed in @author doc comments. + * + * For more information on the BioJava project and its aims, or to join the + * biojava-l mailing list, visit the home page at: + * + * http://www.biojava.org/ + */ +package org.biojava.nbio.core.util; + +import java.io.IOException; + +/** + * Signals that an HTTP request completed but returned a status code outside the + * 2xx range. + *

+ * This exists so that callers can tell apart the two very different reasons a + * download can fail: + *

    + *
  • The resource does not exist ({@link #isNotFound()}, i.e. HTTP 404 or + * 410). For a caller that tries several mirrors or several alternative data + * sources in turn, this simply means "not here, try the next one".
  • + *
  • Anything else — a server error, a redirect that was not + * followed, an authentication failure. These usually mean the whole operation + * should be abandoned rather than silently treated as "no data available".
  • + *
+ * Without a distinct exception type the only way to tell these apart is by + * parsing the message of a plain {@link IOException}, which is brittle. + * + * @author Amr ALHOSSARY + * @since 7.3.0 + */ +public class HttpStatusException extends IOException { + + private static final long serialVersionUID = 1L; + + private final int statusCode; + private final String url; + + /** + * @param statusCode the HTTP status code returned by the server + * @param url the URL that was requested + * @param responseMessage the HTTP reason phrase, may be null + */ + public HttpStatusException(int statusCode, String url, String responseMessage) { + super(String.format("HTTP %d%s for %s", statusCode, + responseMessage == null || responseMessage.isEmpty() ? "" : " " + responseMessage, url)); + this.statusCode = statusCode; + this.url = url; + } + + /** + * @return the HTTP status code returned by the server + */ + public int getStatusCode() { + return statusCode; + } + + /** + * @return the URL that was requested + */ + public String getUrl() { + return url; + } + + /** + * Whether the status indicates that the resource is simply not there, as + * opposed to a transport or server problem. + * + * @return true for HTTP 404 (Not Found) and 410 (Gone) + */ + public boolean isNotFound() { + return statusCode == 404 || statusCode == 410; + } +} diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/util/PrettyXMLWriter.java b/biojava-core/src/main/java/org/biojava/nbio/core/util/PrettyXMLWriter.java index 437085866f..6e4a7db77c 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/util/PrettyXMLWriter.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/util/PrettyXMLWriter.java @@ -72,7 +72,7 @@ public void declareNamespace(String nsURI, String prefixHint) private void handleDeclaredNamespaces() throws IOException { - if (namespacesDeclared.size() == 0) { + if (namespacesDeclared.isEmpty()) { for (Iterator nsi = namespacesDeclared.iterator(); nsi.hasNext(); ) { String nsURI = nsi.next(); if (!namespacePrefixes.containsKey(nsURI)) { diff --git a/biojava-core/src/test/java/org/biojava/nbio/core/sequence/loader/GenbankProxySequenceReaderTest.java b/biojava-core/src/test/java/org/biojava/nbio/core/sequence/loader/GenbankProxySequenceReaderTest.java index 6883637a49..0e8f7f41f5 100644 --- a/biojava-core/src/test/java/org/biojava/nbio/core/sequence/loader/GenbankProxySequenceReaderTest.java +++ b/biojava-core/src/test/java/org/biojava/nbio/core/sequence/loader/GenbankProxySequenceReaderTest.java @@ -162,7 +162,7 @@ so it should be done here (manualy). logger.info("taxonomy name '{}'", taxonName); Assert.assertNotNull(taxonName); - if (seq.getFeaturesByType("CDS").size() > 0) { + if (!seq.getFeaturesByType("CDS").isEmpty()) { FeatureInterface, AminoAcidCompound> CDS = seq.getFeaturesByType("CDS").get(0); logger.info("CDS: {}", CDS); String codedBy = CDS.getQualifiers().get("coded_by").get(0).getValue(); diff --git a/biojava-core/src/test/java/org/biojava/nbio/core/util/FileDownloadUtilsTest.java b/biojava-core/src/test/java/org/biojava/nbio/core/util/FileDownloadUtilsTest.java index 201ad88e48..4a3cae7fb7 100644 --- a/biojava-core/src/test/java/org/biojava/nbio/core/util/FileDownloadUtilsTest.java +++ b/biojava-core/src/test/java/org/biojava/nbio/core/util/FileDownloadUtilsTest.java @@ -4,6 +4,7 @@ import static org.biojava.nbio.core.util.FileDownloadUtils.getFilePrefix; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,6 +12,7 @@ import java.io.IOException; import java.io.PrintStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import org.junit.jupiter.api.Nested; @@ -187,17 +189,248 @@ void testValidationFiles() throws IOException{ assertTrue(sizeFile.exists(), "couldn't create size file"); assertTrue(FileDownloadUtils.validateFile(destFile), "file not detected to be invalid although there is correct size validation file"); + // files.wwpdb.org returns the content MD5 as the ETag, so the default + // ETag policy records a real checksum without a separate hash URL. + assertTrue(hashFile.exists(), "no hash file was derived from the ETag"); + assertTrue(FileDownloadUtils.validateFile(destFile), "correctly downloaded file failed hash validation"); + PrintStream temp2 = new PrintStream(hashFile); - temp2.print("ABCD"); // some wrong hash value + temp2.print("ABCD"); // not a digest of any supported length temp2.close(); - //This is not yet implemented. I am using this test for documentation purpose. - assertThrows(UnsupportedOperationException.class, - () -> FileDownloadUtils.validateFile(destFile), + // An unreadable sidecar must not condemn an otherwise good download. + assertTrue(FileDownloadUtils.validateFile(destFile), + "a malformed hash file should be ignored, not treated as a mismatch"); + + PrintStream temp3 = new PrintStream(hashFile); + temp3.print("00000000000000000000000000000000"); // well-formed but wrong MD5 + temp3.close(); + assertFalse(FileDownloadUtils.validateFile(destFile), "file not detected to be invalid although hash value is wrong."); - + System.out.println("Just ignore the previous warning. It is expected."); + destFile.delete(); sizeFile.delete(); hashFile.delete(); } } + + @Nested + class HttpStatus { + + @Test + void notFoundThrowsAndLeavesNothingBehind() throws IOException { + // A path that is guaranteed absent from the wwPDB archive. + URL missing = new URL("https://files.wwpdb.org/pub/pdb/data/structures/divided/mmCIF/zz/zzzz.cif.gz"); + File dest = new File(System.getProperty("java.io.tmpdir"), "bj-missing.cif.gz"); + File sizeFile = new File(dest.getParentFile(), dest.getName() + ".size"); + dest.delete(); + sizeFile.delete(); + + HttpStatusException e = assertThrows(HttpStatusException.class, + () -> FileDownloadUtils.downloadFile(missing, dest)); + assertEquals(404, e.getStatusCode()); + assertTrue(e.isNotFound()); + assertFalse(dest.exists(), "a 404 body must never be written to the destination"); + + // ... and no validation metadata may be recorded for it either, or the + // cached error page would later pass validation. + FileDownloadUtils.createValidationFiles(missing, dest, null, FileDownloadUtils.Hash.UNKNOWN); + assertFalse(sizeFile.exists(), "no size file should be written for a 404 response"); + } + } + + @Nested + class Hashing { + + private File writeTemp(String name, byte[] content) throws IOException { + File f = new File(System.getProperty("java.io.tmpdir"), name); + Files.write(f.toPath(), content); + f.deleteOnExit(); + return f; + } + + @Test + void digestsOfEmptyFileMatchKnownValues() throws IOException { + File empty = writeTemp("bj-empty.bin", new byte[0]); + assertEquals("d41d8cd98f00b204e9800998ecf8427e", + FileDownloadUtils.computeHash(empty, FileDownloadUtils.Hash.MD5)); + assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", + FileDownloadUtils.computeHash(empty, FileDownloadUtils.Hash.SHA1)); + assertEquals("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + FileDownloadUtils.computeHash(empty, FileDownloadUtils.Hash.SHA256)); + } + + @Test + void digestOfKnownContent() throws IOException { + File abc = writeTemp("bj-abc.bin", "abc".getBytes(StandardCharsets.UTF_8)); + assertEquals("900150983cd24fb0d6963f7d28e17f72", + FileDownloadUtils.computeHash(abc, FileDownloadUtils.Hash.MD5)); + assertTrue(FileDownloadUtils.verifyHash(abc, FileDownloadUtils.Hash.MD5, + "900150983CD24FB0D6963F7D28E17F72"), "comparison should be case-insensitive"); + assertFalse(FileDownloadUtils.verifyHash(abc, FileDownloadUtils.Hash.MD5, + "00000000000000000000000000000000")); + } + + @Test + void algorithmNames() { + assertEquals("MD5", FileDownloadUtils.getAlgorithmName(FileDownloadUtils.Hash.MD5)); + assertEquals("SHA-1", FileDownloadUtils.getAlgorithmName(FileDownloadUtils.Hash.SHA1)); + assertEquals("SHA-256", FileDownloadUtils.getAlgorithmName(FileDownloadUtils.Hash.SHA256)); + assertThrows(IllegalArgumentException.class, + () -> FileDownloadUtils.getAlgorithmName(FileDownloadUtils.Hash.UNKNOWN)); + } + } + + @Nested + class HashFileParsing { + + private static final String MD5 = "900150983cd24fb0d6963f7d28e17f72"; + + private String parse(String content) throws IOException { + File f = new File(System.getProperty("java.io.tmpdir"), "bj-hashfile.txt"); + Files.write(f.toPath(), content.getBytes(StandardCharsets.UTF_8)); + f.deleteOnExit(); + return FileDownloadUtils.parseHashFile(f); + } + + @Test + void bareHex() throws IOException { + assertEquals(MD5, parse(MD5)); + assertEquals(MD5, parse(MD5 + "\n")); + } + + @Test + void uppercaseHexIsKeptVerbatim() throws IOException { + assertEquals(MD5.toUpperCase(), parse(MD5.toUpperCase())); + } + + @Test + void coreutilsLayouts() throws IOException { + assertEquals(MD5, parse(MD5 + " somefile.cif.gz\n")); + assertEquals(MD5, parse(MD5 + " *somefile.cif.gz\n")); + } + + @Test + void bsdLayout() throws IOException { + assertEquals(MD5, parse("MD5 (somefile.cif.gz) = " + MD5 + "\n")); + } + + @Test + void blankLeadingLinesAreSkipped() throws IOException { + assertEquals(MD5, parse("\n \n" + MD5 + "\n")); + } + + @Test + void garbageYieldsNull() throws IOException { + assertNull(parse("not a hash at all\n")); + assertNull(parse("ABCD\n")); + assertNull(parse("")); + } + } + + @Nested + class ETagParsing { + + @Test + void wwpdbStyleMd5IsRecognised() { + assertEquals(FileDownloadUtils.Hash.MD5, + FileDownloadUtils.hashFromETag("\"f99fb9d964e1e1c22f2ea559ac5745cf\"")); + assertEquals("f99fb9d964e1e1c22f2ea559ac5745cf", + FileDownloadUtils.normalizeETag("\"f99fb9d964e1e1c22f2ea559ac5745cf\"")); + } + + @Test + void sha1AndSha256LengthsAreRecognised() { + assertEquals(FileDownloadUtils.Hash.SHA1, + FileDownloadUtils.hashFromETag("da39a3ee5e6b4b0d3255bfef95601890afd80709")); + assertEquals(FileDownloadUtils.Hash.SHA256, + FileDownloadUtils.hashFromETag( + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + } + + @Test + void ebiStyleTimeSizeETagIsNotMistakenForADigest() { + // nginx and Apache emit -; the dash keeps it out of + // the hex-only pattern, which is what stops us recording a bogus checksum. + assertEquals(FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.hashFromETag("\"67910788-1009e0\"")); + assertEquals(FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.hashFromETag("\"14c95dd-51eedb9922b40\"")); + } + + @Test + void weakAndMissingETags() { + assertEquals(FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.hashFromETag("W/\"abc\"")); + assertEquals(FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.hashFromETag(null)); + assertEquals(FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.hashFromETag(" ")); + assertNull(FileDownloadUtils.normalizeETag(null)); + assertNull(FileDownloadUtils.normalizeETag("\"\"")); + } + } + + @Nested + class ValidateFile { + + @Test + void bareRelativeNameDoesNotThrow() { + // getParentFile() is null here; this used to be a NullPointerException. + assertTrue(FileDownloadUtils.validateFile(new File("no-such-file-in-cwd.cif"))); + } + + @Test + void emptySizeFileIsIgnoredRatherThanThrowing() throws IOException { + File dir = Files.createTempDirectory("bj-validate").toFile(); + try { + File data = new File(dir, "data.bin"); + Files.write(data.toPath(), "hello".getBytes(StandardCharsets.UTF_8)); + Files.write(new File(dir, "data.bin.size").toPath(), new byte[0]); + assertTrue(FileDownloadUtils.validateFile(data)); + } finally { + FileDownloadUtils.deleteDirectory(dir.toPath()); + } + } + + @Test + void sizeMismatchIsDetected() throws IOException { + File dir = Files.createTempDirectory("bj-validate").toFile(); + try { + File data = new File(dir, "data.bin"); + Files.write(data.toPath(), "hello".getBytes(StandardCharsets.UTF_8)); + Files.write(new File(dir, "data.bin.size").toPath(), "99".getBytes(StandardCharsets.UTF_8)); + assertFalse(FileDownloadUtils.validateFile(data)); + } finally { + FileDownloadUtils.deleteDirectory(dir.toPath()); + } + } + + @Test + void everyHashSidecarIsChecked() throws IOException { + File dir = Files.createTempDirectory("bj-validate").toFile(); + try { + File data = new File(dir, "data.bin"); + Files.write(data.toPath(), "abc".getBytes(StandardCharsets.UTF_8)); + // correct MD5, wrong SHA1: the second sidecar must still be caught + Files.write(new File(dir, "data.bin.hash_MD5").toPath(), + "900150983cd24fb0d6963f7d28e17f72".getBytes(StandardCharsets.UTF_8)); + Files.write(new File(dir, "data.bin.hash_SHA1").toPath(), + "0000000000000000000000000000000000000000".getBytes(StandardCharsets.UTF_8)); + assertFalse(FileDownloadUtils.validateFile(data)); + } finally { + FileDownloadUtils.deleteDirectory(dir.toPath()); + } + } + + @Test + void writeHashFileRoundTrip() throws IOException { + File dir = Files.createTempDirectory("bj-validate").toFile(); + try { + File data = new File(dir, "data.bin"); + Files.write(data.toPath(), "abc".getBytes(StandardCharsets.UTF_8)); + FileDownloadUtils.writeHashFile(data, FileDownloadUtils.Hash.MD5, + FileDownloadUtils.computeHash(data, FileDownloadUtils.Hash.MD5)); + assertTrue(new File(dir, "data.bin.hash_MD5").exists()); + assertTrue(FileDownloadUtils.validateFile(data)); + } finally { + FileDownloadUtils.deleteDirectory(dir.toPath()); + } + } + } } diff --git a/biojava-genome/pom.xml b/biojava-genome/pom.xml index 7729a6e9e3..e469de412c 100644 --- a/biojava-genome/pom.xml +++ b/biojava-genome/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT 4.0.0 biojava-genome @@ -63,38 +63,25 @@ compile
- junit - junit - test + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-params org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile org.biojava biojava-alignment - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile - - junit-addons - junit-addons - 1.4 - test - - - xerces - xmlParserAPIs - - - xerces - xercesImpl - - - org.slf4j diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/GeneFeatureHelper.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/GeneFeatureHelper.java index c9786b3ad0..b417c4608d 100644 --- a/biojava-genome/src/main/java/org/biojava/nbio/genome/GeneFeatureHelper.java +++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/GeneFeatureHelper.java @@ -36,7 +36,7 @@ /** * - * @author Scooter Willis + * @author Scooter Willis */ public class GeneFeatureHelper { @@ -418,7 +418,7 @@ static public void addGmodGFF3GeneFeatures(Map chrom String startCodonName = ""; String stopCodonName = ""; FeatureList startCodonList = mRNAChildren.selectByType("five_prime_UTR"); - if (startCodonList != null && startCodonList.size() > 0) { + if (startCodonList != null && !startCodonList.isEmpty()) { startCodon = startCodonList.get(0); if (strand == Strand.NEGATIVE) { startCodonBegin = startCodon.location().bioEnd(); @@ -430,7 +430,7 @@ static public void addGmodGFF3GeneFeatures(Map chrom FeatureList stopCodonList = mRNAChildren.selectByType("three_prime_UTR"); - if (stopCodonList != null && stopCodonList.size() > 0) { + if (stopCodonList != null && !stopCodonList.isEmpty()) { stopCodon = stopCodonList.get(0); if (strand == Strand.NEGATIVE) { stopCodonEnd = stopCodon.location().bioStart(); @@ -577,7 +577,7 @@ static public void addGlimmerGFF3GeneFeatures(Map ch String startCodonName = ""; String stopCodonName = ""; FeatureList startCodonList = gene.selectByAttribute("Note", "initial-exon"); - if (startCodonList != null && startCodonList.size() > 0) { + if (startCodonList != null && !startCodonList.isEmpty()) { startCodon = startCodonList.get(0); if (strand == Strand.NEGATIVE) { startCodonBegin = startCodon.location().bioEnd(); @@ -589,7 +589,7 @@ static public void addGlimmerGFF3GeneFeatures(Map ch FeatureList stopCodonList = gene.selectByAttribute("Note", "final-exon"); - if (stopCodonList != null && stopCodonList.size() > 0) { + if (stopCodonList != null && !stopCodonList.isEmpty()) { stopCodon = stopCodonList.get(0); if (strand == Strand.NEGATIVE) { stopCodonEnd = stopCodon.location().bioStart(); @@ -723,7 +723,7 @@ static public void addGeneMarkGTFGeneFeatures(Map ch String startCodonName = ""; String stopCodonName = ""; FeatureList startCodonList = transcriptFeature.selectByType("start_codon"); - if (startCodonList != null && startCodonList.size() > 0) { + if (startCodonList != null && !startCodonList.isEmpty()) { startCodon = startCodonList.get(0); if (strand == Strand.POSITIVE) { startCodonBegin = startCodon.location().bioStart(); @@ -735,7 +735,7 @@ static public void addGeneMarkGTFGeneFeatures(Map ch FeatureList stopCodonList = transcriptFeature.selectByType("stop_codon"); - if (stopCodonList != null && stopCodonList.size() > 0) { + if (stopCodonList != null && !stopCodonList.isEmpty()) { stopCodon = stopCodonList.get(0); if (strand == Strand.POSITIVE) { stopCodonEnd = stopCodon.location().bioEnd(); diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/homology/GFF3FromUniprotBlastHits.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/homology/GFF3FromUniprotBlastHits.java index 67e22bd992..1547aba2d9 100644 --- a/biojava-genome/src/main/java/org/biojava/nbio/genome/homology/GFF3FromUniprotBlastHits.java +++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/homology/GFF3FromUniprotBlastHits.java @@ -46,7 +46,7 @@ /** * - * @author Scooter Willis + * @author Scooter Willis * @author Mark Chapman */ public class GFF3FromUniprotBlastHits { @@ -163,7 +163,7 @@ PairwiseSequenceAlignerType.LOCAL, new SimpleGapPenalty(), String notes = ""; if (featureKeyWords != null) { List keyWords = featureKeyWords.getKeyWords(); - if (keyWords.size() > 0) { + if (!keyWords.isEmpty()) { notes = ";Note="; for (String note : keyWords) { if ("Complete proteome".equals(note)) { @@ -187,7 +187,7 @@ PairwiseSequenceAlignerType.LOCAL, new SimpleGapPenalty(), List cazyList = databaseReferenceHashMap.get("CAZy"); List goList = databaseReferenceHashMap.get("GO"); List eccList = databaseReferenceHashMap.get("BRENDA"); - if (pfamList != null && pfamList.size() > 0) { + if (pfamList != null && !pfamList.isEmpty()) { if (notes.length() == 0) { notes = ";Note="; } @@ -197,7 +197,7 @@ PairwiseSequenceAlignerType.LOCAL, new SimpleGapPenalty(), } } - if (cazyList != null && cazyList.size() > 0) { + if (cazyList != null && !cazyList.isEmpty()) { if (notes.length() == 0) { notes = ";Note="; } @@ -208,7 +208,7 @@ PairwiseSequenceAlignerType.LOCAL, new SimpleGapPenalty(), } } - if (eccList != null && eccList.size() > 0) { + if (eccList != null && !eccList.isEmpty()) { if (notes.length() == 0) { notes = ";Note="; } @@ -221,8 +221,8 @@ PairwiseSequenceAlignerType.LOCAL, new SimpleGapPenalty(), } } - if (goList != null && goList.size() > 0) { - if (notes.length() == 0) { + if (goList != null && !goList.isEmpty()) { + if (notes.isEmpty()) { notes = ";Note="; } for (DBReferenceInfo note : goList) { diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/GFF3Writer.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/GFF3Writer.java index 88af970928..0f8bd97884 100644 --- a/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/GFF3Writer.java +++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/GFF3Writer.java @@ -31,7 +31,7 @@ /** * - * @author Scooter Willis + * @author Scooter Willis */ public class GFF3Writer { @@ -122,7 +122,7 @@ public void write(OutputStream outputStream, Map chr private String getGFF3Note(List notesList) { String notes = ""; - if (notesList.size() > 0) { + if (!notesList.isEmpty()) { notes = ";Note="; int noteindex = 1; for (String note : notesList) { diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/Location.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/Location.java index 4163be2b56..a28f1019fb 100644 --- a/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/Location.java +++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/parsers/gff/Location.java @@ -135,7 +135,7 @@ public static Location fromBio( int start, int end, char strand ) int s= start - 1; int e= end; - if( !( strand == '-' || strand == '+' || strand == '.' )) + if( strand != '-' && strand != '+' && strand != '.' ) { throw new IllegalArgumentException( "Strand must be '+', '-', or '.'" ); } @@ -166,7 +166,7 @@ public static Location fromBioExt( int start, int length, char strand, int total int s= start; int e= s + length; - if( !( strand == '-' || strand == '+' || strand == '.' )) + if( strand != '-' && strand != '+' && strand != '.' ) { throw new IllegalArgumentException( "Strand must be '+', '-', or '.'" ); } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/FeatureListTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/FeatureListTest.java index 6e1cae5d8b..8c50e19bc2 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/FeatureListTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/FeatureListTest.java @@ -26,26 +26,26 @@ import org.biojava.nbio.genome.parsers.gff.Feature; import org.biojava.nbio.genome.parsers.gff.FeatureList; import org.biojava.nbio.genome.parsers.gff.Location; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * @author mckeee1 * */ -public class FeatureListTest { +class FeatureListTest { @Test - public void testAddIndex() throws Exception + void testAddIndex() throws Exception { FeatureList fl = new FeatureList(); fl.add(new Feature("seqname", "source", "type", new Location(1, 2), (double)0, 0, "gene_id \"gene_id_1\"; transcript_id \"transcript_id_1\";")); fl.addIndex("transcript_id"); - Assert.assertEquals(1, fl.selectByAttribute("transcript_id").size()); + Assertions.assertEquals(1, fl.selectByAttribute("transcript_id").size()); FeatureList f2 = new FeatureList(); f2.addIndex("transcript_id"); f2.add(new Feature("seqname", "source", "type", new Location(1, 2), (double)0, 0, "gene_id \"gene_id_1\"; transcript_id \"transcript_id_1\";")); - Assert.assertEquals(1, f2.selectByAttribute("transcript_id").size()); + Assertions.assertEquals(1, f2.selectByAttribute("transcript_id").size()); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/GeneFeatureHelperTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/GeneFeatureHelperTest.java index 3c81c50916..53ab7c20cb 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/GeneFeatureHelperTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/GeneFeatureHelperTest.java @@ -20,7 +20,6 @@ */ package org.biojava.nbio.genome; -import junitx.framework.FileAssert; import org.biojava.nbio.genome.parsers.gff.FeatureList; import org.biojava.nbio.genome.parsers.gff.GFF3Reader; import org.biojava.nbio.genome.parsers.gff.GFF3Writer; @@ -28,9 +27,10 @@ import org.biojava.nbio.core.sequence.GeneSequence; import org.biojava.nbio.core.sequence.ProteinSequence; import org.biojava.nbio.core.sequence.io.FastaWriterHelper; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,20 +45,20 @@ * * @author Scooter Willis */ -public class GeneFeatureHelperTest { +class GeneFeatureHelperTest { private static final Logger logger = LoggerFactory.getLogger(GeneFeatureHelperTest.class); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } @Test - public void testZeroLocation() throws Exception { + void testZeroLocation() throws Exception { @SuppressWarnings("unused") FeatureList listGenes = GFF3Reader.read("src/test/resources/amphimedon.gff3"); @@ -71,7 +71,7 @@ public void testZeroLocation() throws Exception { */ @Test - public void testLoadFastaAddGeneFeaturesFromUpperCaseExonFastaFile() throws Exception { + void testLoadFastaAddGeneFeaturesFromUpperCaseExonFastaFile() throws Exception { // logger.info("loadFastaAddGeneFeaturesFromUpperCaseExonFastaFile"); File fastaSequenceFile = new File("src/test/resources/volvox_all.fna"); File uppercaseFastaFile = new File("src/test/resources/volvox_all_genes_exon_uppercase.fna"); @@ -93,15 +93,17 @@ public void testLoadFastaAddGeneFeaturesFromUpperCaseExonFastaFile() throws Exce * Test of outputFastaSequenceLengthGFF3 method, of class GeneFeatureHelper. */ @Test - public void testOutputFastaSequenceLengthGFF3() throws Exception { + void testOutputFastaSequenceLengthGFF3() throws Exception { // logger.info("outputFastaSequenceLengthGFF3"); File fastaSequenceFile = new File("src/test/resources/volvox_all.fna"); File gffFile = Files.createTempFile("volvox_length","gff3").toFile(); gffFile.deleteOnExit(); GeneFeatureHelper.outputFastaSequenceLengthGFF3(fastaSequenceFile, gffFile); - FileAssert.assertEquals("volvox_length.gff3 and volvox_length_output.gff3 are not equal", gffFile, - new File("src/test/resources/volvox_length_reference.gff3")); + Assertions.assertEquals( + Files.readString(new File("src/test/resources/volvox_length_reference.gff3").toPath()), + Files.readString(gffFile.toPath()), + "volvox_length.gff3 and volvox_length_output.gff3 are not equal"); } @@ -112,7 +114,7 @@ public void testOutputFastaSequenceLengthGFF3() throws Exception { */ @Test - public void testAddGFF3Note() throws Exception { + void testAddGFF3Note() throws Exception { Map chromosomeSequenceList = GeneFeatureHelper .loadFastaAddGeneFeaturesFromGmodGFF3(new File("src/test/resources/volvox_all.fna"), new File( "src/test/resources/volvox.gff3"), false); @@ -128,7 +130,7 @@ public void testAddGFF3Note() throws Exception { * output. */ @Test - public void testGetProteinSequences() throws Exception { + void testGetProteinSequences() throws Exception { Map chromosomeSequenceList = GeneFeatureHelper .loadFastaAddGeneFeaturesFromGmodGFF3(new File("src/test/resources/volvox_all.fna"), new File( "src/test/resources/volvox.gff3"), false); @@ -140,15 +142,17 @@ public void testGetProteinSequences() throws Exception { File tmp = Files.createTempFile("volvox_all","faa").toFile(); tmp.deleteOnExit(); FastaWriterHelper.writeProteinSequence(tmp, proteinSequenceList.values()); - FileAssert.assertEquals("volvox_all_reference.faa and volvox_all.faa are not equal", new File( - "src/test/resources/volvox_all_reference.faa"), tmp); + Assertions.assertEquals( + Files.readString(new File("src/test/resources/volvox_all_reference.faa").toPath()), + Files.readString(tmp.toPath()), + "volvox_all_reference.faa and volvox_all.faa are not equal"); } /** * Test of getGeneSequences method, of class GeneFeatureHelper. */ @Test - public void testGetGeneSequences() throws Exception { + void testGetGeneSequences() throws Exception { // logger.info("getGeneSequences"); Map chromosomeSequenceList = GeneFeatureHelper .loadFastaAddGeneFeaturesFromGmodGFF3(new File("src/test/resources/volvox_all.fna"), new File( diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestChromosomeMappingTools.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestChromosomeMappingTools.java index 9ddd43357a..b17539a0ac 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestChromosomeMappingTools.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestChromosomeMappingTools.java @@ -21,21 +21,20 @@ package org.biojava.nbio.genome; import org.biojava.nbio.genome.util.ChromosomeMappingTools; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.assertEquals; - /** * Created by Yana Valasatava on 8/14/17. */ -public class TestChromosomeMappingTools { +class TestChromosomeMappingTools { @Test - public void testGetCDSLengthForward() { + void testGetCDSLengthForward() { List exonStarts = new ArrayList<>(Arrays.asList(10, 30, 50, 70)); List exonEnds = new ArrayList<>(Arrays.asList(20, 40, 60, 80)); @@ -46,11 +45,11 @@ public void testGetCDSLengthForward() { ChromosomeMappingTools.setCoordinateSystem(0); int cdsTest = ChromosomeMappingTools.getCDSLengthForward(exonStarts, exonEnds, cdsStart, cdsEnd); - assertEquals(cdsDesired, cdsTest); + Assertions.assertEquals(cdsDesired, cdsTest); } @Test - public void testGetCDSLengthReverseAsc() { + void testGetCDSLengthReverseAsc() { List exonStarts = new ArrayList<>(Arrays.asList(10, 50, 70)); List exonEnds = new ArrayList<>(Arrays.asList(20, 60, 80)); @@ -61,11 +60,11 @@ public void testGetCDSLengthReverseAsc() { ChromosomeMappingTools.setCoordinateSystem(0); int cdsTest = ChromosomeMappingTools.getCDSLengthReverse(exonStarts, exonEnds, cdsStart, cdsEnd); - assertEquals(cdsDesired, cdsTest); + Assertions.assertEquals(cdsDesired, cdsTest); } @Test - public void testGetCDSLengthReverseDesc() { + void testGetCDSLengthReverseDesc() { List exonStarts = new ArrayList<>(Arrays.asList(70, 50, 10)); List exonEnds = new ArrayList<>(Arrays.asList(80, 60, 20)); @@ -76,6 +75,6 @@ public void testGetCDSLengthReverseDesc() { ChromosomeMappingTools.setCoordinateSystem(0); int cdsTest = ChromosomeMappingTools.getCDSLengthReverse(exonStarts, exonEnds, cdsStart, cdsEnd); - assertEquals(cdsDesired, cdsTest); + Assertions.assertEquals(cdsDesired, cdsTest); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java index 4999cfa6fb..257b88e3e1 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java @@ -22,8 +22,8 @@ import com.google.common.collect.Range; import org.biojava.nbio.genome.util.ChromosomeMappingTools; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; @@ -32,10 +32,10 @@ /** * Created by andreas on 7/19/16. */ -public class TestGenomeMapping { +class TestGenomeMapping { @Test - public void testGenomeMappingToolGetCDSRanges(){ + void testGenomeMappingToolGetCDSRanges(){ List lst1 = new ArrayList<>(Arrays.asList( 86346823, 86352858, 86354529)); List lst2 = new ArrayList<>(Arrays.asList(86348878, 86352984, 86354692)); @@ -45,21 +45,21 @@ public void testGenomeMappingToolGetCDSRanges(){ List> result = ChromosomeMappingTools.getCDSRegions(lst1,lst2,cdsStart,cdsEnd); // makes sure the first list does not get changed; - Assert.assertEquals(86346823, (int) lst1.get(0)); + Assertions.assertEquals(86346823, (int) lst1.get(0)); - Assert.assertEquals(86348749, (int) result.get(0).lowerEndpoint()); - Assert.assertEquals(86352858, (int) result.get(1).lowerEndpoint()); - Assert.assertEquals(86354529, (int) result.get(2).lowerEndpoint()); + Assertions.assertEquals(86348749, (int) result.get(0).lowerEndpoint()); + Assertions.assertEquals(86352858, (int) result.get(1).lowerEndpoint()); + Assertions.assertEquals(86354529, (int) result.get(2).lowerEndpoint()); - Assert.assertEquals(86348878, (int) result.get(0).upperEndpoint()); - Assert.assertEquals(86352984, (int) result.get(1).upperEndpoint()); - Assert.assertEquals(86387027, (int) result.get(2).upperEndpoint()); + Assertions.assertEquals(86348878, (int) result.get(0).upperEndpoint()); + Assertions.assertEquals(86352984, (int) result.get(1).upperEndpoint()); + Assertions.assertEquals(86387027, (int) result.get(2).upperEndpoint()); } @Test - public void testGenomeMappingToolGetCDSRangesSERINC2(){ + void testGenomeMappingToolGetCDSRangesSERINC2(){ List lst1 = new ArrayList<>(Arrays.asList(31413812, 31415872, 31423692)); List lst2 = new ArrayList<>(Arrays.asList(31414777, 31415907, 31423854)); @@ -69,7 +69,7 @@ public void testGenomeMappingToolGetCDSRangesSERINC2(){ List> result = ChromosomeMappingTools.getCDSRegions(lst1,lst2,cdsStart,cdsEnd); // makes sure the first list does not get changed; - Assert.assertEquals(31423818, (int) result.get(0).lowerEndpoint()); + Assertions.assertEquals(31423818, (int) result.get(0).lowerEndpoint()); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestIssue355.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestIssue355.java index 5543682f17..4c98225b04 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestIssue355.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestIssue355.java @@ -20,29 +20,28 @@ */ package org.biojava.nbio.genome; -import static org.junit.Assert.*; - import org.biojava.nbio.genome.parsers.gff.Location; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -public class TestIssue355 { +class TestIssue355 { @Test - public void testIssue1() { + void testIssue1() { Location l1 = Location.fromBio(51227320, 51227381, '+'); Location l2 = Location.fromBio(51227323, 51227382, '+'); Location union = l1.union(l2); - assertEquals(51227320,union.bioStart()); - assertEquals(51227382,union.bioEnd()); + Assertions.assertEquals(51227320, union.bioStart()); + Assertions.assertEquals(51227382, union.bioEnd()); } @Test - public void testIssue2() { + void testIssue2() { Location l1 = Location.fromBio(100, 200, '+'); Location l2 = Location.fromBio(1, 99, '+'); Location intersection = l1.intersection(l2); - assertNull(intersection); + Assertions.assertNull(intersection); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestLocation.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestLocation.java index 1289cb757e..42893cf22b 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestLocation.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestLocation.java @@ -20,15 +20,14 @@ */ package org.biojava.nbio.genome; -import static org.junit.Assert.*; - import org.biojava.nbio.genome.parsers.gff.Location; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -public class TestLocation { +class TestLocation { @Test - public void testLocation() { + void testLocation() { // tests taken from Location.main() //Location p3_7= new Location( 3, 7 ); @@ -49,70 +48,70 @@ public void testLocation() { Location r5_8= new Location( 5, 8 ); //distance - assertEquals(7, L(14,14).distance( L(3,7) )); - assertEquals(7, L(3,7).distance( L(14,14) )); - assertEquals(3, L(1,4).distance( L(7, 10) )); + Assertions.assertEquals(7, L(14,14).distance( L(3,7) )); + Assertions.assertEquals(7, L(3,7).distance( L(14,14) )); + Assertions.assertEquals(3, L(1,4).distance( L(7, 10) )); //union - assertEquals(p10_17, p10_12.union( p14_17 )); - assertEquals(p10_17, p14_17.union( p10_12 )); - assertEquals(p15_19, p15_19.union( p15_16 )); + Assertions.assertEquals(p10_17, p10_12.union( p14_17 )); + Assertions.assertEquals(p10_17, p14_17.union( p10_12 )); + Assertions.assertEquals(p15_19, p15_19.union( p15_16 )); //intersection - assertEquals(new Location( 21, 25 ), r13_17.union( r21_25 ).intersection( r21_25 )); + Assertions.assertEquals(new Location( 21, 25 ), r13_17.union( r21_25 ).intersection( r21_25 )); //isBefore - assertTrue( r2_5.isBefore( r5_8 )); - assertTrue( !r2_5.isBefore( r4_7 )); + Assertions.assertTrue(r2_5.isBefore( r5_8 )); + Assertions.assertTrue(!r2_5.isBefore( r4_7 )); //isAfter - assertTrue(r5_8.isAfter( r2_5 )); - assertTrue(!r5_8.isAfter( r4_7 )); + Assertions.assertTrue(r5_8.isAfter( r2_5 )); + Assertions.assertTrue(!r5_8.isAfter( r4_7 )); //contains - assertTrue(p15_19.contains( p16_19 )); + Assertions.assertTrue(p15_19.contains( p16_19 )); //overlaps - assertTrue(r2_5.overlaps( r4_7 )); - assertTrue(r2_5.overlaps( r0_3 )); - assertTrue(!r5_8.overlaps( r2_5 )); - assertTrue(!r2_5.overlaps( r5_8 )); + Assertions.assertTrue(r2_5.overlaps( r4_7 )); + Assertions.assertTrue(r2_5.overlaps( r0_3 )); + Assertions.assertTrue(!r5_8.overlaps( r2_5 )); + Assertions.assertTrue(!r2_5.overlaps( r5_8 )); //prefix - assertEquals(L(2,3), L(2,20).prefix(1)); - assertEquals(L(2,19), L(2,20).prefix(-1)); - assertEquals( L(2,10), L(2,20).prefix( L(10,12))); + Assertions.assertEquals(L(2,3), L(2,20).prefix(1)); + Assertions.assertEquals(L(2,19), L(2,20).prefix(-1)); + Assertions.assertEquals(L(2,10), L(2,20).prefix( L(10,12))); //suffix - assertEquals(L(3,20), L(2,20).suffix(1)); - assertEquals(L(19,20), L(2,20).suffix(-1)); - assertEquals(L(12,20), L(2,20).suffix( L(10,12))); + Assertions.assertEquals(L(3,20), L(2,20).suffix(1)); + Assertions.assertEquals(L(19,20), L(2,20).suffix(-1)); + Assertions.assertEquals(L(12,20), L(2,20).suffix( L(10,12))); } @Test - public void testLocationIntersections() { + void testLocationIntersections() { // One inside another Location r21_25 = new Location( 21, 25 ); Location r1_100 = new Location(1, 100 ); - assertEquals(r21_25, r21_25.intersection( r1_100)); - assertEquals(r21_25, r1_100.intersection( r21_25)); + Assertions.assertEquals(r21_25, r21_25.intersection( r1_100)); + Assertions.assertEquals(r21_25, r1_100.intersection( r21_25)); // Non overlapping Location r10_100 = new Location(10, 100 ); Location r1_9 = new Location( 1, 9 ); - assertNull(r10_100.intersection( r1_9)); - assertNull(r1_9.intersection( new Location( 9, 10 ))); + Assertions.assertNull(r10_100.intersection( r1_9)); + Assertions.assertNull(r1_9.intersection( new Location( 9, 10 ))); // Partially overlappping Location r1_25 = new Location( 1, 25 ); Location r21_100 = new Location(21, 100 ); - assertEquals(r21_25, r1_25.intersection( r21_100)); - assertEquals(r21_25, r21_100.intersection( r1_25)); + Assertions.assertEquals(r21_25, r1_25.intersection( r21_100)); + Assertions.assertEquals(r21_25, r21_100.intersection( r1_25)); } //shorthand for testing diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqReaderTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqReaderTest.java index 7cde3d3ec9..3e1385f3a2 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqReaderTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqReaderTest.java @@ -20,8 +20,8 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; @@ -34,7 +34,7 @@ /** * Abstract unit test for implementations of FastqReader. */ -public abstract class AbstractFastqReaderTest { +abstract class AbstractFastqReaderTest { /** Array of example files that should throw IOExceptions. */ static final String[] ERROR_EXAMPLES = new String[] { @@ -87,21 +87,21 @@ public abstract class AbstractFastqReaderTest { public void testCreateFastq() { Fastq fastq = createFastq(); - Assert.assertNotNull(fastq); + Assertions.assertNotNull(fastq); } @Test public void testCreateFastqReader() { FastqReader reader = createFastqReader(); - Assert.assertNotNull(reader); + Assertions.assertNotNull(reader); } @Test public void testCreateFastqWriter() { FastqWriter writer = createFastqWriter(); - Assert.assertNotNull(writer); + Assertions.assertNotNull(writer); } @Test @@ -111,7 +111,7 @@ public void testReadFile() throws Exception try { reader.read((File) null); - Assert.fail("read((File) null) expected IllegalArgumentException"); + Assertions.fail("read((File) null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -121,7 +121,7 @@ public void testReadFile() throws Exception { File noSuchFile = new File("no such file"); reader.read(noSuchFile); - Assert.fail("read(no such file) expected IOException"); + Assertions.fail("read(no such file) expected IOException"); } catch (IOException e) { @@ -135,14 +135,14 @@ public void testReadEmptyFile() throws Exception FastqReader reader = createFastqReader(); File empty = Files.createTempFile("abstractFastqReaderTest",null).toFile(); Iterable iterable = reader.read(empty); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); + Assertions.assertNotNull(f); count++; } - Assert.assertEquals(0, count); + Assertions.assertEquals(0, count); } @Test @@ -154,14 +154,14 @@ public void testReadRoundTripSingleFile() throws Exception FastqWriter writer = createFastqWriter(); writer.write(single, fastq); Iterable iterable = reader.read(single); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); + Assertions.assertNotNull(f); count++; } - Assert.assertEquals(1, count); + Assertions.assertEquals(1, count); } @Test @@ -175,14 +175,14 @@ public void testReadRoundTripMultipleFile() throws Exception FastqWriter writer = createFastqWriter(); writer.write(multiple, fastq0, fastq1, fastq2); Iterable iterable = reader.read(multiple); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); + Assertions.assertNotNull(f); count++; } - Assert.assertEquals(3, count); + Assertions.assertEquals(3, count); } @Test @@ -192,7 +192,7 @@ public void testReadURL() throws Exception try { reader.read((URL) null); - Assert.fail("read((URL) null) expected IllegalArgumentException"); + Assertions.fail("read((URL) null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -202,7 +202,7 @@ public void testReadURL() throws Exception { URL noSuchURL = new URL("file:///no such url"); reader.read(noSuchURL); - Assert.fail("read(no such URL) expected IOException"); + Assertions.fail("read(no such URL) expected IOException"); } catch (IOException e) { @@ -216,14 +216,14 @@ public void testReadEmptyURL() throws Exception FastqReader reader = createFastqReader(); URL empty = getClass().getResource("empty.fastq"); Iterable iterable = reader.read(empty); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); + Assertions.assertNotNull(f); count++; } - Assert.assertEquals(0, count); + Assertions.assertEquals(0, count); } @Test @@ -233,7 +233,7 @@ public void testReadInputStream() throws Exception try { reader.read((InputStream) null); - Assert.fail("read((InputStream) null) expected IllegalArgumentException"); + Assertions.fail("read((InputStream) null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -247,14 +247,14 @@ public void testReadEmptyInputStream() throws Exception FastqReader reader = createFastqReader(); InputStream empty = getClass().getResourceAsStream("empty.fastq"); Iterable iterable = reader.read(empty); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); + Assertions.assertNotNull(f); count++; } - Assert.assertEquals(0, count); + Assertions.assertEquals(0, count); empty.close(); } @@ -264,15 +264,15 @@ public void testWrappedSequence() throws Exception FastqReader reader = createFastqReader(); InputStream wrappedSequence = getClass().getResourceAsStream("wrapped-sequence.fastq"); Iterable iterable = reader.read(wrappedSequence); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); - Assert.assertEquals("ACTG", f.getSequence()); + Assertions.assertNotNull(f); + Assertions.assertEquals("ACTG", f.getSequence()); count++; } - Assert.assertEquals(1, count); + Assertions.assertEquals(1, count); wrappedSequence.close(); } @@ -282,15 +282,15 @@ public void testWrappedQuality() throws Exception FastqReader reader = createFastqReader(); InputStream wrappedQuality = getClass().getResourceAsStream("wrapped-quality.fastq"); Iterable iterable = reader.read(wrappedQuality); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); - Assert.assertEquals("ZZZZ", f.getQuality()); + Assertions.assertNotNull(f); + Assertions.assertEquals("ZZZZ", f.getQuality()); count++; } - Assert.assertEquals(1, count); + Assertions.assertEquals(1, count); wrappedQuality.close(); } @@ -300,15 +300,15 @@ public void testMultipleWrappedQuality() throws Exception FastqReader reader = createFastqReader(); InputStream wrappedQuality = getClass().getResourceAsStream("multiple-wrapped-quality.fastq"); Iterable iterable = reader.read(wrappedQuality); - Assert.assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - Assert.assertNotNull(f); - Assert.assertEquals("ZZZZ", f.getQuality()); + Assertions.assertNotNull(f); + Assertions.assertEquals("ZZZZ", f.getQuality()); count++; } - Assert.assertEquals(4, count); + Assertions.assertEquals(4, count); wrappedQuality.close(); } @@ -322,7 +322,7 @@ public void testErrorExamples() throws Exception try { reader.read(inputStream); - Assert.fail("error example " + errorExample + " expected IOException"); + Assertions.fail("error example " + errorExample + " expected IOException"); } catch (IOException e) { @@ -430,7 +430,7 @@ public void complete() throws IOException { // empty } }); - Assert.fail("parse(null, ) expected IllegalArgumentException"); + Assertions.fail("parse(null, ) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -446,7 +446,7 @@ public void testParseNullParseListener() throws Exception try { reader.parse(new StringReader(input), null); - Assert.fail("parse(, null) expected IllegalArgumentException"); + Assertions.fail("parse(, null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqWriterTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqWriterTest.java index cf2b695968..f2000b5096 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqWriterTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/AbstractFastqWriterTest.java @@ -20,8 +20,8 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.File; @@ -34,7 +34,7 @@ /** * Abstract unit test for implementations of FastqWriter. */ -public abstract class AbstractFastqWriterTest { +abstract class AbstractFastqWriterTest { /** * Create and return a new FASTQ formatted sequence suitable for testing. @@ -54,14 +54,14 @@ public abstract class AbstractFastqWriterTest { public void testCreateFastq() { Fastq fastq = createFastq(); - Assert.assertNotNull(fastq); + Assertions.assertNotNull(fastq); } @Test public void testCreateFastqWriter() { FastqWriter writer = createFastqWriter(); - Assert.assertNotNull(writer); + Assertions.assertNotNull(writer); } @Test @@ -72,16 +72,16 @@ public void testAppendVararg() throws Exception Fastq fastq0 = createFastq(); Fastq fastq1 = createFastq(); Fastq fastq2 = createFastq(); - Assert.assertSame(appendable, writer.append(appendable, fastq0)); - Assert.assertSame(appendable, writer.append(appendable, fastq0, fastq1)); - Assert.assertSame(appendable, writer.append(appendable, fastq0, fastq1, fastq2)); - Assert.assertSame(appendable, writer.append(appendable, fastq0, fastq1, fastq2, null)); - Assert.assertSame(appendable, writer.append(appendable, (Fastq) null)); + Assertions.assertSame(appendable, writer.append(appendable, fastq0)); + Assertions.assertSame(appendable, writer.append(appendable, fastq0, fastq1)); + Assertions.assertSame(appendable, writer.append(appendable, fastq0, fastq1, fastq2)); + Assertions.assertSame(appendable, writer.append(appendable, fastq0, fastq1, fastq2, null)); + Assertions.assertSame(appendable, writer.append(appendable, (Fastq) null)); try { writer.append((Appendable) null, fastq0); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -98,20 +98,20 @@ public void testAppendIterable() throws Exception Fastq fastq1 = createFastq(); Fastq fastq2 = createFastq(); List list = new ArrayList(); - Assert.assertSame(appendable, writer.append(appendable, list)); + Assertions.assertSame(appendable, writer.append(appendable, list)); list.add(fastq0); - Assert.assertSame(appendable, writer.append(appendable, list)); + Assertions.assertSame(appendable, writer.append(appendable, list)); list.add(fastq1); - Assert.assertSame(appendable, writer.append(appendable, list)); + Assertions.assertSame(appendable, writer.append(appendable, list)); list.add(fastq2); - Assert.assertSame(appendable, writer.append(appendable, list)); + Assertions.assertSame(appendable, writer.append(appendable, list)); list.add(null); - Assert.assertSame(appendable, writer.append(appendable, list)); + Assertions.assertSame(appendable, writer.append(appendable, list)); try { writer.append((Appendable) null, list); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -120,7 +120,7 @@ public void testAppendIterable() throws Exception try { writer.append(appendable, (Iterable) null); - Assert.fail("append(,null) expected IllegalArgumentException"); + Assertions.fail("append(,null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -149,7 +149,7 @@ public void testWriteFileVararg() throws Exception try { writer.write((File) null, fastq0); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -189,7 +189,7 @@ public void testWriteFileIterable() throws Exception try { writer.write((File) null, fastq0); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -198,7 +198,7 @@ public void testWriteFileIterable() throws Exception try { writer.write(file5, (Iterable) null); - Assert.fail("append(,null) expected IllegalArgumentException"); + Assertions.fail("append(,null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -223,7 +223,7 @@ public void testWriteOutputStreamVararg() throws Exception try { writer.write((OutputStream) null, fastq0); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -253,7 +253,7 @@ public void testWriteOutputStreamIterable() throws Exception try { writer.write((OutputStream) null, fastq0); - Assert.fail("append(null,) expected IllegalArgumentException"); + Assertions.fail("append(null,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -262,7 +262,7 @@ public void testWriteOutputStreamIterable() throws Exception try { writer.write(outputStream, (Iterable) null); - Assert.fail("append(,null) expected IllegalArgumentException"); + Assertions.fail("append(,null) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/ConvertTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/ConvertTest.java index b07e237ef2..c6789e622f 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/ConvertTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/ConvertTest.java @@ -26,19 +26,18 @@ import java.util.List; import java.util.Map; -import org.junit.Test; -import static org.junit.Assert.*; - import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Round trip conversion functional tests. */ -public final class ConvertTest { +final class ConvertTest { @Test - public void testConvert() throws Exception + void testConvert() throws Exception { Map readers = Maps.newHashMap(); readers.put(FastqVariant.FASTQ_SANGER, new SangerFastqReader()); @@ -88,13 +87,13 @@ public void testConvert() throws Exception List observed = Lists.newArrayList(resultReader.read(tmp)); List expected = Lists.newArrayList(resultReader.read(getClass().getResource(expectedFileName))); - assertEquals(expected.size(), observed.size()); + Assertions.assertEquals(expected.size(), observed.size()); for (int i = 0; i < expected.size(); i++) { - assertEquals(expected.get(i).getDescription(), observed.get(i).getDescription()); - assertEquals(expected.get(i).getSequence(), observed.get(i).getSequence()); - assertEquals(expected.get(i).getQuality(), observed.get(i).getQuality()); - assertEquals(expected.get(i).getVariant(), observed.get(i).getVariant()); + Assertions.assertEquals(expected.get(i).getDescription(), observed.get(i).getDescription()); + Assertions.assertEquals(expected.get(i).getSequence(), observed.get(i).getSequence()); + Assertions.assertEquals(expected.get(i).getQuality(), observed.get(i).getQuality()); + Assertions.assertEquals(expected.get(i).getVariant(), observed.get(i).getVariant()); } } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqBuilderTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqBuilderTest.java index 5803068276..46957aeb26 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqBuilderTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqBuilderTest.java @@ -20,25 +20,23 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Assert; -import org.junit.Test; - -import org.junit.function.ThrowingRunnable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Unit test for FastqBuilder. */ -public final class FastqBuilderTest { +final class FastqBuilderTest { @Test - public void testConstructor() + void testConstructor() { FastqBuilder fastqBuilder = new FastqBuilder(); - Assert.assertNotNull(fastqBuilder); + Assertions.assertNotNull(fastqBuilder); } @Test - public void testConstructorFastq() + void testConstructorFastq() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -49,34 +47,29 @@ public void testConstructorFastq() Fastq fastq = fastqBuilder.build(); FastqBuilder fastqBuilder2 = new FastqBuilder(fastq); - Assert.assertNotNull(fastqBuilder2); + Assertions.assertNotNull(fastqBuilder2); Fastq fastq2 = fastqBuilder2.build(); - Assert.assertEquals("description", fastq2.getDescription()); - Assert.assertEquals("sequence", fastq2.getSequence()); - Assert.assertEquals("quality_", fastq2.getQuality()); - Assert.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq2.getVariant()); + Assertions.assertEquals("description", fastq2.getDescription()); + Assertions.assertEquals("sequence", fastq2.getSequence()); + Assertions.assertEquals("quality_", fastq2.getQuality()); + Assertions.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq2.getVariant()); } @Test - public void testConstructorNullFastq() + void testConstructorNullFastq() { - Assert.assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { - @Override - public void run() { - new FastqBuilder(null); - } - }); + Assertions.assertThrows(IllegalArgumentException.class, () -> new FastqBuilder(null)); } @Test - public void testBuildDefault() + void testBuildDefault() { try { FastqBuilder fastqBuilder = new FastqBuilder(); fastqBuilder.build(); - Assert.fail("build default expected IllegalStateException"); + Assertions.fail("build default expected IllegalStateException"); } catch (IllegalStateException e) { @@ -85,7 +78,7 @@ public void testBuildDefault() } @Test - public void testBuildNullDescription() + void testBuildNullDescription() { try { @@ -96,7 +89,7 @@ public void testBuildNullDescription() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build null description expected IllegalArgumentException"); + Assertions.fail("build null description expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -105,7 +98,7 @@ public void testBuildNullDescription() } @Test - public void testBuildNullSequence() + void testBuildNullSequence() { try { @@ -116,7 +109,7 @@ public void testBuildNullSequence() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build null sequence expected IllegalArgumentException"); + Assertions.fail("build null sequence expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -125,7 +118,7 @@ public void testBuildNullSequence() } @Test - public void testBuildNullAppendSequence() + void testBuildNullAppendSequence() { try { @@ -136,7 +129,7 @@ public void testBuildNullAppendSequence() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build null append sequence expected IllegalArgumentException"); + Assertions.fail("build null append sequence expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -145,7 +138,7 @@ public void testBuildNullAppendSequence() } @Test - public void testBuildNullQuality() + void testBuildNullQuality() { try { @@ -156,7 +149,7 @@ public void testBuildNullQuality() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build null quality expected IllegalArgumentException"); + Assertions.fail("build null quality expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -165,7 +158,7 @@ public void testBuildNullQuality() } @Test - public void testBuildNullAppendQuality() + void testBuildNullAppendQuality() { try { @@ -176,7 +169,7 @@ public void testBuildNullAppendQuality() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build null append quality expected IllegalArgumentException"); + Assertions.fail("build null append quality expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -185,7 +178,7 @@ public void testBuildNullAppendQuality() } @Test - public void testBuildNullVariant() + void testBuildNullVariant() { try { @@ -196,7 +189,7 @@ public void testBuildNullVariant() .withVariant(null); fastqBuilder.build(); - Assert.fail("build null variant expected IllegalArgumentException"); + Assertions.fail("build null variant expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -205,7 +198,7 @@ public void testBuildNullVariant() } @Test - public void testBuildMissingDescription() + void testBuildMissingDescription() { try { @@ -215,7 +208,7 @@ public void testBuildMissingDescription() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build missing description expected IllegalStateException"); + Assertions.fail("build missing description expected IllegalStateException"); } catch (IllegalStateException e) { @@ -224,7 +217,7 @@ public void testBuildMissingDescription() } @Test - public void testBuildMissingSequence() + void testBuildMissingSequence() { try { @@ -234,7 +227,7 @@ public void testBuildMissingSequence() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build missing sequence expected IllegalStateException"); + Assertions.fail("build missing sequence expected IllegalStateException"); } catch (IllegalStateException e) { @@ -243,7 +236,7 @@ public void testBuildMissingSequence() } @Test - public void testBuildMissingQuality() + void testBuildMissingQuality() { try { @@ -253,7 +246,7 @@ public void testBuildMissingQuality() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build missing quality expected IllegalStateException"); + Assertions.fail("build missing quality expected IllegalStateException"); } catch (IllegalStateException e) { @@ -262,7 +255,7 @@ public void testBuildMissingQuality() } @Test - public void testBuildDefaultVariant() + void testBuildDefaultVariant() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -270,16 +263,16 @@ public void testBuildDefaultVariant() .withQuality("quality_"); Fastq fastq = fastqBuilder.build(); - Assert.assertEquals("description", fastqBuilder.getDescription()); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); - Assert.assertEquals("description", fastq.getDescription()); - Assert.assertEquals("sequence", fastq.getSequence()); - Assert.assertEquals("quality_", fastq.getQuality()); - Assert.assertEquals(FastqBuilder.DEFAULT_VARIANT, fastq.getVariant()); + Assertions.assertEquals("description", fastqBuilder.getDescription()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals("description", fastq.getDescription()); + Assertions.assertEquals("sequence", fastq.getSequence()); + Assertions.assertEquals("quality_", fastq.getQuality()); + Assertions.assertEquals(FastqBuilder.DEFAULT_VARIANT, fastq.getVariant()); } @Test - public void testBuild() + void testBuild() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -287,16 +280,16 @@ public void testBuild() .withQuality("quality_") .withVariant(FastqVariant.FASTQ_SOLEXA); Fastq fastq = fastqBuilder.build(); - Assert.assertEquals("description", fastqBuilder.getDescription()); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); - Assert.assertEquals("description", fastq.getDescription()); - Assert.assertEquals("sequence", fastq.getSequence()); - Assert.assertEquals("quality_", fastq.getQuality()); - Assert.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); + Assertions.assertEquals("description", fastqBuilder.getDescription()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals("description", fastq.getDescription()); + Assertions.assertEquals("sequence", fastq.getSequence()); + Assertions.assertEquals("quality_", fastq.getQuality()); + Assertions.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); } @Test - public void testBuildAppendSequence() + void testBuildAppendSequence() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -305,16 +298,16 @@ public void testBuildAppendSequence() .withQuality("quality_") .withVariant(FastqVariant.FASTQ_SOLEXA); Fastq fastq = fastqBuilder.build(); - Assert.assertEquals("description", fastqBuilder.getDescription()); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); - Assert.assertEquals("description", fastq.getDescription()); - Assert.assertEquals("sequence", fastq.getSequence()); - Assert.assertEquals("quality_", fastq.getQuality()); - Assert.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); + Assertions.assertEquals("description", fastqBuilder.getDescription()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals("description", fastq.getDescription()); + Assertions.assertEquals("sequence", fastq.getSequence()); + Assertions.assertEquals("quality_", fastq.getQuality()); + Assertions.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); } @Test - public void testBuildAppendQuality() + void testBuildAppendQuality() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -323,48 +316,48 @@ public void testBuildAppendQuality() .appendQuality("ity_") .withVariant(FastqVariant.FASTQ_SOLEXA); Fastq fastq = fastqBuilder.build(); - Assert.assertEquals("description", fastqBuilder.getDescription()); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); - Assert.assertEquals("description", fastq.getDescription()); - Assert.assertEquals("sequence", fastq.getSequence()); - Assert.assertEquals("quality_", fastq.getQuality()); - Assert.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); + Assertions.assertEquals("description", fastqBuilder.getDescription()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals("description", fastq.getDescription()); + Assertions.assertEquals("sequence", fastq.getSequence()); + Assertions.assertEquals("quality_", fastq.getQuality()); + Assertions.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); } @Test - public void testBuildNonMatchingSequenceQualityScoreLengthsBothNull() + void testBuildNonMatchingSequenceQualityScoreLengthsBothNull() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") .withVariant(FastqVariant.FASTQ_SOLEXA); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); } @Test - public void testBuildNonMatchingSequenceQualityScoreLengthsSequenceNull() + void testBuildNonMatchingSequenceQualityScoreLengthsSequenceNull() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") .withQuality("0123") .withVariant(FastqVariant.FASTQ_SOLEXA); - Assert.assertEquals(false, fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals(false, fastqBuilder.sequenceAndQualityLengthsMatch()); } @Test - public void testBuildNonMatchingSequenceQualityScoreLengthsQualityNull() + void testBuildNonMatchingSequenceQualityScoreLengthsQualityNull() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") .withSequence("ACTG") .withVariant(FastqVariant.FASTQ_SOLEXA); - Assert.assertEquals(false, fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals(false, fastqBuilder.sequenceAndQualityLengthsMatch()); } @Test - public void testBuildNonMatchingSequenceQualityScoreLengths0() + void testBuildNonMatchingSequenceQualityScoreLengths0() { try { @@ -375,7 +368,7 @@ public void testBuildNonMatchingSequenceQualityScoreLengths0() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build sequence length > quality length expected IllegalStateException"); + Assertions.fail("build sequence length > quality length expected IllegalStateException"); } catch (IllegalStateException e) { @@ -384,7 +377,7 @@ public void testBuildNonMatchingSequenceQualityScoreLengths0() } @Test - public void testBuildNonMatchingSequenceQualityScoreLengths1() + void testBuildNonMatchingSequenceQualityScoreLengths1() { try { @@ -395,7 +388,7 @@ public void testBuildNonMatchingSequenceQualityScoreLengths1() .withVariant(FastqVariant.FASTQ_SOLEXA); fastqBuilder.build(); - Assert.fail("build sequence length < quality length expected IllegalStateException"); + Assertions.fail("build sequence length < quality length expected IllegalStateException"); } catch (IllegalStateException e) { @@ -404,7 +397,7 @@ public void testBuildNonMatchingSequenceQualityScoreLengths1() } @Test - public void testBuildMultiple() + void testBuildMultiple() { FastqBuilder fastqBuilder = new FastqBuilder() .withDescription("description") @@ -414,12 +407,12 @@ public void testBuildMultiple() for (int i = 0; i < 10; i++) { Fastq fastq = fastqBuilder.withSequence("sequence" + i).build(); - Assert.assertEquals("description", fastqBuilder.getDescription()); - Assert.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); - Assert.assertEquals("description", fastq.getDescription()); - Assert.assertEquals("sequence" + i, fastq.getSequence()); - Assert.assertEquals("quality__", fastq.getQuality()); - Assert.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); + Assertions.assertEquals("description", fastqBuilder.getDescription()); + Assertions.assertTrue(fastqBuilder.sequenceAndQualityLengthsMatch()); + Assertions.assertEquals("description", fastq.getDescription()); + Assertions.assertEquals("sequence" + i, fastq.getSequence()); + Assertions.assertEquals("quality__", fastq.getQuality()); + Assertions.assertEquals(FastqVariant.FASTQ_SOLEXA, fastq.getVariant()); } } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqTest.java index 62d7ee9368..5bcbb43ee9 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqTest.java @@ -20,26 +20,24 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Assert; -import org.junit.Test; - -import org.junit.function.ThrowingRunnable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Unit test for Fastq. */ -public final class FastqTest { +final class FastqTest { @Test - public void testConstructor() + void testConstructor() { Fastq fastq = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertNotNull(fastq); + Assertions.assertNotNull(fastq); try { new Fastq(null, "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.fail("ctr(null description) expected IllegalArgumentException"); + Assertions.fail("ctr(null description) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -48,7 +46,7 @@ public void testConstructor() try { new Fastq("description", null, "quality_", FastqVariant.FASTQ_SANGER); - Assert.fail("ctr(null sequence) expected IllegalArgumentException"); + Assertions.fail("ctr(null sequence) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -57,7 +55,7 @@ public void testConstructor() try { new Fastq("description", "sequence", null, FastqVariant.FASTQ_SANGER); - Assert.fail("ctr(null quality) expected IllegalArgumentException"); + Assertions.fail("ctr(null quality) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -66,7 +64,7 @@ public void testConstructor() try { new Fastq("description", "sequence", "quality_", null); - Assert.fail("ctr(null variant) expected IllegalArgumentException"); + Assertions.fail("ctr(null variant) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -75,88 +73,83 @@ public void testConstructor() } @Test - public void testDescription() + void testDescription() { Fastq fastq = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertTrue(fastq.getDescription() != null); - Assert.assertEquals("description", fastq.getDescription()); + Assertions.assertTrue(fastq.getDescription() != null); + Assertions.assertEquals("description", fastq.getDescription()); } @Test - public void testSequence() + void testSequence() { Fastq fastq = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertTrue(fastq.getSequence() != null); - Assert.assertEquals("sequence", fastq.getSequence()); + Assertions.assertTrue(fastq.getSequence() != null); + Assertions.assertEquals("sequence", fastq.getSequence()); } @Test - public void testQuality() + void testQuality() { Fastq fastq = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertTrue(fastq.getQuality() != null); - Assert.assertEquals("quality_", fastq.getQuality()); + Assertions.assertTrue(fastq.getQuality() != null); + Assertions.assertEquals("quality_", fastq.getQuality()); } @Test - public void testVariant() + void testVariant() { Fastq fastq = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertTrue(fastq.getVariant() != null); - Assert.assertEquals(FastqVariant.FASTQ_SANGER, fastq.getVariant()); + Assertions.assertTrue(fastq.getVariant() != null); + Assertions.assertEquals(FastqVariant.FASTQ_SANGER, fastq.getVariant()); } @Test - public void testBuilder() + void testBuilder() { - Assert.assertNotNull(Fastq.builder()); + Assertions.assertNotNull(Fastq.builder()); } @Test - public void testBuilderNullFastq() + void testBuilderNullFastq() { - Assert.assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { - @Override - public void run() { - Fastq.builder(null); - } - }); + Assertions.assertThrows(IllegalArgumentException.class, () -> Fastq.builder(null)); } @Test - public void testEquals() + void testEquals() { Fastq fastq0 = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); Fastq fastq1 = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertFalse(fastq0.equals(null)); - Assert.assertFalse(fastq1.equals(null)); - Assert.assertFalse(fastq0.equals(new Object())); - Assert.assertFalse(fastq1.equals(new Object())); - Assert.assertTrue(fastq0.equals(fastq0)); - Assert.assertTrue(fastq1.equals(fastq1)); - Assert.assertFalse(fastq0 == fastq1); - Assert.assertFalse(fastq0.equals(fastq1)); - Assert.assertFalse(fastq1.equals(fastq0)); + Assertions.assertFalse(fastq0.equals(null)); + Assertions.assertFalse(fastq1.equals(null)); + Assertions.assertFalse(fastq0.equals(new Object())); + Assertions.assertFalse(fastq1.equals(new Object())); + Assertions.assertTrue(fastq0.equals(fastq0)); + Assertions.assertTrue(fastq1.equals(fastq1)); + Assertions.assertFalse(fastq0 == fastq1); + Assertions.assertFalse(fastq0.equals(fastq1)); + Assertions.assertFalse(fastq1.equals(fastq0)); } @Test - public void testHashCode() + void testHashCode() { Fastq fastq0 = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); Fastq fastq1 = new Fastq("description", "sequence", "quality_", FastqVariant.FASTQ_SANGER); - Assert.assertEquals(fastq0.hashCode(), fastq0.hashCode()); - Assert.assertEquals(fastq1.hashCode(), fastq1.hashCode()); + Assertions.assertEquals(fastq0.hashCode(), fastq0.hashCode()); + Assertions.assertEquals(fastq1.hashCode(), fastq1.hashCode()); if (fastq0.equals(fastq1)) { - Assert.assertEquals(fastq0.hashCode(), fastq1.hashCode()); - Assert.assertEquals(fastq1.hashCode(), fastq0.hashCode()); + Assertions.assertEquals(fastq0.hashCode(), fastq1.hashCode()); + Assertions.assertEquals(fastq1.hashCode(), fastq0.hashCode()); } if (fastq1.equals(fastq0)) { - Assert.assertEquals(fastq0.hashCode(), fastq1.hashCode()); - Assert.assertEquals(fastq1.hashCode(), fastq0.hashCode()); + Assertions.assertEquals(fastq0.hashCode(), fastq1.hashCode()); + Assertions.assertEquals(fastq1.hashCode(), fastq0.hashCode()); } } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqToolsTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqToolsTest.java index 469601e624..43672b517b 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqToolsTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqToolsTest.java @@ -27,493 +27,340 @@ import org.biojava.nbio.core.sequence.features.QualityFeature; import org.biojava.nbio.core.sequence.features.QuantityFeature; import org.biojava.nbio.core.sequence.template.AbstractSequence; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; /** * Unit test for FastqTools. */ -public final class FastqToolsTest { +final class FastqToolsTest { private final FastqBuilder builder = new FastqBuilder().withDescription("foo").withSequence("ACTG").withQuality("ZZZZ"); @Test - public void testCreateDNASequence() throws CompoundNotFoundException + void testCreateDNASequence() throws CompoundNotFoundException { DNASequence sequence = FastqTools.createDNASequence(builder.build()); - Assert.assertNotNull(sequence); + Assertions.assertNotNull(sequence); } @Test - public void testCreateDNASequenceNullFastq() throws CompoundNotFoundException + void testCreateDNASequenceNullFastq() { - try - { - FastqTools.createDNASequence(null); - Assert.fail("createDNASequence(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createDNASequence(null)); } @Test - public void testCreateDNASequenceWithQualityScores() throws CompoundNotFoundException + void testCreateDNASequenceWithQualityScores() throws CompoundNotFoundException { DNASequence sequence = FastqTools.createDNASequenceWithQualityScores(builder.build()); - Assert.assertNotNull(sequence); + Assertions.assertNotNull(sequence); List, NucleotideCompound>> features = sequence.getFeaturesByType("qualityScores"); - Assert.assertNotNull(features); - Assert.assertEquals(1, features.size()); + Assertions.assertNotNull(features); + Assertions.assertEquals(1, features.size()); QualityFeature, NucleotideCompound> qualityScores = (QualityFeature, NucleotideCompound>) features.get(0); - Assert.assertEquals(sequence.getLength(), qualityScores.getQualities().size()); - Assert.assertEquals(sequence.getLength(), qualityScores.getLocations().getLength()); + Assertions.assertEquals(sequence.getLength(), qualityScores.getQualities().size()); + Assertions.assertEquals(sequence.getLength(), qualityScores.getLocations().getLength()); } @Test - public void testCreateDNASequenceWithQualityScoresNullFastq() throws CompoundNotFoundException + void testCreateDNASequenceWithQualityScoresNullFastq() { - try - { - FastqTools.createDNASequenceWithQualityScores(null); - Assert.fail("createDNASequenceWithQualityScores(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createDNASequenceWithQualityScores(null)); } @Test - public void testCreateDNASequenceWithErrorProbabilies() throws CompoundNotFoundException + void testCreateDNASequenceWithErrorProbabilies() throws CompoundNotFoundException { DNASequence sequence = FastqTools.createDNASequenceWithErrorProbabilities(builder.build()); - Assert.assertNotNull(sequence); + Assertions.assertNotNull(sequence); List, NucleotideCompound>> features = sequence.getFeaturesByType("errorProbabilities"); - Assert.assertNotNull(features); - Assert.assertEquals(1, features.size()); + Assertions.assertNotNull(features); + Assertions.assertEquals(1, features.size()); QuantityFeature, NucleotideCompound> errorProbabilities = (QuantityFeature, NucleotideCompound>) features.get(0); - Assert.assertEquals(sequence.getLength(), errorProbabilities.getQuantities().size()); - Assert.assertEquals(sequence.getLength(), errorProbabilities.getLocations().getLength()); + Assertions.assertEquals(sequence.getLength(), errorProbabilities.getQuantities().size()); + Assertions.assertEquals(sequence.getLength(), errorProbabilities.getLocations().getLength()); } @Test - public void testCreateDNASequenceWithErrorProbabilitiesNullFastq() throws CompoundNotFoundException + void testCreateDNASequenceWithErrorProbabilitiesNullFastq() { - try - { - FastqTools.createDNASequenceWithErrorProbabilities(null); - Assert.fail("createDNASequenceWithErrorProbabilities(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createDNASequenceWithErrorProbabilities(null)); } @Test - public void testCreateDNASequenceWithQualityScoresAndErrorProbabilities() throws CompoundNotFoundException + void testCreateDNASequenceWithQualityScoresAndErrorProbabilities() throws CompoundNotFoundException { DNASequence sequence = FastqTools.createDNASequenceWithQualityScoresAndErrorProbabilities(builder.build()); - Assert.assertNotNull(sequence); + Assertions.assertNotNull(sequence); List, NucleotideCompound>> qualityScoresFeatures = sequence.getFeaturesByType("qualityScores"); - Assert.assertNotNull(qualityScoresFeatures); - Assert.assertEquals(1, qualityScoresFeatures.size()); + Assertions.assertNotNull(qualityScoresFeatures); + Assertions.assertEquals(1, qualityScoresFeatures.size()); QualityFeature, NucleotideCompound> qualityScores = (QualityFeature, NucleotideCompound>) qualityScoresFeatures.get(0); - Assert.assertEquals(sequence.getLength(), qualityScores.getQualities().size()); - Assert.assertEquals(sequence.getLength(), qualityScores.getLocations().getLength()); + Assertions.assertEquals(sequence.getLength(), qualityScores.getQualities().size()); + Assertions.assertEquals(sequence.getLength(), qualityScores.getLocations().getLength()); List, NucleotideCompound>> errorProbabilitiesFeatures = sequence.getFeaturesByType("errorProbabilities"); - Assert.assertNotNull(errorProbabilitiesFeatures); - Assert.assertEquals(1, errorProbabilitiesFeatures.size()); + Assertions.assertNotNull(errorProbabilitiesFeatures); + Assertions.assertEquals(1, errorProbabilitiesFeatures.size()); QuantityFeature, NucleotideCompound> errorProbabilities = (QuantityFeature, NucleotideCompound>) errorProbabilitiesFeatures.get(0); - Assert.assertEquals(sequence.getLength(), errorProbabilities.getQuantities().size()); - Assert.assertEquals(sequence.getLength(), errorProbabilities.getLocations().getLength()); + Assertions.assertEquals(sequence.getLength(), errorProbabilities.getQuantities().size()); + Assertions.assertEquals(sequence.getLength(), errorProbabilities.getLocations().getLength()); } @Test - public void testCreateDNASequenceWithQualityScoresAndErrorProbabilitiesNullFastq() throws CompoundNotFoundException + void testCreateDNASequenceWithQualityScoresAndErrorProbabilitiesNullFastq() { - try - { - FastqTools.createDNASequenceWithQualityScoresAndErrorProbabilities(null); - Assert.fail("createDNASequenceWithQualityScoresAndErrorProbabilities(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createDNASequenceWithQualityScoresAndErrorProbabilities(null)); } @Test - public void testCreateQualityScores() + void testCreateQualityScores() { Fastq fastq = builder.build(); QualityFeature, NucleotideCompound> qualityScores = FastqTools.createQualityScores(fastq); - Assert.assertNotNull(qualityScores); - Assert.assertEquals(fastq.getSequence().length(), qualityScores.getQualities().size()); + Assertions.assertNotNull(qualityScores); + Assertions.assertEquals(fastq.getSequence().length(), qualityScores.getQualities().size()); } @Test - public void testCreateQualityScoresNullFastq() + void testCreateQualityScoresNullFastq() { - try - { - FastqTools.createQualityScores(null); - Assert.fail("createQualityScores(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createQualityScores(null)); } @Test - public void testCreateErrorProbabilities() + void testCreateErrorProbabilities() { Fastq fastq = builder.build(); QuantityFeature, NucleotideCompound> errorProbabilities = FastqTools.createErrorProbabilities(fastq); - Assert.assertNotNull(errorProbabilities); - Assert.assertEquals(fastq.getSequence().length(), errorProbabilities.getQuantities().size()); + Assertions.assertNotNull(errorProbabilities); + Assertions.assertEquals(fastq.getSequence().length(), errorProbabilities.getQuantities().size()); } @Test - public void testCreateErrorProbabilitiesNullFastq() + void testCreateErrorProbabilitiesNullFastq() { - try - { - FastqTools.createErrorProbabilities(null); - Assert.fail("createErrorProbabilities(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.createErrorProbabilities(null)); } @Test - public void testQualityScores() + void testQualityScores() { Iterable qualityScores = FastqTools.qualityScores(builder.build()); - Assert.assertNotNull(qualityScores); - int count = 0; - for (Number qualityScore : qualityScores) - { - Assert.assertNotNull(qualityScore); - count++; - } - Assert.assertEquals(4, count); + List scoresList = StreamSupport.stream(qualityScores.spliterator(), false) + .collect(Collectors.toList()); + Assertions.assertAll( + () -> Assertions.assertEquals(4, scoresList.size()), + () -> Assertions.assertFalse(scoresList.contains(null)) + ); } @Test - public void testQualityScoresNullFastq() + void testQualityScoresNullFastq() { - try - { - FastqTools.qualityScores(null); - Assert.fail("qualityScores(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.qualityScores(null)); } @Test - public void testQualityScoresIntArray() + void testQualityScoresIntArray() { int[] qualityScores = new int[4]; FastqTools.qualityScores(builder.build(), qualityScores); - for (int i = 0; i < 4; i++) - { - Assert.assertTrue(qualityScores[i] != 0); - } + + Assertions.assertTrue(Arrays.stream(qualityScores).allMatch(score -> score != 0), () -> + "Array contains zero at some position: " + Arrays.toString(qualityScores)); } @Test - public void testQualityScoresIntArrayNullFastq() + void testQualityScoresIntArrayNullFastq() { - try - { - FastqTools.qualityScores(null, new int[0]); - Assert.fail("qualityScores(null, int[]) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.qualityScores(null, new int[0])); } @Test - public void testQualityScoresNullIntArray() + void testQualityScoresNullIntArray() { - try - { - FastqTools.qualityScores(builder.build(), null); - Assert.fail("qualityScores(fastq, null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.qualityScores(fastq, null)); } @Test - public void testQualityScoresQualityScoresTooSmall() + void testQualityScoresQualityScoresTooSmall() { - try - { - FastqTools.qualityScores(builder.build(), new int[3]); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.qualityScores(fastq, new int[3])); } @Test - public void testQualityScoresQualityScoresTooLarge() + void testQualityScoresQualityScoresTooLarge() { - try - { - FastqTools.qualityScores(builder.build(), new int[5]); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.qualityScores(fastq, new int[5])); } @Test - public void testErrorProbabilities() + void testErrorProbabilities() { Iterable errorProbabilities = FastqTools.errorProbabilities(builder.build()); - Assert.assertNotNull(errorProbabilities); - int count = 0; - for (Number errorProbability : errorProbabilities) - { - Assert.assertNotNull(errorProbability); - count++; - } - Assert.assertEquals(4, count); + List scores = StreamSupport.stream(errorProbabilities.spliterator(), false) + .collect(Collectors.toList()); + + Assertions.assertNotNull(scores); + Assertions.assertEquals(4, scores.size()); + Assertions.assertTrue(scores.stream().allMatch(Objects::nonNull)); } @Test - public void testErrorProbabilitiesNullFastq() + void testErrorProbabilitiesNullFastq() { - try - { - FastqTools.errorProbabilities(null); - Assert.fail("errorProbabilities(null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.errorProbabilities(null)); } @Test - public void testErrorProbabilitiesDoubleArray() + void testErrorProbabilitiesDoubleArray() { double[] errorProbabilities = new double[4]; FastqTools.errorProbabilities(builder.build(), errorProbabilities); - for (int i = 0; i < 0; i++) - { - Assert.assertTrue(errorProbabilities[i] > 0.0d); - } + Assertions.assertTrue( + Arrays.stream(errorProbabilities).allMatch(p -> p > 0.0), + () -> "Expected all probabilities to be > 0.0, but got: " + Arrays.toString(errorProbabilities) + ); } @Test - public void testErrorProbabilitiesDoubleArrayNullFastq() + void testErrorProbabilitiesDoubleArrayNullFastq() { - try - { - FastqTools.errorProbabilities(null, new double[0]); - Assert.fail("errorProbabilities(null, double[]) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.errorProbabilities(null, new double[0])); } @Test - public void testErrorProbabilitiesNullErrorProbabilities() + void testErrorProbabilitiesNullErrorProbabilities() { - try - { - FastqTools.errorProbabilities(builder.build(), null); - Assert.fail("errorProbabilities(fastq, null) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.errorProbabilities(fastq, null)); } @Test - public void testErrorProbabilitiesErrorProbabilitiesTooSmall() + void testErrorProbabilitiesErrorProbabilitiesTooSmall() { - try - { - FastqTools.errorProbabilities(builder.build(), new double[3]); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.errorProbabilities(fastq, new double[3])); } @Test - public void testErrorProbabilitiesErrorProbabilitiesTooLarge() + void testErrorProbabilitiesErrorProbabilitiesTooLarge() { - try - { - FastqTools.errorProbabilities(builder.build(), new double[5]); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.errorProbabilities(fastq, new double[5])); } @Test - public void testConvertNullFastq() + void testConvertNullFastq() { - try - { - FastqTools.convert(null, FastqVariant.FASTQ_SANGER); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.convert(null, FastqVariant.FASTQ_SANGER)); } @Test - public void testConvertNullVariant() + void testConvertNullVariant() { - try - { - FastqTools.convert(builder.build(), null); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.convert(fastq, null)); } @Test - public void testConvertSameVariant() + void testConvertSameVariant() { Fastq fastq = builder.build(); - Assert.assertEquals(fastq, FastqTools.convert(fastq, fastq.getVariant())); + Assertions.assertEquals(fastq, FastqTools.convert(fastq, fastq.getVariant())); } @Test - public void testConvertQualitiesNullFastq() + void testConvertQualitiesNullFastq() { - try - { - FastqTools.convertQualities(null, FastqVariant.FASTQ_SANGER); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.convertQualities(null, FastqVariant.FASTQ_SANGER)); } @Test - public void testConvertQualitiesNullVariant() + void testConvertQualitiesNullVariant() { - try - { - FastqTools.convertQualities(builder.build(), null); - Assert.fail("expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) - { - // expected - } + Fastq fastq = builder.build(); + Assertions.assertThrows(IllegalArgumentException.class, () -> FastqTools.convertQualities(fastq, null)); } @Test - public void testConvertQualitiesSameVariant() + void testConvertQualitiesSameVariant() { Fastq fastq = builder.build(); - Assert.assertEquals(fastq.getQuality(), FastqTools.convertQualities(fastq, fastq.getVariant())); + Assertions.assertEquals(fastq.getQuality(), FastqTools.convertQualities(fastq, fastq.getVariant())); } @Test - public void testConvertQualitiesSangerToSolexa() + void testConvertQualitiesSangerToSolexa() { Fastq fastq = builder.build(); - Assert.assertEquals("yyyy", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SOLEXA)); + Assertions.assertEquals("yyyy", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SOLEXA)); } @Test - public void testConvertQualitiesSangerToIllumina() + void testConvertQualitiesSangerToIllumina() { Fastq fastq = builder.build(); - Assert.assertEquals("yyyy", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_ILLUMINA)); + Assertions.assertEquals("yyyy", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_ILLUMINA)); } @Test - public void testConvertQualitiesSolexaToSanger() + void testConvertQualitiesSolexaToSanger() { Fastq fastq = builder.withVariant(FastqVariant.FASTQ_SOLEXA).build(); - Assert.assertEquals(";;;;", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SANGER)); + Assertions.assertEquals(";;;;", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SANGER)); } @Test - public void testConvertQualitiesIlluminaToSanger() + void testConvertQualitiesIlluminaToSanger() { Fastq fastq = builder.withVariant(FastqVariant.FASTQ_ILLUMINA).build(); - Assert.assertEquals(";;;;", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SANGER)); + Assertions.assertEquals(";;;;", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SANGER)); } @Test - public void testConvertQualitiesSolexaToIllumina() + void testConvertQualitiesSolexaToIllumina() { Fastq fastq = builder.withVariant(FastqVariant.FASTQ_SOLEXA).build(); - Assert.assertEquals("ZZZZ", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_ILLUMINA)); + Assertions.assertEquals("ZZZZ", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_ILLUMINA)); } @Test - public void testConvertQualitiesIlluminaToSolexa() + void testConvertQualitiesIlluminaToSolexa() { Fastq fastq = builder.withVariant(FastqVariant.FASTQ_ILLUMINA).build(); - Assert.assertEquals("ZZZZ", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SOLEXA)); + Assertions.assertEquals("ZZZZ", FastqTools.convertQualities(fastq, FastqVariant.FASTQ_SOLEXA)); } @Test - public void testToList() + void testToList() { - List list = new ArrayList(); - Assert.assertSame(list, FastqTools.toList(list)); + List list = new ArrayList<>(); + Assertions.assertSame(list, FastqTools.toList(list)); } @Test - public void testToListNotAList() + void testToListNotAList() { - Collection collection = new HashSet(); - Assert.assertTrue(FastqTools.toList(collection) instanceof List); - Assert.assertNotSame(collection, FastqTools.toList(collection)); + Collection collection = new HashSet<>(); + Assertions.assertTrue(FastqTools.toList(collection) instanceof List); + Assertions.assertNotSame(collection, FastqTools.toList(collection)); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqVariantTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqVariantTest.java index f8b0855a8e..a47896b714 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqVariantTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/FastqVariantTest.java @@ -22,66 +22,66 @@ import static org.biojava.nbio.genome.io.fastq.FastqVariant.*; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Unit test for FastqVariant. */ -public final class FastqVariantTest { +final class FastqVariantTest { @Test - public void testDescription() + void testDescription() { for (FastqVariant variant : values()) { - Assert.assertNotNull(variant.getDescription()); + Assertions.assertNotNull(variant.getDescription()); } } @Test - public void testIsSanger() + void testIsSanger() { - Assert.assertTrue(FASTQ_SANGER.isSanger()); - Assert.assertFalse(FASTQ_SOLEXA.isSanger()); - Assert.assertFalse(FASTQ_ILLUMINA.isSanger()); + Assertions.assertTrue(FASTQ_SANGER.isSanger()); + Assertions.assertFalse(FASTQ_SOLEXA.isSanger()); + Assertions.assertFalse(FASTQ_ILLUMINA.isSanger()); } @Test - public void testIsSolexa() + void testIsSolexa() { - Assert.assertFalse(FASTQ_SANGER.isSolexa()); - Assert.assertTrue(FASTQ_SOLEXA.isSolexa()); - Assert.assertFalse(FASTQ_ILLUMINA.isSolexa()); + Assertions.assertFalse(FASTQ_SANGER.isSolexa()); + Assertions.assertTrue(FASTQ_SOLEXA.isSolexa()); + Assertions.assertFalse(FASTQ_ILLUMINA.isSolexa()); } @Test - public void testIsIllumina() + void testIsIllumina() { - Assert.assertFalse(FASTQ_SANGER.isIllumina()); - Assert.assertFalse(FASTQ_SOLEXA.isIllumina()); - Assert.assertTrue(FASTQ_ILLUMINA.isIllumina()); + Assertions.assertFalse(FASTQ_SANGER.isIllumina()); + Assertions.assertFalse(FASTQ_SOLEXA.isIllumina()); + Assertions.assertTrue(FASTQ_ILLUMINA.isIllumina()); } @Test - public void testParseFastqVariant() + void testParseFastqVariant() { - Assert.assertEquals(null, parseFastqVariant(null)); - Assert.assertEquals(null, parseFastqVariant("")); - Assert.assertEquals(null, parseFastqVariant("not a valid FASTQ variant")); - Assert.assertEquals(FASTQ_SANGER, parseFastqVariant("FASTQ_SANGER")); - Assert.assertEquals(FASTQ_SANGER, parseFastqVariant("fastq-sanger")); + Assertions.assertEquals(null, parseFastqVariant(null)); + Assertions.assertEquals(null, parseFastqVariant("")); + Assertions.assertEquals(null, parseFastqVariant("not a valid FASTQ variant")); + Assertions.assertEquals(FASTQ_SANGER, parseFastqVariant("FASTQ_SANGER")); + Assertions.assertEquals(FASTQ_SANGER, parseFastqVariant("fastq-sanger")); } @Test - public void testQualityLessThanMinimumQualityScore() + void testQualityLessThanMinimumQualityScore() { for (FastqVariant variant : values()) { try { variant.quality(variant.minimumQualityScore() - 1); - Assert.fail("expected IllegalArgumentException"); + Assertions.fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -91,14 +91,14 @@ public void testQualityLessThanMinimumQualityScore() } @Test - public void testQualityMoreThanMaximumQualityScore() + void testQualityMoreThanMaximumQualityScore() { for (FastqVariant variant : values()) { try { variant.quality(variant.maximumQualityScore() + 1); - Assert.fail("expected IllegalArgumentException"); + Assertions.fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -108,13 +108,13 @@ public void testQualityMoreThanMaximumQualityScore() } @Test - public void testQualityQualityScoreRoundTrip() + void testQualityQualityScoreRoundTrip() { for (FastqVariant variant : values()) { for (int i = variant.minimumQualityScore(); i < (variant.maximumQualityScore() + 1); i++) { - Assert.assertEquals(i, variant.qualityScore(variant.quality(i))); + Assertions.assertEquals(i, variant.qualityScore(variant.quality(i))); } } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqReaderTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqReaderTest.java index d7b0a8b9d2..1d2adf76b6 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqReaderTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqReaderTest.java @@ -20,8 +20,8 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; @@ -31,7 +31,7 @@ /** * Unit test for IlluminaFastqReader. */ -public final class IlluminaFastqReaderTest +final class IlluminaFastqReaderTest extends AbstractFastqReaderTest { @@ -59,119 +59,119 @@ public FastqWriter createFastqWriter() } @Test - public void testValidateDescription() throws Exception + void testValidateDescription() throws Exception { IlluminaFastqReader reader = new IlluminaFastqReader(); URL invalidDescription = getClass().getResource("illumina-invalid-description.fastq"); try { reader.read(invalidDescription); - fail("read(invalidDescription) expected IOException"); + Assertions.fail("read(invalidDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("description must begin with a '@' character")); + Assertions.assertTrue(e.getMessage().contains("description must begin with a '@' character")); } } @Test - public void testValidateRepeatDescription() throws Exception + void testValidateRepeatDescription() throws Exception { IlluminaFastqReader reader = new IlluminaFastqReader(); URL invalidRepeatDescription = getClass().getResource("illumina-invalid-repeat-description.fastq"); try { reader.read(invalidRepeatDescription); - fail("read(invalidRepeatDescription) expected IOException"); + Assertions.fail("read(invalidRepeatDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("repeat description must match description")); + Assertions.assertTrue(e.getMessage().contains("repeat description must match description")); } } @Test - public void testWrappingAsIllumina() throws Exception + void testWrappingAsIllumina() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("wrapping_as_illumina.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(3, count); + Assertions.assertEquals(3, count); inputStream.close(); } @Test - public void testFullRangeAsIllumina() throws Exception + void testFullRangeAsIllumina() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("illumina_full_range_as_illumina.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(2, count); + Assertions.assertEquals(2, count); inputStream.close(); } @Test - public void testMiscDnaAsIllumina() throws Exception + void testMiscDnaAsIllumina() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_dna_as_illumina.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testMiscRnaAsIllumina() throws Exception + void testMiscRnaAsIllumina() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_rna_as_illumina.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testLongReadsAsIllumina() throws Exception + void testLongReadsAsIllumina() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("longreads_as_illumina.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(10, count); + Assertions.assertEquals(10, count); inputStream.close(); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqWriterTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqWriterTest.java index c9701595fa..384e204ff0 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqWriterTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/IlluminaFastqWriterTest.java @@ -21,12 +21,12 @@ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Unit test for IlluminaFastqWriter. */ -public final class IlluminaFastqWriterTest +final class IlluminaFastqWriterTest extends AbstractFastqWriterTest { @@ -48,7 +48,7 @@ public Fastq createFastq() } @Test - public void testConvertNotIlluminaVariant() throws Exception + void testConvertNotIlluminaVariant() throws Exception { IlluminaFastqWriter writer = new IlluminaFastqWriter(); Appendable appendable = new StringBuilder(); diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqReaderTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqReaderTest.java index af6f67319f..3e99a4cdc8 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqReaderTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqReaderTest.java @@ -20,18 +20,17 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import static org.junit.Assert.*; - /** * Unit test for SangerFastqReader. */ -public final class SangerFastqReaderTest +final class SangerFastqReaderTest extends AbstractFastqReaderTest { @@ -65,197 +64,197 @@ public void testValidateDescription() throws Exception try { reader.read(invalidDescription); - fail("read(invalidDescription) expected IOException"); + Assertions.fail("read(invalidDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("description must begin with a '@' character")); + Assertions.assertTrue(e.getMessage().contains("description must begin with a '@' character")); } } @Test - public void testValidateRepeatDescription() throws Exception + void testValidateRepeatDescription() throws Exception { SangerFastqReader reader = new SangerFastqReader(); URL invalidRepeatDescription = getClass().getResource("sanger-invalid-repeat-description.fastq"); try { reader.read(invalidRepeatDescription); - fail("read(invalidRepeatDescription) expected IOException"); + Assertions.fail("read(invalidRepeatDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("repeat description must match description")); + Assertions.assertTrue(e.getMessage().contains("repeat description must match description")); } } @Test - public void testWrappingOriginal() throws Exception + void testWrappingOriginal() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("wrapping_original_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(3, count); + Assertions.assertEquals(3, count); inputStream.close(); } @Test - public void testWrappingAsSanger() throws Exception + void testWrappingAsSanger() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("wrapping_as_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(3, count); + Assertions.assertEquals(3, count); inputStream.close(); } @Test - public void testFullRangeOriginal() throws Exception + void testFullRangeOriginal() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("sanger_full_range_original_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(2, count); + Assertions.assertEquals(2, count); inputStream.close(); } @Test - public void testFullRangeAsSanger() throws Exception + void testFullRangeAsSanger() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("sanger_full_range_as_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(2, count); + Assertions.assertEquals(2, count); inputStream.close(); } @Test - public void testMiscDnaOriginal() throws Exception + void testMiscDnaOriginal() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_dna_original_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testMiscDnaAsSanger() throws Exception + void testMiscDnaAsSanger() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_dna_as_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testMiscRnaOriginal() throws Exception + void testMiscRnaOriginal() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_rna_original_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testMiscRnaAsSanger() throws Exception + void testMiscRnaAsSanger() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_rna_as_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testLongReadsOriginal() throws Exception + void testLongReadsOriginal() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("longreads_original_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(10, count); + Assertions.assertEquals(10, count); inputStream.close(); } @Test - public void testLongReadsAsSanger() throws Exception + void testLongReadsAsSanger() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("longreads_as_sanger.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(10, count); + Assertions.assertEquals(10, count); inputStream.close(); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqWriterTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqWriterTest.java index f94db84e0a..fcb1638c71 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqWriterTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SangerFastqWriterTest.java @@ -21,12 +21,12 @@ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Unit test for SangerFastqWriter. */ -public final class SangerFastqWriterTest +final class SangerFastqWriterTest extends AbstractFastqWriterTest { @@ -48,7 +48,7 @@ public Fastq createFastq() } @Test - public void testConvertNotSangerVariant() throws Exception + void testConvertNotSangerVariant() throws Exception { SangerFastqWriter writer = new SangerFastqWriter(); Appendable appendable = new StringBuilder(); diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqReaderTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqReaderTest.java index 5f6f041c84..cd87c53dc6 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqReaderTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqReaderTest.java @@ -20,19 +20,18 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import static org.junit.Assert.*; - /** * Unit test for SolexaFastqReader. */ -public final class SolexaFastqReaderTest +final class SolexaFastqReaderTest extends AbstractFastqReaderTest { @@ -60,119 +59,119 @@ public FastqWriter createFastqWriter() } @Test - public void testValidateDescription() throws Exception + void testValidateDescription() throws Exception { SolexaFastqReader reader = new SolexaFastqReader(); URL invalidDescription = getClass().getResource("solexa-invalid-description.fastq"); try { reader.read(invalidDescription); - fail("read(invalidDescription) expected IOException"); + Assertions.fail("read(invalidDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("description must begin with a '@' character")); + Assertions.assertTrue(e.getMessage().contains("description must begin with a '@' character")); } } @Test - public void testValidateRepeatDescription() throws Exception + void testValidateRepeatDescription() throws Exception { SolexaFastqReader reader = new SolexaFastqReader(); URL invalidRepeatDescription = getClass().getResource("solexa-invalid-repeat-description.fastq"); try { reader.read(invalidRepeatDescription); - fail("read(invalidRepeatDescription) expected IOException"); + Assertions.fail("read(invalidRepeatDescription) expected IOException"); } catch (IOException e) { - assertTrue(e.getMessage().contains("repeat description must match description")); + Assertions.assertTrue(e.getMessage().contains("repeat description must match description")); } } @Test - public void testWrappingAsSolexa() throws Exception + void testWrappingAsSolexa() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("wrapping_as_solexa.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(3, count); + Assertions.assertEquals(3, count); inputStream.close(); } @Test - public void testFullRangeAsSolexa() throws Exception + void testFullRangeAsSolexa() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("solexa_full_range_as_solexa.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(2, count); + Assertions.assertEquals(2, count); inputStream.close(); } @Test - public void testMiscDnaAsSolexa() throws Exception + void testMiscDnaAsSolexa() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_dna_as_solexa.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testMiscRnaAsSolexa() throws Exception + void testMiscRnaAsSolexa() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("misc_rna_as_solexa.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(4, count); + Assertions.assertEquals(4, count); inputStream.close(); } @Test - public void testLongReadsAsSolexa() throws Exception + void testLongReadsAsSolexa() throws Exception { FastqReader reader = createFastqReader(); InputStream inputStream = getClass().getResourceAsStream("longreads_as_solexa.fastq"); Iterable iterable = reader.read(inputStream); - assertNotNull(iterable); + Assertions.assertNotNull(iterable); int count = 0; for (Fastq f : iterable) { - assertNotNull(f); + Assertions.assertNotNull(f); count++; } - assertEquals(10, count); + Assertions.assertEquals(10, count); inputStream.close(); } } diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqWriterTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqWriterTest.java index 2f2011e849..0927bf0cff 100755 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqWriterTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/SolexaFastqWriterTest.java @@ -21,12 +21,12 @@ package org.biojava.nbio.genome.io.fastq; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * Unit test for SolexaFastqWriter. */ -public final class SolexaFastqWriterTest +final class SolexaFastqWriterTest extends AbstractFastqWriterTest { @@ -48,7 +48,7 @@ public Fastq createFastq() } @Test - public void testConvertNotSolexaVariant() throws Exception + void testConvertNotSolexaVariant() throws Exception { SolexaFastqWriter writer = new SolexaFastqWriter(); Appendable appendable = new StringBuilder(); diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/StreamingFastqParserTest.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/StreamingFastqParserTest.java index a80f44a43d..02d49d3177 100644 --- a/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/StreamingFastqParserTest.java +++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/io/fastq/StreamingFastqParserTest.java @@ -20,8 +20,8 @@ */ package org.biojava.nbio.genome.io.fastq; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.StringReader; @@ -29,10 +29,10 @@ /** * Unit test for StreamingFastqParser. */ -public class StreamingFastqParserTest { +class StreamingFastqParserTest { @Test - public void testStreamNullReadable() throws Exception + void testStreamNullReadable() throws Exception { try { @@ -42,7 +42,7 @@ public void fastq(final Fastq fastq) { // empty } }); - Assert.fail("stream(null,,) expected IllegalArgumentException"); + Assertions.fail("stream(null,,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -51,7 +51,7 @@ public void fastq(final Fastq fastq) { } @Test - public void testStreamNullVariant() throws Exception + void testStreamNullVariant() throws Exception { try { @@ -62,7 +62,7 @@ public void fastq(final Fastq fastq) { // empty } }); - Assert.fail("stream(null,,) expected IllegalArgumentException"); + Assertions.fail("stream(null,,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -71,13 +71,13 @@ public void fastq(final Fastq fastq) { } @Test - public void testStreamNullListener() throws Exception + void testStreamNullListener() throws Exception { try { final String input = ""; StreamingFastqParser.stream(new StringReader(input), FastqVariant.FASTQ_SANGER, null); - Assert.fail("stream(null,,) expected IllegalArgumentException"); + Assertions.fail("stream(null,,) expected IllegalArgumentException"); } catch (IllegalArgumentException e) { diff --git a/biojava-integrationtest/pom.xml b/biojava-integrationtest/pom.xml index 2c44988004..870612fc63 100644 --- a/biojava-integrationtest/pom.xml +++ b/biojava-integrationtest/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-integrationtest jar @@ -40,7 +40,7 @@ org.biojava biojava-structure - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT diff --git a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/ecod/EcodInstallationTest.java b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/ecod/EcodInstallationTest.java index 8ab3f29fb3..f384f2ba65 100644 --- a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/ecod/EcodInstallationTest.java +++ b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/ecod/EcodInstallationTest.java @@ -22,8 +22,12 @@ import static org.junit.Assert.*; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -47,6 +51,7 @@ import org.biojava.nbio.structure.ecod.EcodDomain; import org.biojava.nbio.structure.ecod.EcodFactory; import org.biojava.nbio.structure.ecod.EcodInstallation; +import org.biojava.nbio.structure.ecod.EcodInstallation.EcodParser; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -277,12 +282,47 @@ public void testFilterByHierarchy() throws IOException { assertEquals(expected,actual); } + /** + * Checks that the current release can still be read. + *

+ * The version is read from the file's header without parsing the domains, so this + * additionally parses the first few thousand lines of the same file. That is enough to + * notice a column change — which is what ECOD did at v294.1, unnoticed for months — + * without building the three million domains the whole file now holds. + */ @Test public void testVersion() throws IOException { EcodDatabase ecod3 = EcodFactory.getEcodDatabase("latest"); String version = ecod3.getVersion(); assertNotNull(version); assertNotEquals("latest", version); + System.out.println("latest version of ECOD is "+version); + + File domainsFile = new File(((EcodInstallation) ecod3).getCacheLocation(), + "ecod.latest.domains.txt"); + assertTrue("No local copy of the domains file at "+domainsFile, domainsFile.exists()); + + EcodParser parser = new EcodParser(firstLines(domainsFile, 5000)); + assertEquals(version, parser.getVersion()); + assertFalse("No domains parsed from ECOD "+version + + "; the distribution format has probably changed", + parser.getDomains().isEmpty()); + } + + /** + * @return a reader over the first {@code maxLines} lines of the file + */ + private static Reader firstLines(File f, int maxLines) throws IOException { + StringBuilder head = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new FileReader(f))) { + String line; + int n = 0; + while (n < maxLines && (line = in.readLine()) != null) { + head.append(line).append('\n'); + n++; + } + } + return new StringReader(head.toString()); } /** diff --git a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/io/TestSeqResParsing.java b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/io/TestSeqResParsing.java index de6c072719..6ea23aef8a 100644 --- a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/io/TestSeqResParsing.java +++ b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/io/TestSeqResParsing.java @@ -53,7 +53,7 @@ public void test11GS() throws IOException, StructureException{ s = StructureIO.getStructure(pdbID); assertNotNull(s); - assertTrue(s.getChains().size() > 0); + assertFalse(s.getChains().isEmpty()); Chain c = s.getChainByIndex(0); assertTrue(c.getSeqResGroups().size() > 2); diff --git a/biojava-modfinder/pom.xml b/biojava-modfinder/pom.xml index 0b8cb53791..e0db50f1db 100644 --- a/biojava-modfinder/pom.xml +++ b/biojava-modfinder/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-modfinder biojava-modfinder @@ -31,7 +31,7 @@ org.biojava biojava-structure - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT jar compile diff --git a/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/io/ModifiedCompoundXMLConverter.java b/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/io/ModifiedCompoundXMLConverter.java index 187e113924..e038f57cd3 100644 --- a/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/io/ModifiedCompoundXMLConverter.java +++ b/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/io/ModifiedCompoundXMLConverter.java @@ -69,7 +69,7 @@ public static String toXML(ModifiedCompound mc) throws IOException{ Set linkages = mc.getAtomLinkages(); - if ( linkages.size() > 0 ) { + if (!linkages.isEmpty()) { int pos = -1; for ( StructureAtomLinkage link: linkages){ pos ++; diff --git a/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/structure/ProteinModificationIdentifier.java b/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/structure/ProteinModificationIdentifier.java index c9575a5444..0d94d36e6e 100644 --- a/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/structure/ProteinModificationIdentifier.java +++ b/biojava-modfinder/src/main/java/org/biojava/nbio/protmod/structure/ProteinModificationIdentifier.java @@ -285,7 +285,7 @@ public void identify(final List chains, if (residues.isEmpty()) { String pdbId = "?"; - if ( chains.size() > 0) { + if (!chains.isEmpty()) { Structure struc = chains.get(0).getStructure(); if ( struc != null) pdbId = struc.getPDBCode(); diff --git a/biojava-modfinder/src/test/java/org/biojava/nbio/protmod/phosphosite/TestAcetylation.java b/biojava-modfinder/src/test/java/org/biojava/nbio/protmod/phosphosite/TestAcetylation.java index 34376307a0..ba8e6d2a3d 100644 --- a/biojava-modfinder/src/test/java/org/biojava/nbio/protmod/phosphosite/TestAcetylation.java +++ b/biojava-modfinder/src/test/java/org/biojava/nbio/protmod/phosphosite/TestAcetylation.java @@ -32,7 +32,8 @@ import java.net.URL; import java.util.List; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -100,11 +101,11 @@ public void testAcetylation() throws IOException { List sites = Site.parseSites(localFile); - assertTrue(sites.size() > 0); + assertFalse(sites.isEmpty()); for (Site s : sites) { - assertTrue(s.getResidue() != null); + assertNotNull(s.getResidue()); } diff --git a/biojava-ontology/pom.xml b/biojava-ontology/pom.xml index 001e3e52f1..eb9e3564c1 100644 --- a/biojava-ontology/pom.xml +++ b/biojava-ontology/pom.xml @@ -4,7 +4,7 @@ org.biojava biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-ontology diff --git a/biojava-ontology/src/main/java/org/biojava/nbio/ontology/utils/WeakValueHashMap.java b/biojava-ontology/src/main/java/org/biojava/nbio/ontology/utils/WeakValueHashMap.java index 7762642c76..c508cdc206 100644 --- a/biojava-ontology/src/main/java/org/biojava/nbio/ontology/utils/WeakValueHashMap.java +++ b/biojava-ontology/src/main/java/org/biojava/nbio/ontology/utils/WeakValueHashMap.java @@ -58,12 +58,12 @@ public WeakValueHashMap() { private void diddleReferenceQueue() { // Avoid making behind-the-scenes modifications while iterators exist. - if (iteratorRefs.size() > 0) { + if (!iteratorRefs.isEmpty()) { Reference ref; while ((ref = iteratorRefQueue.poll()) != null) { iteratorRefs.remove(ref); } - if (iteratorRefs.size() > 0) { + if (!iteratorRefs.isEmpty()) { return; } } diff --git a/biojava-protein-comparison-tool/pom.xml b/biojava-protein-comparison-tool/pom.xml index 12e4941d55..1527d691f4 100644 --- a/biojava-protein-comparison-tool/pom.xml +++ b/biojava-protein-comparison-tool/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-protein-comparison-tool @@ -36,23 +36,23 @@ org.biojava biojava-alignment - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT org.biojava biojava-structure - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT org.biojava biojava-structure-gui - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT net.sourceforge.jmol diff --git a/biojava-protein-disorder/pom.xml b/biojava-protein-disorder/pom.xml index 82107e8058..827c708a2b 100644 --- a/biojava-protein-disorder/pom.xml +++ b/biojava-protein-disorder/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-protein-disorder jar @@ -63,7 +63,7 @@ org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT diff --git a/biojava-structure-gui/pom.xml b/biojava-structure-gui/pom.xml index 24644c8ffe..21e01ce42e 100644 --- a/biojava-structure-gui/pom.xml +++ b/biojava-structure-gui/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT 4.0.0 biojava-structure-gui @@ -27,13 +27,13 @@ org.biojava biojava-structure - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile diff --git a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/JAutoSuggest.java b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/JAutoSuggest.java index 9e3c825910..c5542b4edc 100644 --- a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/JAutoSuggest.java +++ b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/JAutoSuggest.java @@ -229,7 +229,7 @@ public void keyReleased(KeyEvent e) { list.ensureIndexIsVisible(list.getSelectedIndex() - 1); return; } else if (e.getKeyCode() == KeyEvent.VK_ENTER - && list.getSelectedIndex() != -1 && suggestions.size() > 0) { + && list.getSelectedIndex() != -1 && !suggestions.isEmpty()) { setText((String) list.getSelectedValue()); @@ -365,7 +365,7 @@ public String doInBackground() { setFont(regular); - if (suggestions.size() > 0) { + if (!suggestions.isEmpty()) { list.setListData(suggestions); list.setSelectedIndex(0); list.ensureIndexIsVisible(0); diff --git a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/SCOPAutoSuggestProvider.java b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/SCOPAutoSuggestProvider.java index 136f184584..8e1a975205 100644 --- a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/SCOPAutoSuggestProvider.java +++ b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/autosuggest/SCOPAutoSuggestProvider.java @@ -111,7 +111,7 @@ private List getPossibleScopDomains(String userInput) { if ( stop.get()) return domains; - if ( domains == null || domains.size() < 1){ + if ( domains == null || domains.isEmpty()){ if ( userInput.length() > 5){ // e.g. d4hhba @@ -127,11 +127,11 @@ private List getPossibleScopDomains(String userInput) { if (DEBUG) System.out.println("domains: " + domains); - if ( domains == null || domains.size() < 1) { + if ( domains == null || domains.isEmpty()) { if ( userInput.length() > 0 ){ List descs = scop.filterByClassificationId(userInput); - if ( descs == null || descs.size() < 1){ + if ( descs == null || descs.isEmpty()){ descs = scop.filterByDescription(userInput); } diff --git a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/jmol/RasmolCommandListener.java b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/jmol/RasmolCommandListener.java index d1ce5c0e30..feec8b0366 100644 --- a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/jmol/RasmolCommandListener.java +++ b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/jmol/RasmolCommandListener.java @@ -74,7 +74,7 @@ public void actionPerformed(ActionEvent event) { // check last command in history // if equivalent, don't add, // otherwise add - if (history.size()>0){ + if (!history.isEmpty()){ String txt=history.get(history.size()-1); if (! txt.equals(cmd)) { history.add(cmd); diff --git a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/gui/util/SequenceScalePanel.java b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/gui/util/SequenceScalePanel.java index 06542e5271..75da6c8e28 100644 --- a/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/gui/util/SequenceScalePanel.java +++ b/biojava-structure-gui/src/main/java/org/biojava/nbio/structure/gui/util/SequenceScalePanel.java @@ -126,7 +126,7 @@ private void setPrefSize() { public void setAligMap(List apos){ this.apos = apos; - if ( apos.size() == 0) + if (apos.isEmpty()) return; AlignedPosition last = apos.get(apos.size()-1); diff --git a/biojava-structure/pom.xml b/biojava-structure/pom.xml index 5392798310..647ab49c72 100644 --- a/biojava-structure/pom.xml +++ b/biojava-structure/pom.xml @@ -4,7 +4,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-structure biojava-structure @@ -51,13 +51,13 @@ org.biojava biojava-alignment - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile @@ -87,7 +87,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.4.2 + 2.18.9 diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Author.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Author.java index b0d7253507..bd5a01b885 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Author.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Author.java @@ -62,7 +62,7 @@ public boolean equals(Object obj) { if ((this.surname == null) ? (other.surname != null) : !this.surname.equals(other.surname)) { return false; } - return !((this.initials == null) ? (other.initials != null) : !this.initials.equals(other.initials)); + return (this.initials == null) ? other.initials == null : this.initials.equals(other.initials); } @Override diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Element.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Element.java index 2f534b2828..4e2d3e340a 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Element.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Element.java @@ -424,7 +424,7 @@ public boolean isHeavyAtom() { * @return true if Element is not Hydrogen and not Carbon. */ public boolean isHeteroAtom() { - return !(this == C || this == H); + return this != C && this != H; } /** diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Site.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Site.java index 9158906d23..341483f31b 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Site.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Site.java @@ -83,7 +83,7 @@ public String toPDB() { @Override public void toPDB(StringBuffer buf) { - if (groups == null || groups.size() < 1) { + if (groups == null || groups.isEmpty()) { return; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ClusterAltAligs.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ClusterAltAligs.java index 373bcf1611..0933198d7b 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ClusterAltAligs.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ClusterAltAligs.java @@ -102,7 +102,7 @@ public static void cluster(AlternativeAlignment[] aligs, int cutoff){ } clusters.add(currentCluster); - if ( remainList.size() == 0) { + if ( remainList.isEmpty()) { break; } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CECalculator.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CECalculator.java index 6c045ba48e..83f16b7982 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CECalculator.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CECalculator.java @@ -1450,7 +1450,7 @@ private int optimizeSuperposition(AFPChain afpChain, int nse1, int nse2, int str //afpChain.setTotalRmsdOpt(rmsd); //System.out.println("rmsd: " + rmsd); - if(!(nAtom= strLen * 0.95 && !isRmsdLenAssigned) { rmsdLen=rmsd; isRmsdLenAssigned=true; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CeCalculatorEnhanced.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CeCalculatorEnhanced.java index 4f57161268..cab98b0113 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CeCalculatorEnhanced.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/ce/CeCalculatorEnhanced.java @@ -1455,7 +1455,7 @@ private int optimizeSuperposition(AFPChain afpChain, int nse1, int nse2, int str //afpChain.setTotalRmsdOpt(rmsd); //System.out.println("rmsd: " + rmsd); - if(!(nAtom= strLen * 0.95 && !isRmsdLenAssigned) { rmsdLen=rmsd; isRmsdLenAssigned=true; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockImpl.java index e0423b6f8f..43da1d7c06 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockImpl.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockImpl.java @@ -127,7 +127,7 @@ public void setAlignRes(List> alignRes) { public int length() { if (alignRes == null) return 0; - if (alignRes.size() == 0) + if (alignRes.isEmpty()) return 0; return alignRes.get(0).size(); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockSetImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockSetImpl.java index cbbb3ae895..344ee3c239 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockSetImpl.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/BlockSetImpl.java @@ -179,7 +179,7 @@ public int size() { // Get the size from the variables that can contain the information if (parent != null) return parent.size(); - else if (getBlocks().size() == 0) { + else if (getBlocks().isEmpty()) { throw new IndexOutOfBoundsException( "Empty BlockSet: number of Blocks == 0."); } else @@ -194,7 +194,7 @@ public int getCoreLength() { } protected void updateLength() { - if (getBlocks().size() == 0) { + if (getBlocks().isEmpty()) { throw new IndexOutOfBoundsException( "Empty BlockSet: number of Blocks == 0."); } @@ -207,7 +207,7 @@ protected void updateLength() { } protected void updateCoreLength() { - if (getBlocks().size() == 0) { + if (getBlocks().isEmpty()) { throw new IndexOutOfBoundsException( "Empty BlockSet: number of Blocks == 0."); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/MultipleAlignmentImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/MultipleAlignmentImpl.java index 738eee30c5..06c93a4403 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/MultipleAlignmentImpl.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/MultipleAlignmentImpl.java @@ -207,7 +207,7 @@ public int getCoreLength() { * lengths. */ protected void updateLength() { - if (getBlockSets().size() == 0) { + if (getBlockSets().isEmpty()) { throw new IndexOutOfBoundsException( "Empty MultipleAlignment: blockSets size == 0."); } // Otherwise try to calculate it from the BlockSet information @@ -223,7 +223,7 @@ protected void updateLength() { * BlockSet core lengths. */ protected void updateCoreLength() { - if (getBlockSets().size() == 0) { + if (getBlockSets().isEmpty()) { throw new IndexOutOfBoundsException( "Empty MultipleAlignment: blockSets size == 0."); } // Otherwise try to calculate it from the BlockSet information diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/mc/MultipleMcOptimizer.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/mc/MultipleMcOptimizer.java index 052f147fc6..29c7012801 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/mc/MultipleMcOptimizer.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/mc/MultipleMcOptimizer.java @@ -153,7 +153,7 @@ public MultipleMcOptimizer(MultipleAlignment seedAln, for (Block b : toDelete) { for (BlockSet bs : msa.getBlockSets()) { bs.getBlocks().remove(b); - if (bs.getBlocks().size() == 0) + if (bs.getBlocks().isEmpty()) emptyBs.add(bs); } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/util/MultipleAlignmentWriter.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/util/MultipleAlignmentWriter.java index 771b8b5f68..5033576df0 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/util/MultipleAlignmentWriter.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/multiple/util/MultipleAlignmentWriter.java @@ -205,7 +205,7 @@ public static String toTransformMatrices(MultipleAlignment alignment) { List btransforms = alignment.getBlockSet(bs) .getTransformations(); - if (btransforms == null || btransforms.size() < 1) + if (btransforms == null || btransforms.isEmpty()) continue; if (alignment.getBlockSets().size() > 1) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/quaternary/QsAlignResult.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/quaternary/QsAlignResult.java index 7ac77a602e..fe1c9c411b 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/quaternary/QsAlignResult.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/quaternary/QsAlignResult.java @@ -117,7 +117,7 @@ public void setSubunitMap(Map subunitMap) { "Subunit Map index higher than Subunit List size."); // Update the relation enum - if (subunitMap.size() == 0) { + if (subunitMap.isEmpty()) { relation = QsRelation.DIFFERENT; } else if (subunitMap.keySet().size() == subunits1.size()) { if (subunitMap.values().size() == subunits2.size()) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AlignmentTools.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AlignmentTools.java index c6791f4ed2..e535a87508 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AlignmentTools.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AlignmentTools.java @@ -1313,7 +1313,7 @@ public static Group[] prepareGroupsForDisplay(AFPChain afpChain, Atom[] ca1, Ato if ( afpChain.getBlockNum() > 0){ // Superimpose ligands relative to the first block - if( hetatms2.size() > 0 ) { + if(!hetatms2.isEmpty()) { if ( afpChain.getBlockRotationMatrix().length > 0 ) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AtomCache.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AtomCache.java index 1435191c2c..71b8a3da22 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AtomCache.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/util/AtomCache.java @@ -228,7 +228,7 @@ public Structure getBiologicalAssembly(String pdbId, int bioAssemblyId, boolean throws StructureException, IOException { return getBiologicalAssembly(new PdbId(pdbId), bioAssemblyId, multiModel); } - + /** * Returns the biological assembly for a given PDB ID and bioAssemblyId, by building the * assembly from the biounit annotations found in {@link Structure#getPDBHeader()} @@ -284,7 +284,7 @@ public Structure getBiologicalAssembly(PdbId pdbId, int bioAssemblyId, boolean m asymUnit.getPDBHeader().getBioAssemblies().get(bioAssemblyId).getTransforms(); - if (transformations == null || transformations.size() == 0) { + if (transformations == null || transformations.isEmpty()) { throw new StructureException("Could not load transformations to recreate biological assembly id " + bioAssemblyId + " of " + pdbId); } @@ -339,7 +339,7 @@ public Structure getBiologicalAssembly(String pdbId, boolean multiModel) throws asymUnit.getPDBHeader().getBioAssemblies().get(bioAssemblyId).getTransforms(); - if (transformations == null || transformations.size() == 0) { + if (transformations == null || transformations.isEmpty()) { throw new StructureException("Could not load transformations to recreate biological assembly id " + bioAssemblyId + " of " + pdbId); } @@ -385,7 +385,7 @@ public List getBiologicalAssemblies(String pdbId, boolean multiModel) List transformations = asymUnit.getPDBHeader().getBioAssemblies().get(bioAssemblyId).getTransforms(); - if (transformations == null || transformations.size() == 0) { + if (transformations == null || transformations.isEmpty()) { logger.info("Could not load transformations to recreate biological assembly id {} of {}. Assembly " + "id will be missing in biological assemblies.", bioAssemblyId, pdbId); continue; @@ -807,7 +807,7 @@ public Structure getStructureForPdbId(String id) throws IOException, StructureEx public Structure getStructureForPdbId(PdbId pdbId) throws IOException { if (pdbId == null) return null; - + while (checkLoading(pdbId)) { // waiting for loading to be finished... try { @@ -833,7 +833,7 @@ public Structure getStructureForPdbId(PdbId pdbId) throws IOException { protected Structure loadStructureFromCifByPdbId(String pdbId) throws IOException { return loadStructureFromCifByPdbId(new PdbId(pdbId)); } - + protected Structure loadStructureFromCifByPdbId(PdbId pdbId) throws IOException { logger.debug("Loading structure {} from mmCIF file {}.", pdbId, path); Structure s; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/xml/MultipleAlignmentXMLParser.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/xml/MultipleAlignmentXMLParser.java index 759ee61931..e8d5434578 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/align/xml/MultipleAlignmentXMLParser.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/align/xml/MultipleAlignmentXMLParser.java @@ -169,7 +169,7 @@ else if ("ScoresCache".equals(child.getNodeName())){ } } //Because if it is 0 means that there were no transformations - if (transforms.size() != 0){ + if (!transforms.isEmpty()){ bs.setTransformations(transforms); } return bs; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java index 0f46b7f416..ed576b9abe 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java @@ -27,7 +27,6 @@ import org.slf4j.LoggerFactory; import javax.vecmath.Point3d; -import javax.vecmath.Vector3d; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -101,12 +100,56 @@ public void run() { } } - static class IndexAndDistance { - final int index; - final double dist; - IndexAndDistance(int index, double dist) { - this.index = index; - this.dist = dist; + /** + * The neighbors of a single atom, as parallel primitive arrays of neighbor atom indices and their distances to + * the central atom, sorted by increasing distance. + *

+ * Parallel primitive arrays are used rather than an array of index-distance objects because there are ~30 + * neighbors per atom: for a large structure that would mean millions of small short-lived objects. + */ + static class Neighbors { + + /** The neighbor atom indices, ordered by increasing distance to the central atom */ + final int[] indices; + /** The distances to the central atom, in increasing order and parallel to {@link #indices} */ + final double[] dists; + + private Neighbors(int[] indices, double[] dists) { + this.indices = indices; + this.dists = dists; + } + + /** + * Creates a Neighbors from the first count elements of the given buffers, copying them to exact-size arrays + * and sorting them by increasing distance. + * @param indicesBuffer the neighbor indices, only the first count elements are used + * @param distsBuffer the neighbor distances, only the first count elements are used + * @param count the number of neighbors + * @return the sorted neighbors + */ + static Neighbors createSorted(int[] indicesBuffer, double[] distsBuffer, int count) { + int[] indices = Arrays.copyOf(indicesBuffer, count); + double[] dists = Arrays.copyOf(distsBuffer, count); + // Sorting by closest to farthest away neighbors achieves faster runtimes when checking for occluded + // sphere sample points in calcSingleAsa. This follows the ideas exposed in + // Eisenhaber et al, J Comp Chemistry 1994 (https://onlinelibrary.wiley.com/doi/epdf/10.1002/jcc.540160303) + // This is essential for performance: it brings down the number of occlusion checks to + // an average of n_sphere_points/10 per atom, producing ~ x4 performance gain overall. + // An insertion sort is used because the arrays are small (~30 elements on average) and because it avoids + // both the boxing of a comparator-based sort and the object allocation an index-distance array would need. + for (int i = 1; i < count; i++) { + double dist = dists[i]; + int index = indices[i]; + int j = i - 1; + while (j >= 0 && dists[j] > dist) { + dists[j + 1] = dists[j]; + indices[j + 1] = indices[j]; + j--; + } + dists[j + 1] = dist; + indices[j + 1] = index; + } + return new Neighbors(indices, dists); } } @@ -116,9 +159,16 @@ static class IndexAndDistance { private final double[] radii; private final double probe; private final int nThreads; - private Vector3d[] spherePoints; + /** + * The sphere points to sample, as a flat array of interleaved x,y,z coordinates (thus of size 3 x nSpherePoints). + * A flat array of primitives (rather than an array of Vector3d objects) is used for performance: it keeps the + * points contiguous in memory and avoids a pointer dereference per point in the innermost loop of + * {@link #calcSingleAsa(int)}. + */ + private double[] spherePoints; + private int nSpherePoints; private double cons; - private IndexAndDistance[][] neighborIndices; + private Neighbors[] neighbors; private boolean useSpatialHashingForNeighbors; @@ -239,7 +289,8 @@ private void initSpherePoints(int nSpherePoints) { logger.debug("Will use {} sphere points", nSpherePoints); // initialising the sphere points to sample - spherePoints = generateSpherePoints(nSpherePoints); + this.nSpherePoints = nSpherePoints; + this.spherePoints = generateSpherePoints(nSpherePoints); cons = 4.0 * Math.PI / nSpherePoints; } @@ -285,10 +336,10 @@ public double[] calculateAsas() { long start = System.currentTimeMillis(); if (useSpatialHashingForNeighbors) { logger.debug("Will use spatial hashing to find neighbors"); - neighborIndices = findNeighborIndicesSpatialHashing(); + neighbors = findNeighborIndicesSpatialHashing(); } else { logger.debug("Will not use spatial hashing to find neighbors"); - neighborIndices = findNeighborIndices(); + neighbors = findNeighborIndices(); } long end = System.currentTimeMillis(); logger.debug("Took {} s to find neighbors", (end-start)/1000.0); @@ -334,109 +385,126 @@ void setUseSpatialHashingForNeighbors(boolean useSpatialHashingForNeighbors) { * Returns list of 3d coordinates of points on a unit sphere using the * Golden Section Spiral algorithm. * @param nSpherePoints the number of points to be used in generating the spherical dot-density - * @return the array of points as Vector3d objects + * @return a flat array of interleaved x,y,z coordinates, of size 3 x nSpherePoints */ - private Vector3d[] generateSpherePoints(int nSpherePoints) { - Vector3d[] points = new Vector3d[nSpherePoints]; + private double[] generateSpherePoints(int nSpherePoints) { + double[] points = new double[3 * nSpherePoints]; double inc = Math.PI * (3.0 - Math.sqrt(5.0)); double offset = 2.0 / nSpherePoints; for (int k=0;k thisNbIndices = new ArrayList<>(initialCapacity); + int count = 0; for (int i = 0; i < atomCoords.length; i++) { if (i == k) continue; double dist = atomCoords[i].distance(atomCoords[k]); - if (dist < radius + radii[i]) { - thisNbIndices.add(new IndexAndDistance(i, dist)); + if (areNeighbors(k, i, dist)) { + if (count == indicesBuffer.length) { + indicesBuffer = Arrays.copyOf(indicesBuffer, count * 2); + distsBuffer = Arrays.copyOf(distsBuffer, count * 2); + } + indicesBuffer[count] = i; + distsBuffer[count] = dist; + count++; } } - IndexAndDistance[] indicesArray = thisNbIndices.toArray(new IndexAndDistance[0]); - nbsIndices[k] = indicesArray; + nbs[k] = Neighbors.createSorted(indicesBuffer, distsBuffer, count); } - return nbsIndices; + return nbs; } /** - * Returns the 2-dimensional array with neighbor indices for every atom, + * Returns the neighbors of every atom, sorted by increasing distance, * using spatial hashing to avoid all to all distance calculation. - * @return 2-dimensional array of size: n_atoms x n_neighbors_per_atom + * @return array of size n_atoms */ - IndexAndDistance[][] findNeighborIndicesSpatialHashing() { - - // looking at a typical protein case, number of neighbours are from ~10 to ~50, with an average of ~30 - int initialCapacity = 60; + Neighbors[] findNeighborIndicesSpatialHashing() { List contactList = calcContacts(); - Map> indices = new HashMap<>(atomCoords.length); + + // A first pass to count the neighbors per atom, so that exact-size arrays can be allocated in the second + // pass. Atom indices are dense, so plain arrays are used rather than a map: that avoids boxing the indices + // and the repeated hashing, which are significant given that there are ~30 contacts per atom. + int[] counts = new int[atomCoords.length]; for (Contact contact : contactList) { // note contacts are stored 1-way only, with j>i int i = contact.getI(); int j = contact.getJ(); - - List iIndices; - List jIndices; - if (!indices.containsKey(i)) { - iIndices = new ArrayList<>(initialCapacity); - indices.put(i, iIndices); - } else { - iIndices = indices.get(i); - } - if (!indices.containsKey(j)) { - jIndices = new ArrayList<>(initialCapacity); - indices.put(j, jIndices); - } else { - jIndices = indices.get(j); - } - - double radius = radii[i] + probe + probe; - double dist = contact.getDistance(); - if (dist < radius + radii[j]) { - iIndices.add(new IndexAndDistance(j, dist)); - jIndices.add(new IndexAndDistance(i, dist)); + if (areNeighbors(i, j, contact.getDistance())) { + counts[i]++; + counts[j]++; } } - // convert map to array for fast access - IndexAndDistance[][] nbsIndices = new IndexAndDistance[atomCoords.length][]; - for (Map.Entry> entry : indices.entrySet()) { - List list = entry.getValue(); - IndexAndDistance[] indexAndDistances = list.toArray(new IndexAndDistance[0]); - nbsIndices[entry.getKey()] = indexAndDistances; + int[][] indices = new int[atomCoords.length][]; + double[][] dists = new double[atomCoords.length][]; + for (int i = 0; i < atomCoords.length; i++) { + // note that some atoms might have no neighbors at all, in which case these are empty arrays + indices[i] = new int[counts[i]]; + dists[i] = new double[counts[i]]; } - // important: some atoms might have no neighbors at all: we need to initialise to empty arrays - for (int i=0; i calcContacts() { private double calcSingleAsa(int i) { Point3d atom_i = atomCoords[i]; - int n_neighbor = neighborIndices[i].length; - IndexAndDistance[] neighbor_indices = neighborIndices[i]; - // Sorting by closest to farthest away neighbors achieves faster runtimes when checking for occluded - // sphere sample points below. This follows the ideas exposed in - // Eisenhaber et al, J Comp Chemistry 1994 (https://onlinelibrary.wiley.com/doi/epdf/10.1002/jcc.540160303) - // This is essential for performance. In my tests this brings down the number of occlusion checks in loop below to - // an average of n_sphere_points/10 per atom i, producing ~ x4 performance gain overall - Arrays.sort(neighbor_indices, Comparator.comparingDouble(o -> o.dist)); + // note the neighbors are already sorted by increasing distance (see Neighbors#createSorted), which is + // essential for the performance of the occlusion checks in the loop below + Neighbors nbs = neighbors[i]; + int[] neighbor_indices = nbs.indices; + double[] neighbor_dists = nbs.dists; + int n_neighbor = neighbor_indices.length; double radius_i = probe + radii[i]; @@ -476,36 +542,47 @@ private double calcSingleAsa(int i) { int[] numDistsCalced = null; if (logger.isDebugEnabled()) numDistsCalced = new int[n_neighbor]; - // now we precalculate anything depending only on i,j in equation 3 in Eisenhaber 1994 - double[] sqRadii = new double[n_neighbor]; - Vector3d[] aj_minus_ais = new Vector3d[n_neighbor]; + // Now we precalculate anything depending only on i,j in equation 3 in Eisenhaber 1994. + // The per-neighbor data is laid out in a single flat array as quadruplets + // [aj_minus_ai.x, aj_minus_ai.y, aj_minus_ai.z, cutoff], so that the innermost loop below is a purely + // sequential scan over contiguous primitives, with no object dereferencing. That matches the access + // pattern of the early break and is significantly faster than an array of Vector3d objects. + double[] nbData = new double[4 * n_neighbor]; for (int nbArrayInd =0; nbArrayInd> 2]++; - if (dotProd > sqRadii[nbArrayInd]) { + if (dotProd > nbData[off + 3]) { is_accessible = false; break; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/cath/CathInstallation.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/cath/CathInstallation.java index 3f0f44158b..4201f8d5ca 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/cath/CathInstallation.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/cath/CathInstallation.java @@ -31,8 +31,6 @@ import java.io.*; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.ParseException; @@ -54,7 +52,7 @@ public class CathInstallation implements CathDatabase{ public static final String nodeListFileName = "cath-names-v%s.txt"; public static final String domallFileName = "cath-domain-boundaries-v%s.txt"; - public static final String CATH_DOWNLOAD_URL = "http://download.cathdb.info/cath/releases/"; + public static final String CATH_DOWNLOAD_URL = "https://download.cathdb.info/cath/releases/"; public static final String CATH_DOWNLOAD_ALL_RELEASES_DIR = "all-releases"; public static final String CATH_DOWNLOAD_CLASSIFICATION_DATA_DIR = "cath-classification-data"; @@ -352,13 +350,15 @@ private void parseCathDomainList() throws IOException { parseCathDomainList(buffer); } - private void parseCathDomainList(BufferedReader bufferedReader) throws IOException{ + protected void parseCathDomainList(BufferedReader bufferedReader) throws IOException{ String line; - // int counter = 0; + int counter = 0; while ( (line = bufferedReader.readLine()) != null ) { if ( line.startsWith("#") ) continue; + if ( line.trim().isEmpty() ) continue; CathDomain cathDomain = parseCathListFileLine(line); - // counter++; + if ( cathDomain == null ) continue; + counter++; String pdbId = cathDomain.getPdbIdAndChain().substring(0,4); // includes chain letter @@ -374,6 +374,9 @@ private void parseCathDomainList(BufferedReader bufferedReader) throws IOExcepti domainMap.put( cathDomain.getDomainName(), cathDomain ); } + if (counter == 0) { + throw new IOException("Could not parse any CATH domains from the domain list file."); + } } private void parseCathNames() throws IOException { @@ -388,7 +391,9 @@ private void parseCathNames(BufferedReader bufferedReader) throws IOException{ //int counter = 0; while ( (line = bufferedReader.readLine()) != null ) { if ( line.startsWith("#") ) continue; + if ( line.trim().isEmpty() ) continue; CathNode cathNode = parseCathNamesFileLine(line); + if ( cathNode == null ) continue; cathTree.put(cathNode.getNodeId(), cathNode); } } @@ -415,6 +420,7 @@ private void parseCathDomainDescriptionFile(BufferedReader bufferedReader) throw StringBuilder sseqs = null; while ( (line = bufferedReader.readLine()) != null ) { if ( line.startsWith("#") ) continue; + if ( line.trim().isEmpty() ) continue; if ( line.startsWith("FORMAT") ) { cathDescription = new CathDomain(); cathDescription.setFormat( line.substring(10) ); @@ -506,8 +512,11 @@ private void parseCathDomainDescriptionFile(BufferedReader bufferedReader) throw }*/ private CathDomain parseCathListFileLine(String line) { + String [] token = line.trim().split("\\s+"); + if (token.length < 12) { + return null; + } CathDomain cathDomain = new CathDomain(); - String [] token = line.split("\\s+"); cathDomain.setDomainName(token[0]); cathDomain.setClassId(Integer.parseInt(token[1])); cathDomain.setArchitectureId(Integer.parseInt(token[2])); @@ -524,8 +533,12 @@ private CathDomain parseCathListFileLine(String line) { } private CathNode parseCathNamesFileLine(String line) { + String[] token = line.trim().split("\\s+",3); + if (token.length < 3) { + LOGGER.debug("Invalid line in cath names file, was expecting 3 tokens but got {} tokens: {}", token.length, line); + return null; + } CathNode cathNode = new CathNode(); - String[] token = line.split("\\s+",3); cathNode.setNodeId( token[0] ); int idx = token[0].lastIndexOf("."); if ( idx == -1 ) idx = token[0].length(); @@ -546,8 +559,8 @@ private void parseCathDomall(BufferedReader bufferedReader) throws IOException{ String line; while ( ((line = bufferedReader.readLine()) != null) ) { if ( line.startsWith("#") ) continue; - if ( line.length() == 0 ) continue; - String[] token = line.split("\\s+"); + if ( line.trim().isEmpty() ) continue; + String[] token = line.trim().split("\\s+"); String chainId = token[0]; Integer numberOfDomains = Integer.parseInt( token[1].substring(1) ); Integer numberOfFragments = Integer.parseInt( token[2].substring(1) ); @@ -637,27 +650,26 @@ private void parseCathDomall(BufferedReader bufferedReader) throws IOException{ } protected void downloadFileFromRemote(URL remoteURL, File localFile) throws IOException{ -// System.out.println("downloading " + remoteURL + " to: " + localFile); LOGGER.info("Downloading file {} to local file {}", remoteURL, localFile); long timeS = System.currentTimeMillis(); - File tempFile = Files.createTempFile(FileDownloadUtils.getFilePrefix(localFile),"." + FileDownloadUtils.getFileExtension(localFile)).toFile(); - FileOutputStream out = new FileOutputStream(tempFile); - - InputStream in = remoteURL.openStream(); - byte[] buf = new byte[4 * 1024]; // 4K buffer - int bytesRead; - while ((bytesRead = in.read(buf)) != -1) { - out.write(buf, 0, bytesRead); + File parent = localFile.getAbsoluteFile().getParentFile(); + if (parent != null && !parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Could not create directory " + parent); } - in.close(); - out.close(); - Files.copy(tempFile.toPath(), localFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + // Previously this read the response with a bare remoteURL.openStream() and + // copied whatever came back. That silently accepted error and redirect + // responses: when download.cathdb.info began redirecting http to https, the + // body of the 301 was written here as though it were classification data, + // and the failure only surfaced much later as an unparseable file. + FileDownloadUtils.downloadFileWithValidation(remoteURL, localFile, null, + FileDownloadUtils.Hash.UNKNOWN, FileDownloadUtils.ETagPolicy.USE_IF_HEX_DIGEST); - // delete the tmp file - tempFile.delete(); + if (!FileDownloadUtils.validateFile(localFile)) { + throw new IOException("Downloaded file invalid: " + localFile); + } long size = localFile.length(); @@ -674,25 +686,25 @@ protected void downloadFileFromRemote(URL remoteURL, File localFile) throws IOEx private boolean domainDescriptionFileAvailable(){ String fileName = getDomainDescriptionFileName(); File f = new File(fileName); - return f.exists(); + return f.exists() && FileDownloadUtils.validateFile(f); } private boolean domainListFileAvailable(){ String fileName = getDomainListFileName(); File f = new File(fileName); - return f.exists(); + return f.exists() && FileDownloadUtils.validateFile(f); } private boolean nodeListFileAvailable(){ String fileName = getNodeListFileName(); File f = new File(fileName); - return f.exists(); + return f.exists() && FileDownloadUtils.validateFile(f); } private boolean domallFileAvailable() { String fileName = getDomallFileName(); File f= new File(fileName); - return f.exists(); + return f.exists() && FileDownloadUtils.validateFile(f); } protected void downloadDomainListFile() throws IOException{ diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/DownloadChemCompProvider.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/DownloadChemCompProvider.java index 9eb9c7c6cf..e19535bce8 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/DownloadChemCompProvider.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/DownloadChemCompProvider.java @@ -1,5 +1,6 @@ package org.biojava.nbio.structure.chem; +import org.biojava.nbio.core.util.FileDownloadUtils; import org.biojava.nbio.core.util.InputStreamProvider; import org.biojava.nbio.structure.align.util.URLConnectionTools; import org.biojava.nbio.structure.align.util.UserConfiguration; @@ -396,6 +397,17 @@ private static boolean downloadChemCompRecord(String recordName) { url = new URL(u); URLConnection uconn = URLConnectionTools.openURLConnection(url); + // A 4xx or 5xx already fails safely, because getInputStream() throws for + // those. A redirect does not: if the server answers 3xx and the JDK + // declines to follow it - which it always does when the redirect changes + // http to https - getInputStream() hands back the body of the redirect + // instead. That body is short but not empty, so the "did we read any + // lines" check below accepts it, and it is gzipped and stored under the + // component's name. Every later lookup then reads it back and fails to + // parse, long after the request that caused it. This is exactly how the + // CATH downloader broke when download.cathdb.info moved to https. + FileDownloadUtils.checkHttpStatus(uconn); + try (PrintWriter pw = new PrintWriter(new GZIPOutputStream(new FileOutputStream(newFile))); BufferedReader fileBuffer = new BufferedReader(new InputStreamReader(uconn.getInputStream()))) { String line; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/ZipChemCompProvider.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/ZipChemCompProvider.java index 4fe19aca58..a68019efb7 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/ZipChemCompProvider.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/chem/ZipChemCompProvider.java @@ -120,7 +120,7 @@ public ChemComp getChemComp(String recordName) { } // If a null record or an empty chemcomp, return a default ChemComp and blacklist. - if (cc == null || (null == cc.getName() && cc.getAtoms().size() == 0)) { + if (cc == null || (null == cc.getName() && cc.getAtoms().isEmpty())) { s_logger.info("Unable to find or download {} - excluding from future searches.", recordName); unavailable.add(recordName); return getEmptyChemComp(recordName); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitCluster.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitCluster.java index 9a87e92f88..87c3c06e3c 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitCluster.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitCluster.java @@ -331,7 +331,7 @@ public boolean mergeIdenticalByEntityId(SubunitCluster other) { } } - if (thisAligned.size() == 0 && otherAligned.size() == 0) { + if (thisAligned.isEmpty() && otherAligned.isEmpty()) { logger.warn("No equivalent aligned atoms found between SubunitClusters {}-{} via entity SEQRES alignment. Is FileParsingParameters.setAlignSeqRes() set?", thisName, otherName); } @@ -507,27 +507,24 @@ public boolean mergeStructure(SubunitCluster other, SubunitClustererParameters p } } - AFPChain afp = aligner.align(this.subunits.get(this.representative) - .getRepresentativeAtoms(), - other.subunits.get(other.representative) - .getRepresentativeAtoms()); + AFPChain afp = aligner.align(this.subunits.get(this.representative).getRepresentativeAtoms(), + other.subunits.get(other.representative).getRepresentativeAtoms()); + String pairName = this.subunits.get(this.representative).getName() + "-" + other.subunits.get(other.representative).getName(); if (afp.getOptLength() < 1) { // alignment failed (eg if chains were too short) throw new StructureException( - String.format("Subunits failed to align using %s", params.getSuperpositionAlgorithm())); + String.format("Subunits %s failed to align using %s", pairName, params.getSuperpositionAlgorithm())); } // Convert AFPChain to MultipleAlignment for convenience MultipleAlignment msa = new MultipleAlignmentEnsembleImpl( afp, this.subunits.get(this.representative).getRepresentativeAtoms(), - other.subunits.get(other.representative) - .getRepresentativeAtoms(), false) - .getMultipleAlignment(0); + other.subunits.get(other.representative).getRepresentativeAtoms(), + false).getMultipleAlignment(0); - double structureCoverage = Math.min(msa.getCoverages().get(0), msa - .getCoverages().get(1)); + double structureCoverage = Math.min(msa.getCoverages().get(0), msa.getCoverages().get(1)); if(params.isUseStructureCoverage() && structureCoverage < params.getStructureCoverageThreshold()) { return false; @@ -543,8 +540,7 @@ public boolean mergeStructure(SubunitCluster other, SubunitClustererParameters p return false; } - logger.info(String.format("SubunitClusters are structurally similar with " - + "%.2f RMSD %.2f coverage", rmsd, structureCoverage)); + logger.info("SubunitClusters {} are structurally similar with [ {} ] RMSD and [ {} ] coverage", pairName, String.format("%.2f", rmsd), String.format("%.2f", structureCoverage)); // Merge clusters List> alignedRes = msa.getBlock(0).getAlignRes(); @@ -565,13 +561,18 @@ public boolean mergeStructure(SubunitCluster other, SubunitClustererParameters p // Only consider residues that are part of the SubunitCluster if (this.subunitEQR.get(this.representative).contains(thisIndex) - && other.subunitEQR.get(other.representative).contains( - otherIndex)) { + && other.subunitEQR.get(other.representative).contains(otherIndex)) { thisAligned.add(thisIndex); otherAligned.add(otherIndex); } } + // this can happen in very rare cases, e.g. 9y9z when merging E_1 into the cluster D_1, OM_1, Y_1 + if (thisAligned.isEmpty() && otherAligned.isEmpty()) { + logger.warn("No equivalent aligned atoms found between SubunitClusters {} via structure alignment. Will not merge the second one into the first.", pairName); + return false; + } + updateEquivResidues(other, thisAligned, otherAligned); this.method = SubunitClustererMethod.STRUCTURE; @@ -602,18 +603,12 @@ private void updateEquivResidues(SubunitCluster other, List thisAligned Collections.sort(otherRemove); Collections.reverse(otherRemove); - for (int t = 0; t < thisRemove.size(); t++) { - for (List eqr : this.subunitEQR) { - int column = thisRemove.get(t); - eqr.remove(column); - } + for (int column : thisRemove) { + this.subunitEQR.forEach(eqr -> eqr.remove(column)); } - for (int t = 0; t < otherRemove.size(); t++) { - for (List eqr : other.subunitEQR) { - int column = otherRemove.get(t); - eqr.remove(column); - } + for (int column : otherRemove) { + other.subunitEQR.forEach(eqr -> eqr.remove(column)); } // The representative is the longest sequence diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitClusterer.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitClusterer.java index 6295f8fdf0..fa63b96d96 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitClusterer.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/cluster/SubunitClusterer.java @@ -58,7 +58,7 @@ public static Stoichiometry cluster(Structure structure, public static Stoichiometry cluster(List subunits, SubunitClustererParameters params) { List clusters = new ArrayList<>(); - if (subunits.size() == 0) + if (subunits.isEmpty()) return new Stoichiometry(clusters); // First generate a new cluster for each Subunit @@ -83,8 +83,7 @@ public static Stoichiometry cluster(List subunits, SubunitClustererPara } } catch (CompoundNotFoundException e) { - logger.warn("Could not merge by Sequence. {}", - e.getMessage()); + logger.info("Could not merge by Sequence. {}", e.getMessage()); } } } @@ -100,7 +99,7 @@ public static Stoichiometry cluster(List subunits, SubunitClustererPara clusters.remove(c2); } } catch (StructureException e) { - logger.warn("Could not merge by Structure. {}", e.getMessage()); + logger.info("Could not merge by Structure. {}", e.getMessage()); } } } @@ -112,8 +111,7 @@ public static Stoichiometry cluster(List subunits, SubunitClustererPara try { clusters.get(c).divideInternally(params); } catch (StructureException e) { - logger.warn("Error analyzing internal symmetry. {}", - e.getMessage()); + logger.info("Error analyzing internal symmetry. {}", e.getMessage()); } } @@ -125,8 +123,7 @@ public static Stoichiometry cluster(List subunits, SubunitClustererPara if (clusters.get(c1).mergeStructure(clusters.get(c2), params)) clusters.remove(c2); } catch (StructureException e) { - logger.warn("Could not merge by Structure. {}", - e.getMessage()); + logger.info("Could not merge by Structure. {}", e.getMessage()); } } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/GroupContact.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/GroupContact.java index 07b163730d..245d4481f6 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/GroupContact.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/GroupContact.java @@ -57,7 +57,7 @@ public void setPair(Pair pair) { } public double getMinDistance() { - if (atomContacts.size()==0) return 0; + if (atomContacts.isEmpty()) return 0; double minDistance = Double.MAX_VALUE; for (AtomContact atomContact:atomContacts) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/StructureInterfaceList.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/StructureInterfaceList.java index 60f7c3a91b..00cf7ef65c 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/StructureInterfaceList.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/contact/StructureInterfaceList.java @@ -380,7 +380,7 @@ public List getClusters(double contactOverlapScoreClu clusters = new ArrayList<>(); // nothing to do if we have no interfaces - if (list.size()==0) return clusters; + if (list.isEmpty()) return clusters; logger.debug("Calculating all-vs-all Jaccard scores for {} interfaces", list.size()); double[][] matrix = new double[list.size()][list.size()]; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/ecod/EcodInstallation.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/ecod/EcodInstallation.java index f4be5cd4f5..027907ffa6 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/ecod/EcodInstallation.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/ecod/EcodInstallation.java @@ -137,9 +137,15 @@ public List getDomainsForPdb(String id) throws IOException { // unlock to allow ensureDomainsFileInstalled to get the write lock logger.trace("UNLOCK readlock"); domainsFileLock.readLock().unlock(); - indexDomains(); - domainsFileLock.readLock().lock(); - logger.trace("LOCK readlock"); + try { + indexDomains(); + } finally { + // re-acquire even if indexing failed, so the outer finally has a + // lock to release; otherwise IllegalMonitorStateException replaces + // the real cause and the failure becomes unreadable + domainsFileLock.readLock().lock(); + logger.trace("LOCK readlock"); + } } PdbId pdbId = null; @@ -244,9 +250,15 @@ public List getAllDomains() throws IOException { // unlock to allow ensureDomainsFileInstalled to get the write lock logger.trace("UNLOCK readlock"); domainsFileLock.readLock().unlock(); - ensureDomainsFileInstalled(); - domainsFileLock.readLock().lock(); - logger.trace("LOCK readlock"); + try { + ensureDomainsFileInstalled(); + } finally { + // re-acquire even if the download failed, so the outer finally has a + // lock to release; otherwise IllegalMonitorStateException replaces + // the real cause and the failure becomes unreadable + domainsFileLock.readLock().lock(); + logger.trace("LOCK readlock"); + } } return allDomains; } finally { @@ -272,12 +284,41 @@ public void clear() { * * Note that this may differ from the version requested in the constructor * for the special case of "latest" + *

+ * Since 7.3.0 this reads only the file's header rather than parsing the whole + * file, so it no longer has the side effect of loading every domain. * @return the ECOD version * @throws IOException If an error occurs while downloading or parsing the file */ @Override public String getVersion() throws IOException { - ensureDomainsFileInstalled(); + domainsFileLock.readLock().lock(); + logger.trace("LOCK readlock"); + try { + if( parsedVersion != null ) { + return parsedVersion; + } + } finally { + logger.trace("UNLOCK readlock"); + domainsFileLock.readLock().unlock(); + } + + // The version is declared in the first few lines of the file, so read those rather + // than the millions of domain records behind them. The current release is 657 MB and + // holds nearly three million records; parsing it in full to answer this question + // costs over a gigabyte of heap and several seconds. + ensureDomainsFileDownloaded(); + + domainsFileLock.writeLock().lock(); + logger.trace("LOCK writelock"); + try { + if( parsedVersion == null ) { + parsedVersion = parseVersionOnly(); + } + } finally { + logger.trace("UNLOCK writelock"); + domainsFileLock.writeLock().unlock(); + } if( parsedVersion == null) { return requestedVersion; @@ -285,6 +326,30 @@ public String getVersion() throws IOException { return parsedVersion; } + /** + * Reads the version from the header of the local domains file without parsing the + * domains themselves. + * @return the version, or null if the header does not declare one + * @throws IOException if the file cannot be read + * @since 7.3.0 + */ + private String parseVersionOnly() throws IOException { + try( BufferedReader in = new BufferedReader(new FileReader(getDomainFile())) ) { + String line; + while( (line = in.readLine()) != null ) { + Matcher match = EcodParser.VERSION_RE.matcher(line); + if( match.matches() ) { + return match.group(1); + } + if( !line.startsWith("#") ) { + // past the header block; from v294.1 the column names are not commented + return null; + } + } + } + return null; + } + /** * Get the top-level ECOD server URL. Defaults to "http://prodata.swmed.edu" * @return the url to the ecod server @@ -325,6 +390,24 @@ public void setCacheLocation(String cacheLocation) { domainsFileLock.writeLock().unlock(); } + /** + * Ensures the domains file is present and current locally, without parsing it. + * @throws IOException in cases of file I/O, including failure to download a healthy file + * @since 7.3.0 + */ + private void ensureDomainsFileDownloaded() throws IOException { + domainsFileLock.writeLock().lock(); + logger.trace("LOCK writelock"); + try { + if( !domainsAvailable() ) { + downloadDomains(); + } + } finally { + logger.trace("UNLOCK writelock"); + domainsFileLock.writeLock().unlock(); + } + } + /** * Blocks until ECOD domains file has been downloaded and parsed. * @@ -549,6 +632,24 @@ Current version (1.4) contains the following columns: v1.2 - added f-group identifiers to fasta file, domain description file. ECODf identifiers now used when available for F-group name. Domain assemblies now represented by assembly uid in domain assembly status. v1.4 - added seqid_range and headers (develop101) +v1.6 - renamed column 4 from f_id to t_id and inserted unp_acc (UniProt accession) as + column 9, giving 16 columns (seen in develop291) + +From v294.1 the distribution was redesigned. The header comment changed from +"#ECOD version develop291" to "# Version: v294.1", the column header row is no longer +commented out, and the columns became: + + uid ecod_domain_id manual_rep f_id pdb chain pdb_range seqid_range architecture_name + x_name h_name t_name f_name assembly_id domain_id_short range_count arch_manual + x_manual h_manual t_manual f_manual valid_structure ligand_binding + +v295 appends ligand_comp_ids and ligand_pdbnum, for 25 columns. Also note that +manual_rep now holds True/False rather than MANUAL_REP/AUTO_NONREP, that assembly_id +and domain_id_short are empty on every row, that f_name is empty rather than +F_UNCLASSIFIED for unclassified domains, and that uid restarts from 0. + +Because the columns have been renamed, reordered and added to repeatedly, files that +declare a column header are read by column name rather than by position. */ /** String for unclassified F-groups */ @@ -561,10 +662,28 @@ Current version (1.4) contains the following columns: public static final String IS_REPRESENTATIVE = "MANUAL_REP"; /** Indicates not a manual representative */ public static final String NOT_REPRESENTATIVE = "AUTO_NONREP"; + /** + * Matches the comment declaring the version, which has taken two forms: + * {@code #ECOD version develop291} up to develop292, and {@code # Version: v295} + * from v294.1 onwards. + * @since 7.3.0 + */ + static final Pattern VERSION_RE = Pattern.compile( + "^\\s*#\\s*(?:ECOD\\s+)?version\\s*:?\\s*(\\S+).*", Pattern.CASE_INSENSITIVE); private List domains; private String version; + // prevent too many warnings; negative numbers print all warnings + private int warnIsDomainAssembly = 1; + private int warnHierarchicalFormat = 5; + private int warnNumberOfFields = 10; + private int warnNumberFormat = 10; + /** Data lines that could not be turned into a domain, for the summary at the end */ + private int skippedLines = 0; + /** Data lines describing a domain in a computed model rather than a PDB entry */ + private int modelLines = 0; + public EcodParser(String filename) throws IOException { this(new File(filename)); } @@ -584,30 +703,55 @@ private void parse(BufferedReader in) throws IOException { // Allocate plenty of space for ECOD as of 2015 ArrayList domainsList = new ArrayList<>(500000); - Pattern versionRE = Pattern.compile("^\\s*#.*ECOD\\s*version\\s+(\\S+).*"); Pattern commentRE = Pattern.compile("^\\s*#.*"); - // prevent too many warnings; negative numbers print all warnings - int warnIsDomainAssembly = 1; - int warnHierarchicalFormat = 5; - int warnNumberOfFields = 10; + ColumnLayout layout = null; String line = in.readLine(); int lineNum = 1; while( line != null ) { // Check for requestedVersion string - Matcher match = versionRE.matcher(line); + Matcher match = VERSION_RE.matcher(line); if(match.matches()) { // special requestedVersion comment this.version = match.group(1); + } else if( ColumnLayout.isColumnHeader(line) ) { + // The column names. Since the columns have been renamed, reordered and + // added to several times, later lines are read by name rather than by + // position wherever this header is present (develop101 onwards). + layout = ColumnLayout.fromHeader(line); + logger.debug("Read ECOD column header at line {}: {} columns",lineNum,layout.size()); } else { match = commentRE.matcher(line); if(match.matches()) { // ignore comments } else { - // data line - String[] fields = line.split("\t"); - if( fields.length == 13 || fields.length == 14 || fields.length == 15) { + // data line. The last column is frequently empty, so keep trailing + // empty fields rather than letting split() discard them. + String[] fields = line.split("\t", -1); + if( layout != null ) { + String pdb = layout.get(fields, "pdb"); + if( pdb != null && pdb.isEmpty() ) { + // From v294.1 the distribution also classifies domains + // found in computed (AlphaFold) models, which have no PDB + // entry and so cannot be represented by an EcodDomain. + modelLines++; + } else { + try { + EcodDomain domain = parseDomain(fields, layout, lineNum); + if(domain != null) { + domainsList.add(domain); + } else { + skippedLines++; + warnMissingColumns(lineNum); + } + } catch(IllegalArgumentException e) { + // includes NumberFormatException and an unusable PDB id + skippedLines++; + warnUnparseableLine(lineNum, e); + } + } + } else if( fields.length == 13 || fields.length == 14 || fields.length == 15) { try { int i = 0; // field number, to allow future insertion of fields @@ -620,32 +764,16 @@ private void parse(BufferedReader in) throws IOException { // Manual column may be missing in version 1.0 files Boolean manual = null; if( fields.length >= 14) { - String manualString = fields[i++]; - if(manualString.equalsIgnoreCase(IS_REPRESENTATIVE)) { - manual = true; - } else if(manualString.equalsIgnoreCase(NOT_REPRESENTATIVE)) { - manual = false; - } else { - logger.warn("Unexpected value for manual field: {} in line {}",manualString,lineNum); - } + manual = parseManualRep(fields[i++], lineNum); } //Column 4: ECOD hierachy identifier - [X-group].[H-group].[T-group].[F-group] // hierarchical field, e.g. "1.1.4.1" - String[] xhtGroup = fields[i++].split("\\."); - if(xhtGroup.length < 3 || 4 < xhtGroup.length) { - if(warnHierarchicalFormat > 1) { - logger.warn("Unexpected format for hierarchical field \"{}\" in line {}",fields[i-1],lineNum); - warnHierarchicalFormat--; - } else if(warnHierarchicalFormat != 0) { - logger.warn("Unexpected format for hierarchical field \"{}\" in line {}. Not printing future similar warnings.",fields[i-1],lineNum); - warnHierarchicalFormat--; - } - } - Integer xGroup = xhtGroup.length>0 ? Integer.parseInt(xhtGroup[0]) : null; - Integer hGroup = xhtGroup.length>1 ? Integer.parseInt(xhtGroup[1]) : null; - Integer tGroup = xhtGroup.length>2 ? Integer.parseInt(xhtGroup[2]) : null; - Integer fGroup = xhtGroup.length>3 ? Integer.parseInt(xhtGroup[3]) : null; + Integer[] xhtfGroup = parseHierarchy(fields[i++], lineNum); + Integer xGroup = xhtfGroup[0]; + Integer hGroup = xhtfGroup[1]; + Integer tGroup = xhtfGroup[2]; + Integer fGroup = xhtfGroup[3]; //Column 5: PDB identifier String pdbId = fields[i++]; @@ -699,32 +827,18 @@ private void parse(BufferedReader in) throws IOException { assemblyId = Long.parseLong(assemblyStr); } - String ligandStr = fields[i++]; - Set ligands = null; - if( "NO_LIGANDS_4A".equals(ligandStr) || ligandStr.isEmpty() ) { - ligands = Collections.emptySet(); - } else { - String[] ligSplit = ligandStr.split(","); - ligands = new LinkedHashSet<>(ligSplit.length); - for(String s : ligSplit) { - ligands.add(s.intern()); - } - } + Set ligands = parseLigands(fields[i++]); EcodDomain domain = new EcodDomain(uid, domainId, manual, xGroup, hGroup, tGroup, fGroup,pdbId, chainId, range, seqId, architectureName, xGroupName, hGroupName, tGroupName, fGroupName, assemblyId, ligands); domainsList.add(domain); } catch(NumberFormatException e) { - logger.warn("Error in ECOD parsing at line "+lineNum,e); + skippedLines++; + warnUnparseableLine(lineNum, e); } } else { - if(warnNumberOfFields > 1) { - logger.warn("Unexpected number of fields in line {}.",lineNum); - warnNumberOfFields--; - } else if(warnNumberOfFields == 0) { - logger.warn("Unexpected number of fields in line {}. Not printing future similar warnings",lineNum); - warnIsDomainAssembly--; - } + skippedLines++; + warnMissingColumns(lineNum); } } } @@ -737,6 +851,23 @@ private void parse(BufferedReader in) throws IOException { else logger.info("Parsed {} ECOD domains from version {}",domainsList.size(),this.version); + if(modelLines > 0) { + logger.info("Ignored {} ECOD domains classified from computed models, " + + "which have no PDB entry", modelLines); + } + + if(domainsList.isEmpty() && skippedLines > 0) { + // Returning an empty list quietly is how an upstream format change went + // unnoticed for eight months. Say so instead. + logger.error("Parsed no ECOD domains from {} data lines of version {}. " + + "The file format has probably changed; please report this at " + + "https://github.com/biojava/biojava/issues", skippedLines, + this.version == null ? "unknown" : this.version); + } else if(skippedLines > 0) { + logger.warn("Skipped {} of {} ECOD data lines that could not be parsed", + skippedLines, skippedLines + domainsList.size()); + } + this.domains = Collections.unmodifiableList( domainsList ); @@ -747,6 +878,169 @@ private void parse(BufferedReader in) throws IOException { } } + /** + * Builds a domain from a data line using the column names the file declares in its + * header, rather than fixed offsets. This is what allows one parser to read the + * 15-column develop101 layout, the 16-column develop291 layout (which inserts + * {@code unp_acc}) and the 23- and 25-column v294.1 and v295 layouts. + * @param fields the tab-separated values of one data line + * @param layout the column names read from the file's header + * @param lineNum the line number, for warnings + * @return the domain, or null if the line does not carry every required column + * @throws NumberFormatException if a numeric column does not hold a number + * @since 7.3.0 + */ + private EcodDomain parseDomain(String[] fields, ColumnLayout layout, int lineNum) { + String uidStr = layout.get(fields, "uid"); + String domainId = layout.get(fields, "ecod_domain_id"); + // renamed from t_id to f_id when the hierarchy gained a fourth level + String hierarchy = layout.get(fields, "f_id", "t_id"); + String pdbId = layout.get(fields, "pdb"); + String chainId = layout.get(fields, "chain"); + String range = layout.get(fields, "pdb_range"); + if( uidStr == null || domainId == null || hierarchy == null + || pdbId == null || chainId == null || range == null ) { + return null; + } + + Long uid = Long.parseLong(uidStr); + Boolean manual = parseManualRep(layout.get(fields, "manual_rep"), lineNum); + Integer[] xhtfGroup = parseHierarchy(hierarchy, lineNum); + // absent before version 1.4 + String seqId = layout.get(fields, "seqid_range"); + + String architectureName = internName(layout.get(fields, "architecture_name", "arch_name")); + String xGroupName = internName(layout.get(fields, "x_name")); + String hGroupName = internName(layout.get(fields, "h_name")); + String tGroupName = internName(layout.get(fields, "t_name")); + // Up to develop292 an unclassified domain carried F_UNCLASSIFIED here. From + // v294.1 the name is simply empty, while f_id still classifies the domain to + // four levels, so the two are no longer equivalent and the empty value is + // deliberately left as it is rather than translated. + String fGroupName = internName(layout.get(fields, "f_name")); + + // v294.1 and later declare assembly_id but leave it empty on every row, which + // means the same as the NOT_DOMAIN_ASSEMBLY of earlier versions. + Long assemblyId = null; + String assemblyStr = layout.get(fields, "assembly_id", "asm_status"); + if( assemblyStr == null || assemblyStr.isEmpty() || NOT_DOMAIN_ASSEMBLY.equals(assemblyStr) ) { + assemblyId = uid; + } else if( IS_DOMAIN_ASSEMBLY.equals(assemblyStr) ) { + warnDomainAssembly(lineNum); + } else { + assemblyId = Long.parseLong(assemblyStr); + } + + // the ligand list moved from the last column to ligand_comp_ids in v295 + Set ligands = parseLigands(layout.get(fields, "ligand_comp_ids", "ligand")); + + return new EcodDomain(uid, domainId, manual, xhtfGroup[0], xhtfGroup[1], xhtfGroup[2], + xhtfGroup[3], pdbId, chainId, range, seqId, architectureName, xGroupName, + hGroupName, tGroupName, fGroupName, assemblyId, ligands); + } + + /** + * Reads the representative-status column, which held MANUAL_REP or AUTO_NONREP up to + * develop292 and True or False from v294.1 onwards. + * @return true, false, or null if the column is absent or unrecognised + * @since 7.3.0 + */ + private Boolean parseManualRep(String manualString, int lineNum) { + if(manualString == null) { + return null; + } + if(manualString.equalsIgnoreCase(IS_REPRESENTATIVE) || manualString.equalsIgnoreCase("true")) { + return true; + } + if(manualString.equalsIgnoreCase(NOT_REPRESENTATIVE) || manualString.equalsIgnoreCase("false")) { + return false; + } + logger.warn("Unexpected value for manual field: {} in line {}",manualString,lineNum); + return null; + } + + /** + * Splits the hierarchical identifier, e.g. "1.1.4.1". + * @return the X, H, T and F group numbers, any of which may be null if absent + * @since 7.3.0 + */ + private Integer[] parseHierarchy(String hierarchy, int lineNum) { + String[] xhtGroup = hierarchy.split("\\."); + if(xhtGroup.length < 3 || 4 < xhtGroup.length) { + if(warnHierarchicalFormat > 1) { + logger.warn("Unexpected format for hierarchical field \"{}\" in line {}",hierarchy,lineNum); + warnHierarchicalFormat--; + } else if(warnHierarchicalFormat != 0) { + logger.warn("Unexpected format for hierarchical field \"{}\" in line {}. Not printing future similar warnings.",hierarchy,lineNum); + warnHierarchicalFormat--; + } + } + Integer[] groups = new Integer[4]; + for(int j = 0; j < groups.length && j < xhtGroup.length; j++) { + groups[j] = Integer.parseInt(xhtGroup[j]); + } + return groups; + } + + /** + * Reads a comma-separated list of non-polymer entities close to the domain. + * @return the ligands, or an empty set for NO_LIGANDS_4A, an empty value or no column + * @since 7.3.0 + */ + private Set parseLigands(String ligandStr) { + if( ligandStr == null || ligandStr.isEmpty() || "NO_LIGANDS_4A".equals(ligandStr) ) { + return Collections.emptySet(); + } + String[] ligSplit = ligandStr.split(","); + Set ligands = new LinkedHashSet<>(ligSplit.length); + for(String s : ligSplit) { + ligands.add(s.intern()); + } + return ligands; + } + + /** + * Interns a name likely to be shared by many domains, stripping the quotes that + * versions up to develop292 wrapped some of them in. + * @since 7.3.0 + */ + private String internName(String name) { + if(name == null) { + return null; + } + return clearStringQuotes(name).intern(); + } + + private void warnDomainAssembly(int lineNum) { + if(warnIsDomainAssembly > 1) { + logger.info("Deprecated 'IS_DOMAIN_ASSEMBLY' value ignored in line {}.",lineNum); + warnIsDomainAssembly--; + } else if(warnIsDomainAssembly == 0) { + logger.info("Deprecated 'IS_DOMAIN_ASSEMBLY' value ignored in line {}. Not printing future similar warnings.",lineNum); + warnIsDomainAssembly--; + } + } + + private void warnMissingColumns(int lineNum) { + if(warnNumberOfFields > 1) { + logger.warn("Unexpected number of fields in line {}.",lineNum); + warnNumberOfFields--; + } else if(warnNumberOfFields == 1) { + logger.warn("Unexpected number of fields in line {}. Not printing future similar warnings",lineNum); + warnNumberOfFields--; + } + } + + private void warnUnparseableLine(int lineNum, IllegalArgumentException e) { + if(warnNumberFormat > 1) { + logger.warn("Error in ECOD parsing at line {}: {}", lineNum, e.getMessage()); + warnNumberFormat--; + } else if(warnNumberFormat == 1) { + logger.warn("Error in ECOD parsing at line {}: {}. Not printing future similar warnings", lineNum, e.getMessage()); + warnNumberFormat--; + } + } + private String clearStringQuotes(String name) { if ( name.startsWith("\"")) name = name.substring(1); @@ -770,6 +1064,77 @@ public List getDomains() { public String getVersion() { return version; } + + /** + * Maps the column names an ECOD domain file declares in its header onto their + * positions, so a data line can be read by name rather than by offset. + *

+ * Every distribution since develop101 carries such a header. It is commented + * (#uid<tab>ecod_domain_id<tab>...) up to develop292 and + * uncommented (uid<tab>ecod_domain_id<tab>...) from v294.1 + * onwards. Because names have also been changed between versions, lookups accept + * aliases and any name the file does not declare simply reads as absent. + * + * @author Amr ALHOSSARY + * @since 7.3.0 + */ + private static class ColumnLayout { + private final Map columns; + + private ColumnLayout(Map columns) { + this.columns = columns; + } + + /** + * @return true if this line names the columns rather than holding domain data + */ + public static boolean isColumnHeader(String line) { + int tab = line.indexOf('\t'); + if(tab < 0) { + return false; + } + String first = line.substring(0, tab).trim(); + if(first.startsWith("#")) { + first = first.substring(1).trim(); + } + return first.equalsIgnoreCase("uid"); + } + + public static ColumnLayout fromHeader(String line) { + String[] names = line.split("\t", -1); + Map columns = new HashMap<>(names.length * 2); + for(int i = 0; i < names.length; i++) { + String name = names[i].trim(); + if(i == 0 && name.startsWith("#")) { + name = name.substring(1).trim(); + } + if(!name.isEmpty()) { + columns.put(name.toLowerCase(), i); + } + } + return new ColumnLayout(columns); + } + + /** + * @param fields the values of one data line + * @param aliases the names this column has gone by, most recent first + * @return the value of the first alias the file declares, or null if it declares + * none of them or this line is too short to reach it + */ + public String get(String[] fields, String... aliases) { + for(String alias : aliases) { + Integer i = columns.get(alias); + if(i != null) { + return i < fields.length ? fields[i] : null; + } + } + return null; + } + + public int size() { + return columns.size(); + } + } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/geometry/MomentsOfInertia.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/geometry/MomentsOfInertia.java index 8cfd032daa..5a6e69c4cf 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/geometry/MomentsOfInertia.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/geometry/MomentsOfInertia.java @@ -72,7 +72,7 @@ public void addPoint(Point3d point, double mass) { public Point3d getCenterOfMass() { - if (points.size() == 0) { + if (points.isEmpty()) { throw new IllegalStateException( "MomentsOfInertia: no points defined"); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/BondMaker.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/BondMaker.java index e6b8548025..e81f7fb867 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/BondMaker.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/BondMaker.java @@ -273,7 +273,7 @@ private void trimBondLists() { for (Chain chain : structure.getChains(modelInd)) { for (Group group : chain.getAtomGroups()) { for (Atom atom : group.getAtoms()) { - if (atom.getBonds()!=null && atom.getBonds().size() > 0) { + if (atom.getBonds()!=null && !atom.getBonds().isEmpty()) { ((ArrayList) atom.getBonds()).trimToSize(); } } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/LocalPDBDirectory.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/LocalPDBDirectory.java index 4ec4577f59..2e6b09fe9a 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/LocalPDBDirectory.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/LocalPDBDirectory.java @@ -550,7 +550,7 @@ private File downloadStructure(PdbId pdbId, String pathOnServer, boolean obsolet ftp = DEFAULT_BCIF_FILE_SERVER + filename; } else { ftp = String.format("%s%s/%s/%s", - serverName, pathOnServer, id.substring(id.length()-3, id.length()-1), getFilename(id)); + serverName, pathOnServer, getMiddleHash(id), getFilename(id)); } URL url = new URL(ftp); @@ -576,21 +576,45 @@ private File downloadStructure(PdbId pdbId, String pathOnServer, boolean obsolet logger.info("Fetching {}", ftp); logger.info("Writing to {}", realFile); - FileDownloadUtils.createValidationFiles(url, realFile, null, FileDownloadUtils.Hash.UNKNOWN); - FileDownloadUtils.downloadFile(url, realFile); + // A single connection, so the recorded size and checksum describe exactly the + // bytes that were written. The wwPDB servers return the content MD5 as the + // ETag, so this also gives the cached file a real integrity check. + FileDownloadUtils.downloadFileWithValidation(url, realFile, null, FileDownloadUtils.Hash.UNKNOWN, + FileDownloadUtils.ETagPolicy.USE_IF_HEX_DIGEST); if(! FileDownloadUtils.validateFile(realFile)) throw new IOException("Downloaded file invalid: "+realFile); return realFile; } + /** + * Returns the two-character directory name under which an entry is filed in the + * PDB's divided layout, e.g. cb for 1cbs. + *

+ * The characters are taken relative to the end of the identifier rather + * than the start, so that both spellings of the same entry land in the same + * bucket: 1cbs and its extended form pdb_00001cbs both + * give cb. Taking them from the start would file the extended form + * under db instead. The extended PDB identifier format is expected + * to keep using this same hashing scheme. + * + * @param pdbId a PDB identifier, in either the short or the extended form + * @return the lowercase two-character directory name + * @since 7.3.0 + */ + public static String getMiddleHash(String pdbId) { + int offset = pdbId.length() - 3; + return pdbId.substring(offset, offset + 2).toLowerCase(); + } + /** * Get the last modified time of the file in given url by retrieveing the "Last-Modified" header. * Note that this only works for http URLs * @param url * @return the last modified date or null if it couldn't be retrieved (in that case a warning will be logged) + * @since 7.3.0 made public so that other caching code can reuse it */ - private Date getLastModifiedTime(URL url) { + public static Date getLastModifiedTime(URL url) { // see http://stackoverflow.com/questions/2416872/how-do-you-obtain-modified-date-from-a-remote-file-java Date date = null; @@ -629,14 +653,12 @@ private Date getLastModifiedTime(URL url) { protected File getDir(String pdbId, boolean obsolete) { File dir = null; - int offset = pdbId.length() - 3; + String middle = getMiddleHash(pdbId); if (obsolete) { // obsolete is always split - String middle = pdbId.substring(offset, offset + 2).toLowerCase(); dir = new File(obsoleteDirPath, middle); } else { - String middle = pdbId.substring(offset, offset + 2).toLowerCase(); dir = new File(splitDirPath, middle); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/PDBFileParser.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/PDBFileParser.java index 176459bbf2..b1d327599e 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/PDBFileParser.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/PDBFileParser.java @@ -1406,7 +1406,7 @@ public void handleResolutionLine(String line, Pattern pR) { try { float res = Float.parseFloat(resString); final float resInHeader = pdbHeader.getResolution(); - if (resInHeader!=PDBHeader.DEFAULT_RESOLUTION && resInHeader != res) { + if (resInHeader!=PDBHeader.DEFAULT_RESOLUTION && Math.abs(resInHeader - res) > 0.001) { logger.warn("More than 1 resolution value present, will use last one {} and discard previous {} " ,resString, String.format("%4.2f",resInHeader)); } @@ -1943,7 +1943,7 @@ private Group getCorrectAltLocGroup( Character altLoc, // see if we know this altLoc already; List atoms = currentGroup.getAtoms(); - if ( atoms.size() > 0) { + if (!atoms.isEmpty()) { Atom a1 = atoms.get(0); // we are just adding atoms to the current group // probably there is a second group following later... @@ -1956,7 +1956,7 @@ private Group getCorrectAltLocGroup( Character altLoc, List altLocs = currentGroup.getAltLocs(); for ( Group altLocG : altLocs ){ atoms = altLocG.getAtoms(); - if ( atoms.size() > 0) { + if (!atoms.isEmpty()) { for ( Atom a1 : atoms) { if (a1.getAltLoc().equals( altLoc)) { @@ -1970,7 +1970,7 @@ private Group getCorrectAltLocGroup( Character altLoc, // build it up. if ( groupCode3.equals(currentGroup.getPDBName())) { - if ( currentGroup.getAtoms().size() == 0) { + if ( currentGroup.getAtoms().isEmpty()) { //System.out.println("current group is empty " + current_group + " " + altLoc); return currentGroup; } @@ -2762,7 +2762,7 @@ private void makeCompounds(List compoundList, } // System.out.println("[makeCompounds] adding sources to compounds from sourceLines"); // since we're starting again from the first compound, reset it here - if ( entities.size() == 0){ + if ( entities.isEmpty()){ current_compound = new EntityInfo(); } else { current_compound = entities.get(0); @@ -2921,7 +2921,7 @@ private void triggerEndFileChecks(){ pdbHeader.setBioAssemblies(bioAssemblyParser.getTransformationMap()); } - if (ncsOperators !=null && ncsOperators.size()>0) { + if (ncsOperators !=null && !ncsOperators.isEmpty()) { crystallographicInfo.setNcsOperators( ncsOperators.toArray(new Matrix4d[ncsOperators.size()])); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/SeqRes2AtomAligner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/SeqRes2AtomAligner.java index 7ee21de4b4..0652ad1809 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/SeqRes2AtomAligner.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/SeqRes2AtomAligner.java @@ -198,7 +198,7 @@ public void mapSeqresRecords(Chain atomRes, Chain seqRes) { } } - if ( atomRes.getAtomGroups(GroupType.AMINOACID).size() < 1) { + if (atomRes.getAtomGroups(GroupType.AMINOACID).isEmpty()) { logger.debug("ATOM chain {} does not contain amino acids, ignoring...", atomRes.getId()); return; } @@ -215,7 +215,7 @@ public void mapSeqresRecords(Chain atomRes, Chain seqRes) { private void alignNucleotideChains(Chain seqRes, Chain atomRes) { - if ( atomRes.getAtomGroups(GroupType.NUCLEOTIDE).size() < 1) { + if (atomRes.getAtomGroups(GroupType.NUCLEOTIDE).isEmpty()) { logger.debug("ATOM chain {} does not contain nucleotides, ignoring...", atomRes.getId()); return; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/AbstractCifFileSupplier.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/AbstractCifFileSupplier.java index 826d9588ef..e43565c827 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/AbstractCifFileSupplier.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/AbstractCifFileSupplier.java @@ -40,8 +40,27 @@ protected CifFile getInternal(Structure structure, List wrappedAtom // entity information List entityInfos = structure.getEntityInfos(); + PdbId pdbId = structure.getPdbId(); + MmCifBlockBuilder blockBuilder = CifBuilder.enterFile(StandardSchemata.MMCIF) - .enterBlock(structure.getPdbId() == null? "" : structure.getPdbId().getId()); + .enterBlock(pdbId == null? "" : pdbId.getId()); + + if (pdbId != null) { + // The block header alone does not carry the identifier for consumers: readers pick it up from + // _entry.id (e.g. Jmol) or from _struct.entry_id (BioJava's own CifStructureConsumerImpl). + // Both are written so that the identifier survives a write-then-read round trip either way. + blockBuilder.enterEntry() + .enterId() + .add(pdbId.getId()) + .leaveColumn() + .leaveCategory(); + + blockBuilder.enterStruct() + .enterEntryId() + .add(pdbId.getId()) + .leaveColumn() + .leaveCategory(); + } blockBuilder.enterStructKeywords().enterText() .add(String.join(", ", structure.getPDBHeader().getKeywords())) @@ -308,7 +327,13 @@ public void accept(WrappedAtom wrappedAtom) { } } labelEntityId.add(entityId); - labelSeqId.add(seqId); + // see https://github.com/biojava/biojava/issues/1116 + // note the first condition is to safeguard and to have a default that writes labelSeqId if there's no knowledge about what's the entity type + if (chain.getEntityInfo()==null || chain.getEntityInfo().getType() == EntityType.POLYMER) { + labelSeqId.add(seqId); + } else { + labelSeqId.markNextNotPresent(); + } String insCode = ""; if (group.getResidueNumber().getInsCode() != null) { insCode = Character.toString(group.getResidueNumber().getInsCode()); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/CifStructureConsumerImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/CifStructureConsumerImpl.java index 67514edd84..f44eb44ab1 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/CifStructureConsumerImpl.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/cif/CifStructureConsumerImpl.java @@ -372,7 +372,7 @@ public void consumeAtomSite(AtomSite atomSite) { private Group getAltLocGroup(String recordName, Character altLoc, Character oneLetterCode, String threeLetterCode, long seqId) { List atoms = currentGroup.getAtoms(); - if (atoms.size() > 0) { + if (!atoms.isEmpty()) { if (atoms.get(0).getAltLoc().equals(altLoc)) { return currentGroup; } @@ -381,7 +381,7 @@ private Group getAltLocGroup(String recordName, Character altLoc, Character oneL List altLocs = currentGroup.getAltLocs(); for (Group altLocGroup : altLocs) { atoms = altLocGroup.getAtoms(); - if (atoms.size() > 0) { + if (!atoms.isEmpty()) { for (Atom a1 : atoms) { if (a1.getAltLoc().equals(altLoc)) { return altLocGroup; @@ -630,7 +630,8 @@ public void consumeDatabasePDBRev(DatabasePDBRev databasePDBrev) { modDate = relDate; } else { String dbrev = databasePDBrev.getDate().get(rowIndex); - modDate = convert(LocalDate.parse(dbrev, DATE_FORMAT)); + if (dbrev != null && !dbrev.isBlank()) + modDate = convert(LocalDate.parse(dbrev, DATE_FORMAT)); } pdbHeader.setModDate(modDate); } @@ -855,7 +856,8 @@ public void consumeRefine(Refine refine) { // we take the last one found so that behaviour is like in PDB file parsing double lsDResHigh = refine.getLsDResHigh().get(rowIndex); // TODO this could use a check to keep reasonable values - 1.5 may be overwritten by 0.0 - if (pdbHeader.getResolution() != PDBHeader.DEFAULT_RESOLUTION) { + if (pdbHeader.getResolution() != PDBHeader.DEFAULT_RESOLUTION && + Math.abs(pdbHeader.getResolution() - lsDResHigh) > 0.001) { logger.warn("More than 1 resolution value present, will use last one {} and discard previous {}", lsDResHigh, String.format("%4.2f",pdbHeader.getResolution())); } @@ -1478,7 +1480,7 @@ private void setStructNcsOps() { } } - if (ncsOperators.size() > 0) { + if (!ncsOperators.isEmpty()) { structure.getCrystallographicInfo() .setNcsOperators(ncsOperators.toArray(new Matrix4d[0])); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureReader.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureReader.java index c2830c1685..865c9e0da4 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureReader.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmtf/MmtfStructureReader.java @@ -372,7 +372,7 @@ public void setInterGroupBond(int indOne, int indTwo, int bondOrder) { private Group getCorrectAltLocGroup(Character altLoc) { // see if we know this altLoc already; List atoms = group.getAtoms(); - if (atoms.size() > 0) { + if (!atoms.isEmpty()) { Atom a1 = atoms.get(0); // we are just adding atoms to the current group // probably there is a second group following later... @@ -396,7 +396,7 @@ private Group getCorrectAltLocGroup(Character altLoc) { } // no matching altLoc group found. // build it up. - if (group.getAtoms().size() == 0) { + if (group.getAtoms().isEmpty()) { return group; } Group altLocG = (Group) group.clone(); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BioAssemblyTools.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BioAssemblyTools.java index 7c359121de..8f76b2ae61 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BioAssemblyTools.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BioAssemblyTools.java @@ -55,7 +55,7 @@ public static boolean isUnaryExpression(String expression) { if (first < 0 || last < 0) { return true; } - return ! (first == 0 && last > first); + return first != 0 || last <= first; } public static List parseUnaryOperatorExpression(String operatorExpression) { @@ -279,7 +279,7 @@ public static double[] getBiologicalMoleculeCentroid( final Structure asymUnit, return centroid; } - if ( transformations.size() == 0) { + if ( transformations.isEmpty()) { return Calc.getCentroid(atoms).getCoords(); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyBuilder.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyBuilder.java index c6ec6bc8ff..9edb8f404c 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyBuilder.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyBuilder.java @@ -205,7 +205,7 @@ private void addChainMultiModel(Structure s, Chain newChain, String transformId) // multi-model bioassembly - if ( modelIndex.size() == 0) + if (modelIndex.isEmpty()) modelIndex.add("PLACEHOLDER FOR ASYM UNIT"); int modelCount = modelIndex.indexOf(transformId); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyTransformation.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyTransformation.java index 36bccd7b39..ff1efd6e32 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyTransformation.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/quaternary/BiologicalAssemblyTransformation.java @@ -226,7 +226,7 @@ public static BiologicalAssemblyTransformation fromXML(String xml) List transformations = fromMultiXML(xml); - if ( transformations.size() > 0) + if (!transformations.isEmpty()) return transformations.get(0); else diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/scop/ScopInstallation.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/scop/ScopInstallation.java index d092d5485e..4ac55cfa08 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/scop/ScopInstallation.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/scop/ScopInstallation.java @@ -655,7 +655,7 @@ private List extractRanges(String range) { } protected void downloadClaFile() throws IOException{ - if(mirrors.size()<1) { + if(mirrors.isEmpty()) { initScopURLs(); } IOException exception = null; @@ -676,7 +676,7 @@ protected void downloadClaFile() throws IOException{ } protected void downloadDesFile() throws IOException{ - if(mirrors.size()<1) { + if(mirrors.isEmpty()) { initScopURLs(); } IOException exception = null; @@ -697,7 +697,7 @@ protected void downloadDesFile() throws IOException{ } protected void downloadHieFile() throws IOException{ - if(mirrors.size()<1) { + if(mirrors.isEmpty()) { initScopURLs(); } IOException exception = null; @@ -719,7 +719,7 @@ protected void downloadHieFile() throws IOException{ } protected void downloadComFile() throws IOException{ - if(mirrors.size()<1) { + if(mirrors.isEmpty()) { initScopURLs(); } IOException exception = null; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/secstruc/SecStrucTools.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/secstruc/SecStrucTools.java index 7732c04b80..42191d682d 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/secstruc/SecStrucTools.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/secstruc/SecStrucTools.java @@ -57,7 +57,7 @@ public static List getSecStrucInfo(Structure s) { Group g = iter.next(); if (g.hasAminoAtoms()) { Object p = g.getProperty(Group.SEC_STRUC); - if (!(p == null)) { + if (p != null) { SecStrucInfo ss = (SecStrucInfo) p; listSSI.add(ss); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelicalRepeatUnit.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelicalRepeatUnit.java index ff9c77cf13..cd16aa5f87 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelicalRepeatUnit.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelicalRepeatUnit.java @@ -64,7 +64,7 @@ public Map getInteractingRepeatUnits() { private void run() { this.repeatUnitCenters = calcRepeatUnitCenters(); - if (this.repeatUnitCenters.size() == 0) { + if (this.repeatUnitCenters.isEmpty()) { return; } this.repeatUnits = calcRepeatUnits(); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java index e1f4792410..b3ac53f385 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java @@ -65,7 +65,7 @@ public void completeGroup() { Set> known = new HashSet<>(permutations); //breadth-first search through the map of all members List> currentLevel = new ArrayList<>(permutations); - while( currentLevel.size() > 0) { + while(!currentLevel.isEmpty()) { List> nextLevel = new ArrayList<>(); for( List p : currentLevel) { for(List gen : gens) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetrySubunits.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetrySubunits.java index b0bef7f1e6..7f434cabaf 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetrySubunits.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetrySubunits.java @@ -211,7 +211,7 @@ public MomentsOfInertia getMomentsOfInertia() { } private void run() { - if (centers.size() > 0) { + if (!centers.isEmpty()) { return; } calcOriginalCenters(); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java index 70b69afe14..002d046e52 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java @@ -83,7 +83,7 @@ public void removeRotation(int index) { public void complete() { if (modified) { - if (rotations.size() > 0) { + if (!rotations.isEmpty()) { findHighestOrderAxis(); setEAxis(); calcAxesDirections(); @@ -98,7 +98,7 @@ public void complete() { public String getPointGroup() { if (modified) { - if (rotations.size() == 0) { + if (rotations.isEmpty()) { return "C1"; } complete(); @@ -344,7 +344,7 @@ private void calcPointGroup() { // when a structure is symmetric, some subunits are below the rmsd threshold, // and some are just above the rmsd threshold int n = 0; - if (rotations.size() > 0) { + if (!rotations.isEmpty()) { n = rotations.get(0).getPermutation().size(); rotations.clear(); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java index 37a44be7ac..b1566a6d2f 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java @@ -305,7 +305,7 @@ private boolean isSpherical() { * @return null if invalid, or a rotation if valid */ private Rotation isValidPermutation(List permutation) { - if (permutation.size() == 0) { + if (permutation.isEmpty()) { return null; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/SystematicSolver.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/SystematicSolver.java index d13fa4db16..a449771b58 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/SystematicSolver.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/SystematicSolver.java @@ -145,7 +145,7 @@ private void completeRotationGroup() { } private boolean isValidPermutation(List permutation) { - if (permutation.size() == 0) { + if (permutation.isEmpty()) { return false; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/DistanceBox.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/DistanceBox.java index 2d9b1d6dca..25d37693fb 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/DistanceBox.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/DistanceBox.java @@ -166,7 +166,7 @@ private List getBoxTwo(long location) { } // ensure that boxTwo has no empty element by copying from tempBox of defined size List boxTwo = null; - if (tempBox.size() == 0) { + if (tempBox.isEmpty()) { boxTwo = Collections.emptyList(); } else if (tempBox.size() == 1) { boxTwo = Collections.singletonList(tempBox.get(0)); diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SequenceFunctionRefiner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SequenceFunctionRefiner.java index a6ad226698..0d52e92fef 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SequenceFunctionRefiner.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SequenceFunctionRefiner.java @@ -79,7 +79,7 @@ public static AFPChain refineSymmetry(AFPChain afpChain, Atom[] ca1, Atom[] ca2, // Refine the alignment Map Map refined = refineSymmetry(alignment, k); - if (refined.size() < 1) + if (refined.isEmpty()) throw new RefinerFailedException("Refiner returned empty alignment"); //Substitute and partition the alignment diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SymmOptimizer.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SymmOptimizer.java index d03e90080f..627908ac8b 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SymmOptimizer.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SymmOptimizer.java @@ -392,10 +392,7 @@ private boolean checkGaps() { length--; } - if (shrinkColumns.size() != 0) - return true; - else - return false; + return !shrinkColumns.isEmpty(); } /** diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/utils/BlastClustReader.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/utils/BlastClustReader.java index b2b3298157..5d1faa8754 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/utils/BlastClustReader.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/utils/BlastClustReader.java @@ -140,7 +140,7 @@ public List> getChainIdsInEntry(String pdbId) { private void loadClusters(int sequenceIdentity) { // load clusters only once - if (clusters.size() > 0) { + if (!clusters.isEmpty()) { return; } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/xtal/SpaceGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/xtal/SpaceGroup.java index cff84c70f8..852d213bb9 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/xtal/SpaceGroup.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/xtal/SpaceGroup.java @@ -645,10 +645,10 @@ public List getTransfAlgebraic() { public void setTransfAlgebraic(List transfAlgebraic) { //System.out.println("setting transfAlgebraic " + transfAlgebraic); - if ( transformations == null || transformations.size() == 0) + if ( transformations == null || transformations.isEmpty()) transformations = new ArrayList(transfAlgebraic.size()); - if ( this.transfAlgebraic == null || this.transfAlgebraic.size() == 0) + if ( this.transfAlgebraic == null || this.transfAlgebraic.isEmpty()) this.transfAlgebraic = new ArrayList<>(transfAlgebraic.size()); for ( String transf : transfAlgebraic){ diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/align/util/AtomCacheTest.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/align/util/AtomCacheTest.java index 073a679dbb..f2f06ed2f5 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/align/util/AtomCacheTest.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/align/util/AtomCacheTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import java.io.File; @@ -408,7 +409,7 @@ public void testEmptyChemComp() throws IOException, StructureException { // should be unknown ChemComp chem = g.getChemComp(); assertNotNull(chem); - assertTrue(chem.getAtoms().size() > 0); + assertFalse(chem.getAtoms().isEmpty()); assertEquals("NON-POLYMER", chem.getType()); } finally { FileDownloadUtils.deleteDirectory(tmpCache); @@ -471,7 +472,7 @@ public void testEmptyGZChemComp() throws IOException, StructureException { // should be unknown ChemComp chem = g.getChemComp(); assertNotNull(chem); - assertTrue(chem.getAtoms().size() > 0); + assertFalse(chem.getAtoms().isEmpty()); assertEquals("NON-POLYMER", chem.getType()); } finally { FileDownloadUtils.deleteDirectory(tmpCache); diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/asa/TestAsaCalc.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/asa/TestAsaCalc.java index 7be56be156..c6a268f1f5 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/asa/TestAsaCalc.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/asa/TestAsaCalc.java @@ -126,19 +126,19 @@ public void testNeighborIndicesFinding() throws StructureException, IOException AsaCalculator.DEFAULT_PROBE_SIZE, 1000, 1, false); - AsaCalculator.IndexAndDistance[][] allNbsSh = asaCalc.findNeighborIndicesSpatialHashing(); + AsaCalculator.Neighbors[] allNbsSh = asaCalc.findNeighborIndicesSpatialHashing(); - AsaCalculator.IndexAndDistance[][] allNbs = asaCalc.findNeighborIndices(); + AsaCalculator.Neighbors[] allNbs = asaCalc.findNeighborIndices(); for (int indexToTest =0; indexToTest < asaCalc.getAtomCoords().length; indexToTest++) { //int indexToTest = 198; - AsaCalculator.IndexAndDistance[] nbsSh = allNbsSh[indexToTest]; - AsaCalculator.IndexAndDistance[] nbs = allNbs[indexToTest]; + int[] nbsSh = allNbsSh[indexToTest].indices; + int[] nbs = allNbs[indexToTest].indices; List listOfMatchingIndices = new ArrayList<>(); for (int i = 0; i < nbsSh.length; i++) { for (int j = 0; j < nbs.length; j++) { - if (nbs[j].index == nbsSh[i].index) { + if (nbs[j] == nbsSh[i]) { listOfMatchingIndices.add(j); break; } @@ -229,21 +229,21 @@ public void testNoNeighborsIssue() { AsaCalculator.DEFAULT_PROBE_SIZE, 1000, 1); - AsaCalculator.IndexAndDistance[][] allNbsSh = asaCalc.findNeighborIndicesSpatialHashing(); + AsaCalculator.Neighbors[] allNbsSh = asaCalc.findNeighborIndicesSpatialHashing(); - AsaCalculator.IndexAndDistance[][] allNbs = asaCalc.findNeighborIndices(); + AsaCalculator.Neighbors[] allNbs = asaCalc.findNeighborIndices(); assertEquals(3, allNbs.length); assertEquals(3, allNbsSh.length); for (int indexToTest =0; indexToTest < asaCalc.getAtomCoords().length; indexToTest++) { - AsaCalculator.IndexAndDistance[] nbsSh = allNbsSh[indexToTest]; - AsaCalculator.IndexAndDistance[] nbs = allNbs[indexToTest]; + int[] nbsSh = allNbsSh[indexToTest].indices; + int[] nbs = allNbs[indexToTest].indices; List listOfMatchingIndices = new ArrayList<>(); for (int i = 0; i < nbsSh.length; i++) { for (int j = 0; j < nbs.length; j++) { - if (nbs[j].index == nbsSh[i].index) { + if (nbs[j] == nbsSh[i]) { listOfMatchingIndices.add(j); break; } @@ -256,7 +256,7 @@ public void testNoNeighborsIssue() { } // first atom should have no neighbors - assertEquals(0, allNbsSh[0].length); + assertEquals(0, allNbsSh[0].indices.length); } private Atom getAtom(double x, double y, double z) { diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/cath/CathInstallationTest.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/cath/CathInstallationTest.java new file mode 100644 index 0000000000..69944cb51c --- /dev/null +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/cath/CathInstallationTest.java @@ -0,0 +1,87 @@ +/* + * BioJava development code + * + * This code may be freely distributed and modified under the + * terms of the GNU Lesser General Public Licence. This should + * be distributed with the code. If you do not have a copy, + * see: + * + * http://www.gnu.org/copyleft/lesser.html + * + * Copyright for this code is held jointly by the individual + * authors. These should be listed in @author doc comments. + * + * For more information on the BioJava project and its aims, + * or to join the biojava-l mailing list, visit the home page + * at: + * + * http://www.biojava.org/ + */ +package org.biojava.nbio.structure.cath; + +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class CathInstallationTest { + + @Test + public void testParseCathDomainListSuccess() throws IOException { + String data = "# CATH domain list\n" + + "\n" + + "1oaiA00 1 10 490 10 1 1 1 1 1 124 1.80\n" + + "1oaiA01 1 10 490 10 1 1 1 1 2 150 1.80\n"; + + CathInstallation installation = new CathInstallation(""); + BufferedReader reader = new BufferedReader(new StringReader(data)); + installation.parseCathDomainList(reader); + installation.setInstalledDomainList(new AtomicBoolean(true)); //1 + installation.setInstalledDomall(new AtomicBoolean(true)); //2 + + CathDomain domain = installation.getDomainByCathId("1oaiA00"); + assertNotNull(domain); + assertEquals("1oaiA00", domain.getDomainName()); + assertEquals(1, domain.getClassId()); + assertEquals(10, domain.getArchitectureId()); + assertEquals(490, domain.getTopologyId()); + assertEquals(10, domain.getHomologyId()); + assertEquals(124, domain.getLength()); + assertEquals(1.80, domain.getResolution(), 0.001); + } + + @Test + public void testParseCathDomainListEmptyThrowsException() { + CathInstallation installation = new CathInstallation(""); + BufferedReader reader = new BufferedReader(new StringReader("")); + assertThrows(IOException.class, () -> installation.parseCathDomainList(reader)); + } + + @Test + public void testParseCathDomainListOnlyCommentsAndWhitespaceThrowsException() { + String data = "# comment 1\n" + + "# comment 2\n" + + " \n" + + "\t\n"; + CathInstallation installation = new CathInstallation(""); + BufferedReader reader = new BufferedReader(new StringReader(data)); + assertThrows(IOException.class, () -> installation.parseCathDomainList(reader)); + } + + @Test + public void testParseCathDomainListNoParsableLinesThrowsException() { + String data = "# comment\n" + + "invalid line with too few tokens\n" + + "another bad line\n"; + CathInstallation installation = new CathInstallation(""); + BufferedReader reader = new BufferedReader(new StringReader(data)); + IOException exception = assertThrows(IOException.class, () -> installation.parseCathDomainList(reader)); + assertNotNull(exception.getMessage()); + } +} diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/chem/TestChemCompRedirectNotCached.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/chem/TestChemCompRedirectNotCached.java new file mode 100644 index 0000000000..b948e9fbfb --- /dev/null +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/chem/TestChemCompRedirectNotCached.java @@ -0,0 +1,154 @@ +/** + * BioJava development code + * + * This code may be freely distributed and modified under the terms of the GNU + * Lesser General Public Licence. This should be distributed with the code. If + * you do not have a copy, see: + * + * http://www.gnu.org/copyleft/lesser.html + * + * Copyright for this code is held jointly by the individual authors. These + * should be listed in @author doc comments. + * + * For more information on the BioJava project and its aims, or to join the + * biojava-l mailing list, visit the home page at: + * + * http://www.biojava.org/ + */ +package org.biojava.nbio.structure.chem; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; + +import com.sun.net.httpserver.HttpServer; + +import org.biojava.nbio.core.util.FlatFileCache; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * A server that redirects, or errors, must not have its response body cached as + * though it were a chemical component definition. + *

+ * This is the failure that took the CATH downloader out when + * download.cathdb.info moved to https, and the chem comp download had + * the same shape. A 4xx already failed safely, because + * getInputStream() throws for those; a redirect did not, because when + * the JDK declines to follow a 3xx it hands back the redirect's body instead, and + * that body is short but not empty. + *

+ * The test serves the responses from a local {@link HttpServer} rather than a real + * service. Pointing it at a third-party server that happens to redirect today would + * make the test fail on the day they stop, which is precisely the coupling that made + * the build unreliable in the first place. + * + * @author Amr ALHOSSARY + * @since 7.3.0 + */ +public class TestChemCompRedirectNotCached { + + private HttpServer server; + private String originalServerUrl; + + @Before + public void setUp() { + originalServerUrl = DownloadChemCompProvider.serverBaseUrl; + } + + @After + public void tearDown() { + if (server != null) { + server.stop(0); + } + // Static state: leaving either of these set would corrupt unrelated tests. + DownloadChemCompProvider.serverBaseUrl = originalServerUrl; + FlatFileCache.clear(); + } + + /** + * Starts a local server that answers every request with the given status and body. + * + * @return the base URL to point the provider at + */ + private String startServer(int status, String location, String body) throws IOException { + server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0); + server.createContext("/", exchange -> { + byte[] bytes = body.getBytes(StandardCharsets.UTF_8); + if (location != null) { + exchange.getResponseHeaders().add("Location", location); + } + exchange.sendResponseHeaders(status, bytes.length); + try (OutputStream out = exchange.getResponseBody()) { + out.write(bytes); + } + }); + server.start(); + return "http://127.0.0.1:" + server.getAddress().getPort() + "/"; + } + + private File cacheFileFor(String id) { + File file = new File(DownloadChemCompProvider.getLocalFileName(id)); + file.delete(); + FlatFileCache.clear(); + return file; + } + + /** + * The case that broke CATH: a redirect the JDK will not follow because it + * changes protocol. Its body must not end up on disk under the component's name. + */ + @Test + public void redirectBodyIsNotCached() throws IOException { + File cached = cacheFileFor("ATP"); + DownloadChemCompProvider.serverBaseUrl = + startServer(301, "https://example.invalid/ATP.cif", "Moved Permanently"); + + ChemComp cc = new DownloadChemCompProvider().getChemComp("ATP"); + + assertFalse("the body of a redirect must never be cached as a definition", cached.exists()); + assertNull("nothing parseable was returned, so the component must be empty", cc.getName()); + } + + /** + * A 200 is still cached, so the guard has not simply disabled downloading. + *

+ * What is under test is the download path, not the CIF parser: the response is + * written to the cache before anything tries to parse it, so a parse failure on + * this deliberately minimal body says nothing about whether the guard behaved. + */ + @Test + public void aValidResponseIsStillCached() throws IOException { + File cached = cacheFileFor("ATP"); + DownloadChemCompProvider.serverBaseUrl = startServer(200, null, + "data_ATP\n#\n_chem_comp.id ATP\n_chem_comp.name \"ADENOSINE-5'-TRIPHOSPHATE\"\n#\n"); + + try { + new DownloadChemCompProvider().getChemComp("ATP"); + } catch (RuntimeException parseFailure) { + // see the note above + } + + assertTrue("a 200 response should still be cached", cached.exists()); + cached.delete(); + } + + /** A server error must not be cached either. */ + @Test + public void serverErrorBodyIsNotCached() throws IOException { + File cached = cacheFileFor("ATP"); + DownloadChemCompProvider.serverBaseUrl = + startServer(503, null, "Service Unavailable"); + + new DownloadChemCompProvider().getChemComp("ATP"); + + assertFalse("the body of a 5xx must never be cached as a definition", cached.exists()); + } +} diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/ecod/EcodParserTest.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/ecod/EcodParserTest.java new file mode 100644 index 0000000000..295806e915 --- /dev/null +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/ecod/EcodParserTest.java @@ -0,0 +1,323 @@ +/* + * BioJava development code + * + * This code may be freely distributed and modified under the + * terms of the GNU Lesser General Public Licence. This should + * be distributed with the code. If you do not have a copy, + * see: + * + * http://www.gnu.org/copyleft/lesser.html + * + * Copyright for this code is held jointly by the individual + * authors. These should be listed in @author doc comments. + * + * For more information on the BioJava project and its aims, + * or to join the biojava-l mailing list, visit the home page + * at: + * + * http://www.biojava.org/ + */ +package org.biojava.nbio.structure.ecod; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +import org.biojava.nbio.structure.ecod.EcodInstallation.EcodParser; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +/** + * Checks that {@link EcodParser} reads every layout ECOD has distributed. + *

+ * The columns have been renamed, reordered and added to several times, and the version + * comment itself changed form at v294.1. Because the full distribution is 657 MB, none of + * that was covered by a test that could run in reasonable time, and a format change went + * unnoticed for months. These fixtures are taken verbatim from the real files, so the + * contract is pinned in milliseconds rather than by a download. + * + * @author Amr ALHOSSARY + * @since 7.3.0 + */ +class EcodParserTest { + + /** develop204, list format 1.5: 15 columns, commented header, quoted names. */ + private static final String DEVELOP204 = String.join("\n", + "#/data/ecod/database_versions/v204/ecod.develop204.domains.txt", + "#ECOD version develop204", + "#Domain list version 1.5", + "#Grishin lab (http://prodata.swmed.edu/ecod)", + "#uid\tecod_domain_id\tmanual_rep\tf_id\tpdb\tchain\tpdb_range\tseqid_range" + + "\tarch_name\tx_name\th_name\tt_name\tf_name\tasm_status\tligand", + "002137905\te6b4nA1\tAUTO_NONREP\t1.1.1\t6b4n\tA\tA:1-99\tA:1-99\tbeta barrels" + + "\t\"cradle loop barrel\"\t\"RIFT-related\"\t\"acid protease\"" + + "\tF_UNCLASSIFIED\tNOT_DOMAIN_ASSEMBLY\tCL,G53,NA"); + + /** develop291, list format 1.6: 16 columns, f_id renamed t_id, unp_acc inserted at 9. */ + private static final String DEVELOP291 = String.join("\n", + "#/data/ecod/database_versions/v291/ecod.develop291.domains.txt", + "#ECOD version develop291", + "#Domain list version 1.6", + "#Grishin lab (http://prodata.swmed.edu/ecod)", + "#uid\tecod_domain_id\tmanual_rep\tt_id\tpdb\tchain\tpdb_range\tseqid_range\tunp_acc" + + "\tarch_name\tx_name\th_name\tt_name\tf_name\tasm_status\tligand", + "000000267\te1udzA1\tMANUAL_REP\t1.1.1\t1udz\tA\tA:203-381\tA:4-182\tP12345" + + "\tbeta barrels\t\"cradle loop barrel\"\t\"RIFT-related\"\t\"acid protease\"" + + "\tF_UNCLASSIFIED\tNOT_DOMAIN_ASSEMBLY\tNO_LIGANDS_4A"); + + private static final String V295_COLUMNS = + "uid\tecod_domain_id\tmanual_rep\tf_id\tpdb\tchain\tpdb_range\tseqid_range" + + "\tarchitecture_name\tx_name\th_name\tt_name\tf_name\tassembly_id\tdomain_id_short" + + "\trange_count\tarch_manual\tx_manual\th_manual\tt_manual\tf_manual" + + "\tvalid_structure\tligand_binding\tligand_comp_ids\tligand_pdbnum"; + + /** v295: 25 columns, uncommented header, True/False, empty assembly_id, moved ligands. */ + private static final String V295 = String.join("\n", + "# ECOD Domain List", + "# Version: v295", + "# Generated: 2026-06-24 22:47:42", + "# Ligand cutoff: 4.0 A (NO_LIGANDS_4A = no contact within cutoff)", + "#", + V295_COLUMNS, + "0\te2nmzA1\tTrue\t1.1.1.3\t2nmz\tA\tA:1-99\tA:1-99\tbeta barrels\tcradle loop barrel" + + "\tRIFT-related\tacid protease\tRVP\t\t\t1\tFalse\tFalse\tFalse\tFalse\tTrue" + + "\tTrue\tTrue\tROC,SO4\tA:601,A:602,B:401", + // the last column is empty on four rows in five, so split() must keep it + "3\te2rspA1\tTrue\t1.1.1.3\t2rsp\tA\tA:1-124\tA:1-124\tbeta barrels\tcradle loop barrel" + + "\tRIFT-related\tacid protease\tRVP\t\t\t1\tFalse\tFalse\tFalse\tFalse\tTrue" + + "\tTrue\tFalse\tNO_LIGANDS_4A\t", + // a domain classified from an AlphaFold model: no PDB entry, so no EcodDomain + "3163557\tP44140_F1_nD2\tFalse\t2004.1.1.123\t\t\t131-315\t131-315\talpha bundles" + + "\tsomething\tsomething else\ta third thing\t\t\t\t1\tFalse\tFalse\tFalse" + + "\tFalse\tTrue\tTrue\tFalse\tNO_LIGANDS_4A\t"); + + private static List parse(String contents) throws IOException { + return new EcodParser(new StringReader(contents)).getDomains(); + } + + private static String version(String contents) throws IOException { + return new EcodParser(new StringReader(contents)).getVersion(); + } + + @Nested + class Version { + @Test + void oldHeaderForm() throws IOException { + assertEquals("develop204", version(DEVELOP204)); + assertEquals("develop291", version(DEVELOP291)); + } + + @Test + void newHeaderForm() throws IOException { + assertEquals("v295", version(V295)); + assertEquals("v294.1", version("# ECOD Domain List\n# Version: v294.1\n")); + } + + @Test + void listFormatVersionIsNotTheEcodVersion() throws IOException { + // "#Domain list version 1.5" describes the columns, not the release + assertNull(version("#Grishin lab\n#Domain list version 1.5\n")); + } + + @Test + void absentVersionIsNull() throws IOException { + assertNull(version("#Grishin lab (http://prodata.swmed.edu/ecod)\n")); + } + } + + @Nested + class OldFormats { + @Test + void listFormat15() throws IOException { + List domains = parse(DEVELOP204); + assertEquals(1, domains.size()); + EcodDomain d = domains.get(0); + assertEquals(Long.valueOf(2137905), d.getUid()); + assertEquals("e6b4nA1", d.getDomainId()); + assertEquals(Boolean.FALSE, d.getManual()); + assertEquals(Integer.valueOf(1), d.getXGroup()); + assertEquals(Integer.valueOf(1), d.getHGroup()); + assertEquals(Integer.valueOf(1), d.getTGroup()); + assertNull(d.getFGroup()); + assertEquals("6B4N", d.getPdbId().getId()); + assertEquals("A", d.getChainId()); + assertEquals("A:1-99", d.getRange()); + assertEquals("A:1-99", d.getSeqIdRange()); + assertEquals("beta barrels", d.getArchitectureName()); + // quotes were stripped up to develop292 + assertEquals("cradle loop barrel", d.getXGroupName()); + assertEquals("RIFT-related", d.getHGroupName()); + assertEquals("acid protease", d.getTGroupName()); + assertEquals("F_UNCLASSIFIED", d.getFGroupName()); + assertEquals(Long.valueOf(2137905), d.getAssemblyId()); + assertEquals(new LinkedHashSet<>(Arrays.asList("CL", "G53", "NA")), d.getLigands()); + } + + /** + * develop291 inserted unp_acc before arch_name. Read positionally, every field from + * there on shifts by one and the domain is silently mangled or dropped. + */ + @Test + void listFormat16InsertsUnpAcc() throws IOException { + List domains = parse(DEVELOP291); + assertEquals(1, domains.size()); + EcodDomain d = domains.get(0); + assertEquals(Boolean.TRUE, d.getManual()); + assertEquals("1UDZ", d.getPdbId().getId()); + assertEquals("A:4-182", d.getSeqIdRange()); + assertEquals("beta barrels", d.getArchitectureName()); + assertEquals("acid protease", d.getTGroupName()); + assertEquals("F_UNCLASSIFIED", d.getFGroupName()); + assertEquals(Long.valueOf(267), d.getAssemblyId()); + assertEquals(Collections.emptySet(), d.getLigands()); + } + + /** + * Headers were only added in develop101. Older files are still read by position. + */ + @Test + void thirteenColumnsWithoutAHeader() throws IOException { + List domains = parse(String.join("\n", + "#ECOD version develop45", + "000000001\te1udzA1\t1.1.1\t1udz\tA\tA:203-381\tbeta barrels" + + "\t\"cradle loop barrel\"\t\"RIFT-related\"\t\"acid protease\"" + + "\tF_UNCLASSIFIED\tNOT_DOMAIN_ASSEMBLY\tNO_LIGANDS_4A")); + assertEquals(1, domains.size()); + EcodDomain d = domains.get(0); + assertNull(d.getManual(), "no manual_rep column before list format 1.1"); + assertNull(d.getSeqIdRange(), "no seqid_range column before list format 1.4"); + assertEquals("1UDZ", d.getPdbId().getId()); + assertEquals("acid protease", d.getTGroupName()); + } + } + + @Nested + class NewFormat { + @Test + void twentyFiveColumns() throws IOException { + List domains = parse(V295); + // two PDB domains; the AlphaFold-derived row cannot be an EcodDomain + assertEquals(2, domains.size()); + + EcodDomain d = domains.get(0); + assertEquals(Long.valueOf(0), d.getUid()); + assertEquals("e2nmzA1", d.getDomainId()); + assertEquals(Boolean.TRUE, d.getManual(), "manual_rep is now True/False"); + assertEquals(Integer.valueOf(1), d.getXGroup()); + assertEquals(Integer.valueOf(1), d.getHGroup()); + assertEquals(Integer.valueOf(1), d.getTGroup()); + assertEquals(Integer.valueOf(3), d.getFGroup(), "f_id now carries a fourth level"); + assertEquals("2NMZ", d.getPdbId().getId()); + assertEquals("A", d.getChainId()); + assertEquals("A:1-99", d.getRange()); + assertEquals("A:1-99", d.getSeqIdRange()); + assertEquals("beta barrels", d.getArchitectureName()); + assertEquals("cradle loop barrel", d.getXGroupName()); + assertEquals("RIFT-related", d.getHGroupName()); + assertEquals("acid protease", d.getTGroupName()); + assertEquals("RVP", d.getFGroupName()); + // assembly_id is empty on every row of v294.1 and later, which means the same + // as the NOT_DOMAIN_ASSEMBLY of earlier versions + assertEquals(Long.valueOf(0), d.getAssemblyId()); + assertEquals(new LinkedHashSet<>(Arrays.asList("ROC", "SO4")), d.getLigands(), + "the ligand list moved to ligand_comp_ids"); + } + + /** + * ligand_pdbnum, the last column, is empty on four rows in five. String.split + * discards trailing empty fields unless asked not to, which would make those rows + * look one column short. + */ + @Test + void rowEndingInAnEmptyColumn() throws IOException { + EcodDomain d = parse(V295).get(1); + assertEquals("e2rspA1", d.getDomainId()); + assertEquals("2RSP", d.getPdbId().getId()); + assertEquals(Collections.emptySet(), d.getLigands()); + } + + @Test + void twentyThreeColumnsOfV2941() throws IOException { + List domains = parse(String.join("\n", + "# ECOD Domain List", + "# Version: v294.1", + "#", + "uid\tecod_domain_id\tmanual_rep\tf_id\tpdb\tchain\tpdb_range\tseqid_range" + + "\tarchitecture_name\tx_name\th_name\tt_name\tf_name\tassembly_id" + + "\tdomain_id_short\trange_count\tarch_manual\tx_manual\th_manual" + + "\tt_manual\tf_manual\tvalid_structure\tligand_binding", + "1\te1hvcA1\tFalse\t1.1.1.3\t1hvc\tA\tA:1B-99A\tA:1-203\tbeta barrels" + + "\tcradle loop barrel\tRIFT-related\tacid protease\tRVP\t\t\t\tFalse" + + "\tFalse\tFalse\tFalse\tFalse\tTrue\tFalse")); + assertEquals(1, domains.size()); + EcodDomain d = domains.get(0); + assertEquals("1HVC", d.getPdbId().getId()); + assertEquals("A:1B-99A", d.getRange()); + assertEquals("RVP", d.getFGroupName()); + // there is no ligand column at all in v294.1 + assertEquals(Collections.emptySet(), d.getLigands()); + } + + @Test + void columnHeaderIsNotADomain() throws IOException { + // v294.1 stopped commenting the column names out, so they arrive looking like data + for (EcodDomain d : parse(V295)) { + assertFalse("uid".equals(d.getDomainId())); + } + } + + /** + * An empty f_name is not the same as F_UNCLASSIFIED: f_id still classifies the + * domain to four levels, so the empty value is left as it is rather than translated. + */ + @Test + void emptyFGroupNameIsLeftAlone() throws IOException { + List domains = parse(String.join("\n", + "# Version: v295", + V295_COLUMNS, + "7\te4fivA1\tTrue\t1.1.1.3\t4fiv\tA\tA:4-116\tA:1-113\tbeta barrels" + + "\tcradle loop barrel\tRIFT-related\tacid protease\t\t\t\t1\tFalse" + + "\tFalse\tFalse\tFalse\tTrue\tTrue\tTrue\tLP1\tA:201")); + assertEquals(1, domains.size()); + assertEquals("", domains.get(0).getFGroupName()); + } + } + + @Nested + class Robustness { + @Test + void unparseableLinesAreSkippedNotFatal() throws IOException { + List domains = parse(String.join("\n", + "# Version: v295", + V295_COLUMNS, + "not-a-number\tefoo\tTrue\t1.1.1.3\tfoo1\tA\tA:1-9\tA:1-9\ta\tb\tc\td\te" + + "\t\t\t1\tFalse\tFalse\tFalse\tFalse\tTrue\tTrue\tFalse\t\t", + "0\te2nmzA1\tTrue\t1.1.1.3\t2nmz\tA\tA:1-99\tA:1-99\tbeta barrels" + + "\tcradle loop barrel\tRIFT-related\tacid protease\tRVP\t\t\t1" + + "\tFalse\tFalse\tFalse\tFalse\tTrue\tTrue\tTrue\tROC,SO4\tA:601")); + assertEquals(1, domains.size(), "the good line is still read"); + assertEquals("e2nmzA1", domains.get(0).getDomainId()); + } + + @Test + void shortLineIsSkipped() throws IOException { + assertTrue(parse(String.join("\n", + "# Version: v295", + V295_COLUMNS, + "0\te2nmzA1\tTrue")).isEmpty()); + } + + @Test + void emptyFileYieldsNoDomains() throws IOException { + assertTrue(parse("").isEmpty()); + } + } +} diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestHeaderOnly.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestHeaderOnly.java index d3c9568240..f579752d4c 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestHeaderOnly.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestHeaderOnly.java @@ -205,8 +205,7 @@ public boolean doSeqResHaveAtoms(Structure s) { * @return true if has any Atom(s) */ public boolean hasAtoms(Group g) { - if (g.getAtoms().size() > 0) return true; - return false; + return !g.getAtoms().isEmpty(); } /** diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestMMcifOrganismParsing.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestMMcifOrganismParsing.java index 8d018f6c0a..2cbdbca679 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestMMcifOrganismParsing.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestMMcifOrganismParsing.java @@ -37,7 +37,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class TestMMcifOrganismParsing { @@ -90,7 +90,7 @@ private void checkPDB(String pdbId, String organismTaxId) throws IOException, St Structure s = StructureIO.getStructure(pdbId); assertNotNull(s.getEntityInfos()); - assertTrue(s.getEntityInfos().size() > 0); + assertFalse(s.getEntityInfos().isEmpty()); for ( EntityInfo c : s.getEntityInfos()) { if(EntityType.POLYMER.equals(c.getType())) { diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestSiftsParsing.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestSiftsParsing.java index 6a9f6ae93c..2b99d660d8 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestSiftsParsing.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestSiftsParsing.java @@ -47,9 +47,9 @@ public void test4DIA() throws Exception { for (SiftsEntity e : entities) { //System.out.println(e.getEntityId() + " " +e.getType()); - Assert.assertTrue(e.getSegments().size() > 0); + Assert.assertFalse(e.getSegments().isEmpty()); for (SiftsSegment seg : e.getSegments()) { - Assert.assertTrue(seg.getResidues().size() > 0); + Assert.assertFalse(seg.getResidues().isEmpty()); for (SiftsResidue res : seg.getResidues()) { @@ -78,9 +78,9 @@ public void test4jn3() throws Exception { for (SiftsEntity e : entities) { //System.out.println(e.getEntityId() + " " +e.getType()); - Assert.assertTrue(e.getSegments().size() > 0); + Assert.assertFalse(e.getSegments().isEmpty()); for (SiftsSegment seg : e.getSegments()) { - Assert.assertTrue(seg.getResidues().size() > 0); + Assert.assertFalse(seg.getResidues().isEmpty()); //System.out.println(seg.getResidues().size()); //System.out.println(" Segment: " + seg.getSegId() + " " + seg.getStart() + " " + seg.getEnd()) ; @@ -125,7 +125,7 @@ public void test4DOU() throws Exception { //assertTrue(seg1.getResidues().size() == 17); for (SiftsSegment seg : e.getSegments()) { - Assert.assertTrue(seg.getResidues().size() > 0); + Assert.assertFalse(seg.getResidues().isEmpty()); //System.out.println(" Segment: " + seg.getSegId() + " " + seg.getStart() + " " + seg.getEnd() + " res. size: " + seg.getResidues().size()) ; @@ -175,7 +175,7 @@ public void test4O6W() throws Exception { //System.out.println(" Segment: " + seg1.getSegId() + " " + seg1.getStart() + " " + seg1.getEnd() + " res. size: " + seg1.getResidues().size()); //assertTrue(seg1.getResidues().size() == 17); - Assert.assertTrue(seg.getResidues().size() > 0); + Assert.assertFalse(seg.getResidues().isEmpty()); for (SiftsResidue res : seg.getResidues()) { diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileConsumerImplTest.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileConsumerImplTest.java index a8925afa88..8fbd1060b0 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileConsumerImplTest.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileConsumerImplTest.java @@ -147,7 +147,7 @@ public void testWaterOnlyChainCif() throws IOException { Chain c = s2.getWaterChainByPDB("F"); assertNotNull("Got null when looking for water-only chain with author id F", c); - assertTrue(c.getAtomGroups().size() > 0); + assertFalse(c.getAtomGroups().isEmpty()); // checking that compounds are linked assertNotNull(c.getEntityInfo()); @@ -157,7 +157,7 @@ public void testWaterOnlyChainCif() throws IOException { Chain cAsymId = s2.getWaterChain("E"); assertNotNull("Got null when looking for water-only chain with asym id E", cAsymId); - assertTrue(cAsymId.getAtomGroups().size() > 0); + assertFalse(cAsymId.getAtomGroups().isEmpty()); assertSame(c, cAsymId); } diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileSupplierImplTest.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileSupplierImplTest.java index df227a8669..1d5f496ff5 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileSupplierImplTest.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/cif/CifFileSupplierImplTest.java @@ -1,5 +1,6 @@ package org.biojava.nbio.structure.io.cif; +import org.biojava.nbio.structure.PdbId; import org.biojava.nbio.structure.Structure; import org.biojava.nbio.structure.io.FileParsingParameters; import org.biojava.nbio.structure.io.PDBFileParser; @@ -41,4 +42,43 @@ public void shouldReadRawPdbOutputtingCifWithEntity() throws IOException { } } + + /** + * The identifier must be written as a data item and not only as the name of the data block: consumers read it + * from _entry.id or from _struct.entry_id, so writing the block header alone loses it. See issue #1143. + */ + @Test + public void shouldWriteEntryIdAndSurviveRoundTrip() throws IOException { + Structure s; + try (InputStream inStream = new GZIPInputStream(this.getClass().getResourceAsStream("/4hhb.cif.gz"))) { + s = CifStructureConverter.fromInputStream(inStream); + } + assertEquals(new PdbId("4HHB"), s.getPdbId()); + + String cifText = CifStructureConverter.toText(s); + assertTrue("_entry.id must be written", cifText.contains("_entry.id")); + assertTrue("_struct.entry_id must be written", cifText.contains("_struct.entry_id")); + + Structure readStruct = CifStructureConverter.fromInputStream( + new ByteArrayInputStream(cifText.getBytes())); + + assertEquals(s.getPdbId(), readStruct.getPdbId()); + assertEquals(s.getPdbId(), readStruct.getPDBHeader().getPdbId()); + } + + /** + * Structures without an identifier must not gain empty entry categories. + */ + @Test + public void shouldNotWriteEntryIdWhenPdbIdIsAbsent() throws IOException { + Structure s; + try (InputStream inStream = new GZIPInputStream(this.getClass().getResourceAsStream("/4hhb.cif.gz"))) { + s = CifStructureConverter.fromInputStream(inStream); + } + s.setPdbId(null); + + String cifText = CifStructureConverter.toText(s); + assertFalse(cifText.contains("_entry.id")); + assertFalse(cifText.contains("_struct.entry_id")); + } } diff --git a/biojava-survival/pom.xml b/biojava-survival/pom.xml index 4f3e00f56b..570a2dd99b 100644 --- a/biojava-survival/pom.xml +++ b/biojava-survival/pom.xml @@ -4,7 +4,7 @@ org.biojava biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-survival diff --git a/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/CoxInfo.java b/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/CoxInfo.java index c9f4f18056..eebdaf86ba 100644 --- a/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/CoxInfo.java +++ b/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/CoxInfo.java @@ -32,7 +32,7 @@ /** * Holds the results of a cox analysis where calling dump(), toString() will give an output similar to R - * @author Scooter Willis + * @author Scooter Willis */ public class CoxInfo { @@ -505,7 +505,7 @@ public String toString(String beginLine, String del, String endLine) { o = o + beginLine + endLine; - if (baselineSurvivorFunction.size() > 0) { + if (!baselineSurvivorFunction.isEmpty()) { o = o + beginLine + "Baseline Survivor Function (at predictor means)" + endLine; for (Double time : baselineSurvivorFunction.keySet()) { Double mean = baselineSurvivorFunction.get(time); diff --git a/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/ResidualsCoxph.java b/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/ResidualsCoxph.java index 42b34905cc..955a7c1f6d 100644 --- a/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/ResidualsCoxph.java +++ b/biojava-survival/src/main/java/org/biojava/nbio/survival/cox/ResidualsCoxph.java @@ -29,7 +29,7 @@ /** * - * @author Scooter Willis + * @author Scooter Willis */ public class ResidualsCoxph { @@ -108,7 +108,7 @@ public static double[][] process(CoxInfo ci, Type type, boolean useWeighted, Arr double[] weighted = ci.getWeighted(); rr = Matrix.scale(rr, weighted); } - if (cluster != null && cluster.size() > 0) { + if (cluster != null && !cluster.isEmpty()) { rr = rowsum(rr, cluster); } diff --git a/biojava-survival/src/main/java/org/biojava/nbio/survival/data/WorkSheet.java b/biojava-survival/src/main/java/org/biojava/nbio/survival/data/WorkSheet.java index 542085942e..17ed18419a 100644 --- a/biojava-survival/src/main/java/org/biojava/nbio/survival/data/WorkSheet.java +++ b/biojava-survival/src/main/java/org/biojava/nbio/survival/data/WorkSheet.java @@ -27,7 +27,7 @@ * Need to handle very large spreadsheets of expression data so keep memory * footprint low * - * @author Scooter Willis + * @author Scooter Willis */ public class WorkSheet { @@ -1391,7 +1391,7 @@ static public WorkSheet unionWorkSheetsRowJoin(WorkSheet w1, WorkSheet w2, boole ArrayList joinedColumns = new ArrayList<>(); joinedColumns.addAll(w1DataColumns); joinedColumns.addAll(w2DataColumns); - if (!joinedColumns.contains("META_DATA") && (w1MetaDataColumns.size() > 0 || w2MetaDataColumns.size() > 0)) { + if (!joinedColumns.contains("META_DATA") && (!w1MetaDataColumns.isEmpty() || !w2MetaDataColumns.isEmpty())) { joinedColumns.add("META_DATA"); } for (String column : w1MetaDataColumns) { diff --git a/biojava-survival/src/main/java/org/biojava/nbio/survival/kaplanmeier/figure/NumbersAtRiskPanel.java b/biojava-survival/src/main/java/org/biojava/nbio/survival/kaplanmeier/figure/NumbersAtRiskPanel.java index c4578f1c8b..144942a809 100644 --- a/biojava-survival/src/main/java/org/biojava/nbio/survival/kaplanmeier/figure/NumbersAtRiskPanel.java +++ b/biojava-survival/src/main/java/org/biojava/nbio/survival/kaplanmeier/figure/NumbersAtRiskPanel.java @@ -76,7 +76,7 @@ private void paintTable(Graphics g) { sfiHashMap = sfi.getStrataInfoHashMap(); } - if(sfiHashMap.size() == 0) + if(sfiHashMap.isEmpty()) return; //int height = this.getHeight(); diff --git a/biojava-ws/pom.xml b/biojava-ws/pom.xml index 7a4fa63589..c3fe2a9513 100644 --- a/biojava-ws/pom.xml +++ b/biojava-ws/pom.xml @@ -3,7 +3,7 @@ biojava org.biojava - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava-ws biojava-ws @@ -19,7 +19,7 @@ org.biojava biojava-core - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT compile diff --git a/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/HmmerResult.java b/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/HmmerResult.java index 3304e78d4f..373b78cd0a 100644 --- a/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/HmmerResult.java +++ b/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/HmmerResult.java @@ -140,7 +140,7 @@ public int compareTo(HmmerResult o) { return(me.getSqFrom().compareTo(other.getSqFrom())); } private boolean emptyDomains(HmmerResult o) { - if ( o.getDomains() == null || o.getDomains().size() == 0) + if ( o.getDomains() == null || o.getDomains().isEmpty()) return true; return false; } diff --git a/pom.xml b/pom.xml index f17d3c2d67..79db94ee26 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.biojava biojava pom - 7.2.3-SNAPSHOT + 7.2.7-SNAPSHOT biojava BioJava is an open-source project dedicated to providing a Java framework for processing biological data. It provides analytical and statistical routines, parsers for common file formats and allows the @@ -41,7 +41,7 @@ 512M 1.0.11 2.0.12 - 2.23.1 + 2.25.5 5.10.1 ciftools-java 7.0.1 @@ -217,7 +217,8 @@ 3.1.1 true - clean install + -DskipTests + clean verify -DskipTests true @@ -325,12 +326,6 @@ 3.1.3 - - org.apache.maven.plugins - maven-javadoc-plugin - 3.11.2 - - org.apache.maven.plugins maven-site-plugin @@ -486,7 +481,7 @@ com.google.guava guava - 33.4.0-jre + 33.6.0-jre @@ -580,17 +575,20 @@ release - - + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - ossrh - https://oss.sonatype.org/ - true + + central + + + true + + published