diff --git a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/loader/UniprotProxySequenceReader.java b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/loader/UniprotProxySequenceReader.java index 06cfd283f9..aecb563cae 100644 --- a/biojava-core/src/main/java/org/biojava/nbio/core/sequence/loader/UniprotProxySequenceReader.java +++ b/biojava-core/src/main/java/org/biojava/nbio/core/sequence/loader/UniprotProxySequenceReader.java @@ -495,6 +495,61 @@ private void writeCache(StringBuilder sb, String accession) throws IOException { fw.write(sb.toString()); fw.close(); } + + /** + * Open a URL connection. + * + * Follows redirects. + * @param url + * @throws IOException + */ + private static HttpURLConnection openURLConnection(URL url) throws IOException { + // This method should be moved to a utility class in BioJava 5.0 + + final int timeout = 5000; + final String useragent = "BioJava"; + + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestProperty("User-Agent", useragent); + conn.setInstanceFollowRedirects(true); + conn.setConnectTimeout(timeout); + conn.setReadTimeout(timeout); + + int status = conn.getResponseCode(); + while (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) { + // Redirect! + String newUrl = conn.getHeaderField("Location"); + + if(newUrl.equals(url.toString())) { + throw new IOException("Cyclic redirect detected at "+newUrl); + } + + // Preserve cookies + String cookies = conn.getHeaderField("Set-Cookie"); + + // open the new connection again + url = new URL(newUrl); + conn.disconnect(); + conn = (HttpURLConnection) url.openConnection(); + if(cookies != null) { + conn.setRequestProperty("Cookie", cookies); + } + conn.addRequestProperty("User-Agent", useragent); + conn.setInstanceFollowRedirects(true); + conn.setConnectTimeout(timeout); + conn.setReadTimeout(timeout); + conn.connect(); + + status = conn.getResponseCode(); + + logger.info("Redirecting from {} to {}", url, newUrl); + } + conn.connect(); + + return conn; + } private StringBuilder fetchUniprotXML(String uniprotURL) throws IOException, CompoundNotFoundException { @@ -504,11 +559,9 @@ private StringBuilder fetchUniprotXML(String uniprotURL) int attempt = 5; List errorCodes = new ArrayList(); while(attempt > 0) { - HttpURLConnection uniprotConnection = (HttpURLConnection) uniprot.openConnection(); - uniprotConnection.setRequestProperty("User-Agent", "BioJava"); - uniprotConnection.connect(); + HttpURLConnection uniprotConnection = openURLConnection(uniprot); int statusCode = uniprotConnection.getResponseCode(); - if (statusCode == 200) { + if (statusCode == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader( new InputStreamReader( uniprotConnection.getInputStream())); 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 2df4f0d8c6..0a25db1368 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,6 @@ */ package org.biojava.nbio.core.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -36,6 +33,15 @@ import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FileDownloadUtils { @@ -240,6 +246,41 @@ public static URLConnection prepareURLConnection(String url, int timeout) throws connection.setConnectTimeout(timeout); return connection; } + + /** + * Recursively delete a folder & contents + * + * @param dir directory to delete + */ + public static void deleteDirectory(Path dir) throws IOException { + if(dir == null || !Files.exists(dir)) + return; + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { + if (e != null) { + throw e; + } + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + /** + * Recursively delete a folder & contents + * + * @param dir directory to delete + */ + public static void deleteDirectory(String dir) throws IOException { + deleteDirectory(Paths.get(dir)); + } + public static void main(String[] args) { String url; diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java index 4e82ee4fed..6c4b546cd9 100644 --- a/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java +++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java @@ -417,7 +417,7 @@ public static ChromPos getChromPosForward(int cdsPos, List exonStarts, int codingLength = 0; @SuppressWarnings("unused") - int lengthExons = 0; + int lengthExons = 0; // map forward for (int i = 0; i < exonStarts.size(); i++) { @@ -565,7 +565,7 @@ public static int getCDSLengthForward(List exonStarts, List ex codingLength += (end - start + 1); } - return codingLength - 3; + return codingLength-3 ; } /** @@ -791,19 +791,19 @@ public static int getCDSPosForChromosomeCoordinate(int coordinate, GeneChromosom chromosomePosition.getCdsStart(), chromosomePosition.getCdsEnd()); } - - /** - * Converts the genetic coordinate to the position of the nucleotide on the mRNA sequence for a gene + + /** + * Converts the genetic coordinate to the position of the nucleotide on the mRNA sequence for a gene * living on the forward DNA strand. - * - * @param chromPos The genetic coordinate on a chromosome - * @param exonStarts The list holding the genetic coordinates pointing to the start positions of the exons (including UTR regions) + * + * @param chromPos The genetic coordinate on a chromosome + * @param exonStarts The list holding the genetic coordinates pointing to the start positions of the exons (including UTR regions) * @param exonEnds The list holding the genetic coordinates pointing to the end positions of the exons (including UTR regions) * @param cdsStart The start position of a coding region * @param cdsEnd The end position of a coding region - * + * * @return the position of the nucleotide base on the mRNA sequence corresponding to the input genetic coordinate (base 1) - * + * * @author Yana Valasatava */ public static int getCDSPosForward(int chromPos, List exonStarts, List exonEnds, diff --git a/biojava-integrationtest/pom.xml b/biojava-integrationtest/pom.xml index 45bcee439d..7a583353a6 100644 --- a/biojava-integrationtest/pom.xml +++ b/biojava-integrationtest/pom.xml @@ -102,7 +102,6 @@ mvn verify org.apache.maven.plugins maven-deploy-plugin - 2.8.2 true diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java index a6c48be969..6910f7ae61 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java @@ -20,7 +20,7 @@ * Created on 08.05.2004 * */ -package org.biojava.nbio.structure; +package org.biojava.nbio.structure ; import java.util.ArrayList; import java.util.Collection; @@ -70,7 +70,7 @@ public static final double getDistance(Atom a, Atom b) { double y = a.getY() - b.getY(); double z = a.getZ() - b.getZ(); - double s = x * x + y * y + z * z; + double s = x * x + y * y + z * z; return Math.sqrt(s); } @@ -92,11 +92,11 @@ public static double getDistanceFast(Atom a, Atom b) { double y = a.getY() - b.getY(); double z = a.getZ() - b.getZ(); - return x * x + y * y + z * z; + return x * x + y * y + z * z; } public static final Atom invert(Atom a) { - double[] coords = new double[] { 0.0, 0.0, 0.0 }; + double[] coords = new double[]{0.0,0.0,0.0} ; Atom zero = new AtomImpl(); zero.setCoords(coords); return subtract(zero, a); @@ -111,14 +111,14 @@ public static final Atom invert(Atom a) { * an Atom object * @return an Atom object */ - public static final Atom add(Atom a, Atom b) { + public static final Atom add(Atom a, Atom b){ Atom c = new AtomImpl(); - c.setX(a.getX() + b.getX()); - c.setY(a.getY() + b.getY()); - c.setZ(a.getZ() + b.getZ()); + c.setX( a.getX() + b.getX() ); + c.setY( a.getY() + b.getY() ); + c.setZ( a.getZ() + b.getZ() ); - return c; + return c ; } /** @@ -132,11 +132,11 @@ public static final Atom add(Atom a, Atom b) { */ public static final Atom subtract(Atom a, Atom b) { Atom c = new AtomImpl(); - c.setX(a.getX() - b.getX()); - c.setY(a.getY() - b.getY()); - c.setZ(a.getZ() - b.getZ()); + c.setX( a.getX() - b.getX() ); + c.setY( a.getY() - b.getY() ); + c.setZ( a.getZ() - b.getZ() ); - return c; + return c ; } /** @@ -148,13 +148,13 @@ public static final Atom subtract(Atom a, Atom b) { * an Atom object * @return an Atom object */ - public static final Atom vectorProduct(Atom a, Atom b) { + public static final Atom vectorProduct(Atom a , Atom b){ Atom c = new AtomImpl(); - c.setX(a.getY() * b.getZ() - a.getZ() * b.getY()); - c.setY(a.getZ() * b.getX() - a.getX() * b.getZ()); - c.setZ(a.getX() * b.getY() - a.getY() * b.getX()); - return c; + c.setX( a.getY() * b.getZ() - a.getZ() * b.getY() ) ; + c.setY( a.getZ() * b.getX() - a.getX() * b.getZ() ) ; + c.setZ( a.getX() * b.getY() - a.getY() * b.getX() ) ; + return c ; } @@ -178,8 +178,8 @@ public static final double scalarProduct(Atom a, Atom b) { * an Atom object * @return Square root of the sum of the squared elements */ - public static final double amount(Atom a) { - return Math.sqrt(scalarProduct(a, a)); + public static final double amount(Atom a){ + return Math.sqrt(scalarProduct(a,a)); } /** @@ -192,7 +192,7 @@ public static final double amount(Atom a) { * @return Angle between a and b in degrees, in range [0,180]. If either * vector has length 0 then angle is not defined and NaN is returned */ - public static final double angle(Atom a, Atom b) { + public static final double angle(Atom a, Atom b){ Vector3d va = new Vector3d(a.getCoordsAsPoint3d()); Vector3d vb = new Vector3d(b.getCoordsAsPoint3d()); @@ -209,13 +209,13 @@ public static final double angle(Atom a, Atom b) { * @return an Atom object */ public static final Atom unitVector(Atom a) { - double amount = amount(a); + double amount = amount(a) ; double[] coords = new double[3]; - coords[0] = a.getX() / amount; - coords[1] = a.getY() / amount; - coords[2] = a.getZ() / amount; + coords[0] = a.getX() / amount ; + coords[1] = a.getY() / amount ; + coords[2] = a.getZ() / amount ; a.setCoords(coords); return a; @@ -241,19 +241,19 @@ public static final Atom unitVector(Atom a) { */ public static final double torsionAngle(Atom a, Atom b, Atom c, Atom d) { - Atom ab = subtract(a, b); - Atom cb = subtract(c, b); - Atom bc = subtract(b, c); - Atom dc = subtract(d, c); + Atom ab = subtract(a,b); + Atom cb = subtract(c,b); + Atom bc = subtract(b,c); + Atom dc = subtract(d,c); - Atom abc = vectorProduct(ab, cb); - Atom bcd = vectorProduct(bc, dc); + Atom abc = vectorProduct(ab,cb); + Atom bcd = vectorProduct(bc,dc); - double angl = angle(abc, bcd); + double angl = angle(abc,bcd) ; /* calc the sign: */ - Atom vecprod = vectorProduct(abc, bcd); - double val = scalarProduct(cb, vecprod); + Atom vecprod = vectorProduct(abc,bcd); + double val = scalarProduct(cb,vecprod); if (val < 0.0) angl = -angl; @@ -275,22 +275,22 @@ public static final double torsionAngle(Atom a, Atom b, Atom c, Atom d) { public static final double getPhi(AminoAcid a, AminoAcid b) throws StructureException { - if (!isConnected(a, b)) { + if ( ! isConnected(a,b)){ throw new StructureException( "can not calc Phi - AminoAcids are not connected!"); } - Atom a_C = a.getC(); - Atom b_N = b.getN(); + Atom a_C = a.getC(); + Atom b_N = b.getN(); Atom b_CA = b.getCA(); - Atom b_C = b.getC(); + Atom b_C = b.getC(); // C and N were checked in isConnected already if (b_CA == null) throw new StructureException( "Can not calculate Phi, CA atom is missing"); - return torsionAngle(a_C, b_N, b_CA, b_C); + return torsionAngle(a_C,b_N,b_CA,b_C); } /** @@ -307,22 +307,22 @@ public static final double getPhi(AminoAcid a, AminoAcid b) */ public static final double getPsi(AminoAcid a, AminoAcid b) throws StructureException { - if (!isConnected(a, b)) { + if ( ! isConnected(a,b)) { throw new StructureException( "can not calc Psi - AminoAcids are not connected!"); } - Atom a_N = a.getN(); - Atom a_CA = a.getCA(); - Atom a_C = a.getC(); - Atom b_N = b.getN(); + Atom a_N = a.getN(); + Atom a_CA = a.getCA(); + Atom a_C = a.getC(); + Atom b_N = b.getN(); // C and N were checked in isConnected already if (a_CA == null) throw new StructureException( "Can not calculate Psi, CA atom is missing"); - return torsionAngle(a_N, a_CA, a_C, b_N); + return torsionAngle(a_N,a_CA,a_C,b_N); } @@ -339,17 +339,17 @@ public static final double getPsi(AminoAcid a, AminoAcid b) * @return true if ... */ public static final boolean isConnected(AminoAcid a, AminoAcid b) { - Atom C = null; + Atom C = null ; Atom N = null; C = a.getC(); N = b.getN(); - if (C == null || N == null) + if ( C == null || N == null) return false; // one could also check if the CA atoms are < 4 A... - double distance = getDistance(C, N); + double distance = getDistance(C,N); return distance < 2.5; } @@ -365,15 +365,15 @@ public static final boolean isConnected(AminoAcid a, AminoAcid b) { * @param m * a rotation matrix represented as a double[3][3] array */ - public static final void rotate(Atom atom, double[][] m) { + public static final void rotate(Atom atom, double[][] m){ double x = atom.getX(); - double y = atom.getY(); + double y = atom.getY() ; double z = atom.getZ(); - double nx = m[0][0] * x + m[0][1] * y + m[0][2] * z; - double ny = m[1][0] * x + m[1][1] * y + m[1][2] * z; - double nz = m[2][0] * x + m[2][1] * y + m[2][2] * z; + double nx = m[0][0] * x + m[0][1] * y + m[0][2] * z ; + double ny = m[1][0] * x + m[1][1] * y + m[1][2] * z ; + double nz = m[2][0] * x + m[2][1] * y + m[2][2] * z ; atom.setX(nx); atom.setY(ny); @@ -394,13 +394,13 @@ public static final void rotate(Atom atom, double[][] m) { public static final void rotate(Structure structure, double[][] rotationmatrix) throws StructureException { - if (rotationmatrix.length != 3) { - throw new StructureException("matrix does not have size 3x3 !"); + if ( rotationmatrix.length != 3 ) { + throw new StructureException ("matrix does not have size 3x3 !"); } - AtomIterator iter = new AtomIterator(structure); + AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { - Atom atom = iter.next(); - Calc.rotate(atom, rotationmatrix); + Atom atom = iter.next() ; + Calc.rotate(atom,rotationmatrix); } } @@ -417,15 +417,15 @@ public static final void rotate(Structure structure, public static final void rotate(Group group, double[][] rotationmatrix) throws StructureException { - if (rotationmatrix.length != 3) { - throw new StructureException("matrix does not have size 3x3 !"); + if ( rotationmatrix.length != 3 ) { + throw new StructureException ("matrix does not have size 3x3 !"); } - AtomIterator iter = new AtomIterator(group); + AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { - Atom atom = null; + Atom atom = null ; - atom = iter.next(); - rotate(atom, rotationmatrix); + atom = iter.next() ; + rotate(atom,rotationmatrix); } } @@ -439,19 +439,19 @@ public static final void rotate(Group group, double[][] rotationmatrix) * @param m * rotation matrix to be applied to the atom */ - public static final void rotate(Atom atom, Matrix m) { + public static final void rotate(Atom atom, Matrix m){ double x = atom.getX(); double y = atom.getY(); double z = atom.getZ(); - double[][] ad = new double[][] { { x, y, z } }; + double[][] ad = new double[][]{{x,y,z}}; Matrix am = new Matrix(ad); Matrix na = am.times(m); - atom.setX(na.get(0, 0)); - atom.setY(na.get(0, 1)); - atom.setZ(na.get(0, 2)); + atom.setX(na.get(0,0)); + atom.setY(na.get(0,1)); + atom.setZ(na.get(0,2)); } @@ -464,13 +464,13 @@ public static final void rotate(Atom atom, Matrix m) { * @param m * a Matrix object representing the rotation matrix */ - public static final void rotate(Group group, Matrix m) { + public static final void rotate(Group group, Matrix m){ - AtomIterator iter = new AtomIterator(group); + AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { - Atom atom = iter.next(); - rotate(atom, m); + Atom atom = iter.next() ; + rotate(atom,m); } @@ -485,13 +485,13 @@ public static final void rotate(Group group, Matrix m) { * @param m * rotation matrix to be applied */ - public static final void rotate(Structure structure, Matrix m) { + public static final void rotate(Structure structure, Matrix m){ - AtomIterator iter = new AtomIterator(structure); + AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { - Atom atom = iter.next(); - rotate(atom, m); + Atom atom = iter.next() ; + rotate(atom,m); } @@ -519,9 +519,9 @@ public static void transform(Atom[] ca, Matrix4d t) { * @param atom * @param m */ - public static final void transform(Atom atom, Matrix4d m) { + public static final void transform (Atom atom, Matrix4d m) { - Point3d p = new Point3d(atom.getX(), atom.getY(), atom.getZ()); + Point3d p = new Point3d(atom.getX(),atom.getY(),atom.getZ()); m.transform(p); atom.setX(p.x); @@ -570,7 +570,7 @@ public static final void transform(Structure structure, Matrix4d m) { * @param chain * @param m */ - public static final void transform(Chain chain, Matrix4d m) { + public static final void transform (Chain chain, Matrix4d m) { for (Group g : chain.getAtomGroups()) { transform(g, m); @@ -584,11 +584,11 @@ public static final void transform(Chain chain, Matrix4d m) { * @param atom * @param v */ - public static final void translate(Atom atom, Vector3d v) { + public static final void translate (Atom atom, Vector3d v) { - atom.setX(atom.getX() + v.x); - atom.setY(atom.getY() + v.y); - atom.setZ(atom.getZ() + v.z); + atom.setX(atom.getX()+v.x); + atom.setY(atom.getY()+v.y); + atom.setZ(atom.getZ()+v.z); } /** @@ -598,10 +598,10 @@ public static final void translate(Atom atom, Vector3d v) { * @param group * @param v */ - public static final void translate(Group group, Vector3d v) { + public static final void translate (Group group, Vector3d v) { for (Atom atom : group.getAtoms()) { - translate(atom, v); + translate(atom,v); } for (Group altG : group.getAltLocs()) { translate(altG, v); @@ -615,9 +615,9 @@ public static final void translate(Group group, Vector3d v) { * @param chain * @param v */ - public static final void translate(Chain chain, Vector3d v) { + public static final void translate (Chain chain, Vector3d v) { - for (Group g : chain.getAtomGroups()) { + for (Group g:chain.getAtomGroups()) { translate(g, v); } } @@ -629,7 +629,7 @@ public static final void translate(Chain chain, Vector3d v) { * @param structure * @param v */ - public static final void translate(Structure structure, Vector3d v) { + public static final void translate (Structure structure, Vector3d v) { for (int n=0; n .999d || m22 < -.999d) { - rZ1 = Math.toDegrees(Math.atan2(m.get(1, 0), m.get(1, 1))); + rZ1 = Math.toDegrees(Math.atan2(m.get(1,0), m.get(1,1))); rZ2 = 0; } else { - rZ1 = Math.toDegrees(Math.atan2(m.get(2, 1), -m.get(2, 0))); - rZ2 = Math.toDegrees(Math.atan2(m.get(1, 2), m.get(0, 2))); + rZ1 = Math.toDegrees(Math.atan2(m.get(2,1), -m.get(2,0))); + rZ2 = Math.toDegrees(Math.atan2(m.get(1,2), m.get(0,2))); } - return new double[] { rZ1, rY, rZ2 }; + return new double[] {rZ1,rY,rZ2}; } /** * Convert a rotation Matrix to Euler angles. This conversion uses * conventions as described on page: - * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm + * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm * Coordinate System: right hand Positive angle: right hand Order of euler * angles: heading first, then attitude, then bank * @@ -1045,24 +1045,24 @@ public static final double[] getZYZEuler(Matrix m) { * @return a array of three doubles containing the three euler angles in * radians */ - public static final double[] getXYZEuler(Matrix m) { + public static final double[] getXYZEuler(Matrix m){ double heading, attitude, bank; // Assuming the angles are in radians. - if (m.get(1, 0) > 0.998) { // singularity at north pole - heading = Math.atan2(m.get(0, 2), m.get(2, 2)); - attitude = Math.PI / 2; + if (m.get(1,0) > 0.998) { // singularity at north pole + heading = Math.atan2(m.get(0,2),m.get(2,2)); + attitude = Math.PI/2; bank = 0; - } else if (m.get(1, 0) < -0.998) { // singularity at south pole - heading = Math.atan2(m.get(0, 2), m.get(2, 2)); - attitude = -Math.PI / 2; + } else if (m.get(1,0) < -0.998) { // singularity at south pole + heading = Math.atan2(m.get(0,2),m.get(2,2)); + attitude = -Math.PI/2; bank = 0; } else { - heading = Math.atan2(-m.get(2, 0), m.get(0, 0)); - bank = Math.atan2(-m.get(1, 2), m.get(1, 1)); - attitude = Math.asin(m.get(1, 0)); + heading = Math.atan2(-m.get(2,0),m.get(0,0)); + bank = Math.atan2(-m.get(1,2),m.get(1,1)); + attitude = Math.asin(m.get(1,0)); } return new double[] { heading, attitude, bank }; } @@ -1070,7 +1070,7 @@ public static final double[] getXYZEuler(Matrix m) { /** * This conversion uses NASA standard aeroplane conventions as described on * page: - * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm + * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm * Coordinate System: right hand Positive angle: right hand Order of euler * angles: heading first, then attitude, then bank. matrix row column * ordering: [m00 m01 m02] [m10 m11 m12] [m20 m21 m22] @@ -1093,16 +1093,16 @@ public static final Matrix matrixFromEuler(double heading, double attitude, double cb = Math.cos(bank); double sb = Math.sin(bank); - Matrix m = new Matrix(3, 3); - m.set(0, 0, ch * ca); - m.set(0, 1, sh * sb - ch * sa * cb); - m.set(0, 2, ch * sa * sb + sh * cb); - m.set(1, 0, sa); - m.set(1, 1, ca * cb); - m.set(1, 2, -ca * sb); - m.set(2, 0, -sh * ca); - m.set(2, 1, sh * sa * cb + ch * sb); - m.set(2, 2, -sh * sa * sb + ch * cb); + Matrix m = new Matrix(3,3); + m.set(0,0, ch * ca); + m.set(0,1, sh*sb - ch*sa*cb); + m.set(0,2, ch*sa*sb + sh*cb); + m.set(1,0, sa); + m.set(1,1, ca*cb); + m.set(1,2, -ca*sb); + m.set(2,0, -sh*ca); + m.set(2,1, sh*sa*cb + ch*sb); + m.set(2,2, -sh*sa*sb + ch*cb); return m; } @@ -1120,13 +1120,13 @@ public static final Matrix matrixFromEuler(double heading, double attitude, * Point we are rotating around. * @param targetPt * Point we want to calculate the angle to. - * @return angle in degrees. This is the angle from centerPt to targetPt. + * @return angle in degrees. This is the angle from centerPt to targetPt. */ public static double calcRotationAngleInDegrees(Atom centerPt, Atom targetPt) { // calculate the angle theta from the deltaY and deltaX values // (atan2 returns radians values from [-PI,PI]) // 0 currently points EAST. - // NOTE: By preserving Y and X param order to atan2, we are expecting + // NOTE: By preserving Y and X param order to atan2, we are expecting // a CLOCKWISE angle direction. double theta = Math.atan2(targetPt.getY() - centerPt.getY(), targetPt.getX() - centerPt.getX()); @@ -1135,7 +1135,7 @@ public static double calcRotationAngleInDegrees(Atom centerPt, Atom targetPt) { // (this makes 0 point NORTH) // NOTE: adding to an angle rotates it clockwise. // subtracting would rotate it counter-clockwise - theta += Math.PI / 2.0; + theta += Math.PI/2.0; // convert from radians to degrees // this will give you an angle from [0->270],[-180,0] @@ -1152,8 +1152,8 @@ public static double calcRotationAngleInDegrees(Atom centerPt, Atom targetPt) { return angle; } - public static void main(String[] args) { - Atom a = new AtomImpl(); + public static void main(String[] args){ + Atom a =new AtomImpl(); a.setX(0); a.setY(0); a.setZ(0); @@ -1200,7 +1200,7 @@ public static Matrix4d getTransformation(Matrix rot, Atom trans) { return new Matrix4d(new Matrix3d(rot.getColumnPackedCopy()), new Vector3d(trans.getCoordsAsPoint3d()), 1.0); } - + /** * Extract the translational vector as an Atom of a transformation matrix. * @@ -1208,10 +1208,10 @@ public static Matrix4d getTransformation(Matrix rot, Atom trans) { * Matrix4d * @return Atom shift vector */ - public static Atom getTranslationVector(Matrix4d transform) { + public static Atom getTranslationVector(Matrix4d transform){ Atom transl = new AtomImpl(); - double[] coords = { transform.m03, transform.m13, transform.m23 }; + double[] coords = {transform.m03, transform.m13, transform.m23}; transl.setCoords(coords); return transl; } @@ -1225,7 +1225,7 @@ public static Atom getTranslationVector(Matrix4d transform) { */ public static Point3d[] atomsToPoints(Atom[] atoms) { Point3d[] points = new Point3d[atoms.length]; - for (int i = 0; i < atoms.length; i++) { + for(int i = 0; i< atoms.length;i++) { points[i] = atoms[i].getCoordsAsPoint3d(); } return points; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/StructureIO.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/StructureIO.java index e42b374a89..886d81c2cc 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/StructureIO.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/StructureIO.java @@ -114,8 +114,9 @@ private static void checkInitAtomCache() { public static void setAtomCache(AtomCache c){ cache = c; } - + public static AtomCache getAtomCache() { + checkInitAtomCache(); return cache; } 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 c845c76201..3a06c80752 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 @@ -636,6 +636,7 @@ 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 = File.createTempFile(FileDownloadUtils.getFilePrefix(localFile), "."+ FileDownloadUtils.getFileExtension(localFile)); @@ -665,7 +666,7 @@ protected void downloadFileFromRemote(URL remoteURL, File localFile) throws IOEx disp = disp / 1024.0; } long timeE = System.currentTimeMillis(); - LOGGER.info("Downloaded file {} ({}) to local file {} in {} sec.", remoteURL, String.format("%.1f",disp) + unit, localFile, (timeE - timeS)/1000); + LOGGER.info("Downloaded {} in {} sec. to {}", String.format("%.1f",disp) + unit, (timeE - timeS)/1000, localFile); } private boolean domainDescriptionFileAvailable(){ 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 d4f6c7222a..762f821eb2 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 @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.file.Files; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -128,6 +129,9 @@ public static enum FetchBehavior { protected static final String lineSplit = System.getProperty("file.separator"); + /** Minimum size for a valid structure file (CIF or PDB), in bytes */ + public static final long MIN_PDB_FILE_SIZE = 40; // Empty gzip files are 20bytes. Add a few more for buffer. + private File path; private List extensions; @@ -382,8 +386,9 @@ public void prefetchStructure(String pdbId) throws IOException { * Attempts to delete all versions of a structure from the local directory. * @param pdbId * @return True if one or more files were deleted + * @throws IOException if the file cannot be deleted */ - public boolean deleteStructure(String pdbId){ + public boolean deleteStructure(String pdbId) throws IOException{ boolean deleted = false; // Force getLocalFile to check in obsolete locations ObsoleteBehavior obsolete = getObsoleteBehavior(); @@ -401,7 +406,7 @@ public boolean deleteStructure(String pdbId){ // delete file boolean success = existing.delete(); if(success) { - logger.info("Deleting "+existing.getAbsolutePath()); + logger.debug("Deleting "+existing.getAbsolutePath()); } deleted = deleted || success; @@ -410,7 +415,7 @@ public boolean deleteStructure(String pdbId){ if(parent != null) { success = parent.delete(); if(success) { - logger.info("Deleting "+parent.getAbsolutePath()); + logger.debug("Deleting "+parent.getAbsolutePath()); } } @@ -630,8 +635,9 @@ protected File getDir(String pdbId, boolean obsolete) { * Searches for previously downloaded files * @param pdbId * @return A file pointing to the existing file, or null if not found + * @throws IOException If the file exists but is empty and can't be deleted */ - public File getLocalFile(String pdbId) { + public File getLocalFile(String pdbId) throws IOException { // Search for existing files @@ -657,6 +663,11 @@ public File getLocalFile(String pdbId) { for(String ex : getExtensions() ){ File f = new File(searchdir,prefix + pdbId.toLowerCase() + ex) ; if ( f.exists()) { + // delete files that are too short to have contents + if( f.length() < MIN_PDB_FILE_SIZE ) { + Files.delete(f.toPath()); + return null; + } return f; } } @@ -667,9 +678,11 @@ public File getLocalFile(String pdbId) { } protected boolean checkFileExists(String pdbId){ - File path = getLocalFile(pdbId); - if ( path != null) - return true; + try { + File path = getLocalFile(pdbId); + if ( path != null) + return true; + } catch(IOException e) {} return false; } 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 66088feafb..116c98d7e0 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 @@ -151,7 +151,7 @@ public class PDBFileParser { private Chain currentChain; private Group currentGroup; - private List seqResChains; // contains all the chains for the SEQRES records + private List seqResChains; // contains all the chains for the SEQRES records //we're going to work on the assumption that the files are current - //if the pdb_HEADER_Handler detects a legacy format, this will be changed to true. //if true then lines will be truncated at 72 characters in certain cases @@ -159,7 +159,7 @@ public class PDBFileParser { private boolean isLegacyFormat = false; private boolean blankChainIdsPresent = false; - + // for re-creating the biological assembly private PDBBioAssemblyParser bioAssemblyParser = null; @@ -186,9 +186,9 @@ public class PDBFileParser { private Map> siteToResidueMap = new LinkedHashMap>(); private List ssbonds = new ArrayList<>(); - - // for storing LINK until we have all the atoms parsed - private List linkRecords; + + // for storing LINK until we have all the atoms parsed + private List linkRecords; private Matrix4d currentNcsOp; private List ncsOperators; @@ -253,7 +253,7 @@ public class PDBFileParser { private FileParsingParameters params; - + private boolean startOfMolecule; private boolean startOfModel; @@ -261,7 +261,7 @@ public PDBFileParser() { params = new FileParsingParameters(); allModels = new ArrayList<>(); - structure = null; + structure = null ; currentModel = null; currentChain = null; currentGroup = null; @@ -285,14 +285,14 @@ public PDBFileParser() { atomCount = 0; atomOverflow = false; parseCAonly = false; - + // this SHOULD not be done // DONOT:setFileParsingParameters(params); // set the correct max values for parsing... loadMaxAtoms = params.getMaxAtoms(); atomCAThreshold = params.getAtomCaThreshold(); - - linkRecords = new ArrayList(); + + linkRecords = new ArrayList(); blankChainIdsPresent = false; @@ -675,7 +675,7 @@ private void pdb_REVDAT_Handler(String line) { // keep the first as latest modified date and the last as release date Date modDate = pdbHeader.getModDate(); - if ( modDate == null || modDate.equals(new Date(0)) ) { + if ( modDate==null || modDate.equals(new Date(0)) ) { // modified date is still uninitialized String modificationDate = line.substring (13, 22).trim() ; @@ -1027,7 +1027,7 @@ private void compndValueSetter(String field, String value) { current_compound = new EntityInfo(); current_compound.setMolId(i); - + // we will set polymer for all defined compounds in PDB file (non-polymer compounds are not defined in header) - JD 2016-03-25 current_compound.setType(EntityType.POLYMER); @@ -1547,7 +1547,7 @@ private void pdb_CRYST1_Handler(String line) { * * * Note that we ignore operators with iGiven==1 - * + * * @param line */ private void pdb_MTRIXn_Handler(String line) { @@ -1860,7 +1860,7 @@ private void pdb_ATOM_Handler(String line) { + "from Chemical Component Dictionary information", fullname.trim(), pdbnumber); } else { - try { + try { element = Element.valueOfIgnoreCase(elementSymbol); guessElement = false; } catch (IllegalArgumentException e){ @@ -1886,9 +1886,9 @@ private void pdb_ATOM_Handler(String line) { if (elementSymbol == null) { logger.info("Atom name {} was not found in the Chemical Component Dictionary information of {}. " + "Assigning generic element R to it", fullname.trim(), currentGroup.getPDBName()); - } else { - try { - element = Element.valueOfIgnoreCase(elementSymbol); + } else { + try { + element = Element.valueOfIgnoreCase(elementSymbol); } catch (IllegalArgumentException e) { // this can still happen for cases like UNK logger.info("Element symbol {} found in chemical component dictionary for Atom {} {} could not be recognised as a known element. " @@ -1922,7 +1922,7 @@ private void pdb_ATOM_Handler(String line) { - } + } private Group getCorrectAltLocGroup( Character altLoc, @@ -2099,7 +2099,7 @@ private void pdb_CONECT_Handler(String line) { private void pdb_MODEL_Handler(String line) { if (params.isHeaderOnly()) return; - + // new model: we start a new molecule startOfMolecule = true; startOfModel = true; @@ -2568,7 +2568,7 @@ public Structure parsePDBFile(InputStream inStream) */ public Structure parsePDBFile(BufferedReader buf) throws IOException - { + { // set the correct max values for parsing... loadMaxAtoms = params.getMaxAtoms(); atomCAThreshold = params.getAtomCaThreshold(); @@ -2607,7 +2607,7 @@ public Structure parsePDBFile(BufferedReader buf) atomOverflow = false; linkRecords = new ArrayList(); siteToResidueMap.clear(); - + blankChainIdsPresent = false; parseCAonly = params.isParseCAOnly(); @@ -2689,7 +2689,7 @@ else if ( params.isParseSecStruc()) { } } catch (StringIndexOutOfBoundsException | NullPointerException ex) { logger.info("Unable to parse [" + line + "]"); - } + } } makeCompounds(compndLines, sourceLines); @@ -2712,7 +2712,7 @@ else if ( params.isParseSecStruc()) { return structure; - } + } /** @@ -2767,13 +2767,13 @@ private void makeCompounds(List compoundList, private void formBonds() { BondMaker maker = new BondMaker(structure, params); - + // LINK records should be preserved, they are the way that // inter-residue bonds are created for ligands such as trisaccharides, unusual polymers. // The analogy in mmCIF is the _struct_conn record. for (LinkRecord linkRecord : linkRecords) { - maker.formLinkRecordBond(linkRecord); - } + maker.formLinkRecordBond(linkRecord); + } maker.formDisulfideBonds(ssbonds); @@ -2807,7 +2807,7 @@ private void triggerEndFileChecks(){ // header data - + Date modDate = pdbHeader.getModDate(); if ( modDate.equals(new Date(0)) ) { // modification date = deposition date @@ -2819,7 +2819,7 @@ private void triggerEndFileChecks(){ } } - + structure.setPDBHeader(pdbHeader); structure.setCrystallographicInfo(crystallographicInfo); @@ -2828,7 +2828,7 @@ private void triggerEndFileChecks(){ buildjournalArticle(); pdbHeader.setJournalArticle(journalArticle); } - + structure.setDBRefs(dbrefs); // Only align if requested (default) and not when headerOnly mode with no Atoms. @@ -2844,7 +2844,7 @@ private void triggerEndFileChecks(){ } - + //associate the temporary Groups in the siteMap to the ones if (!params.isHeaderOnly()) { // Only can link SITES if Atom Groups were parsed. @@ -3175,10 +3175,10 @@ private void assignChainsAndEntities(){ if (!entities.isEmpty()) { // if the file contained COMPOUND records then we can assign entities to the poly chains for (EntityInfo comp : entities){ - List chainIds = compoundMolIds2chainIds.get(comp.getMolId()); - if ( chainIds == null) - continue; - for ( String chainId : chainIds) { + List chainIds = compoundMolIds2chainIds.get(comp.getMolId()); + if ( chainIds == null) + continue; + for ( String chainId : chainIds) { List> models = findChains(chainId, polyModels); @@ -3189,11 +3189,11 @@ private void assignChainsAndEntities(){ } if (matchingChains.isEmpty()) { - // usually if this happens something is wrong with the PDB header - // e.g. 2brd - there is no Chain A, although it is specified in the header - // Some bona-fide cases exist, e.g. 2ja5, chain N is described in SEQRES - // but the authors didn't observe in the density so it's completely missing - // from the ATOM lines + // usually if this happens something is wrong with the PDB header + // e.g. 2brd - there is no Chain A, although it is specified in the header + // Some bona-fide cases exist, e.g. 2ja5, chain N is described in SEQRES + // but the authors didn't observe in the density so it's completely missing + // from the ATOM lines logger.warn("Could not find polymeric chain {} to link to entity {}. The chain will be missing in the entity.", chainId, comp.getMolId()); } } @@ -3225,11 +3225,11 @@ private void assignChainsAndEntities(){ model.addAll(splitNonPolyModels.get(i)); model.addAll(waterModels.get(i)); structure.addModel(model); - } + } } - + /** * Links the Sites in the siteMap to the Groups in the Structure via the * siteToResidueMap ResidueNumber. @@ -3643,6 +3643,7 @@ public void setFileParsingParameters(FileParsingParameters params) loadMaxAtoms = params.getMaxAtoms(); atomCAThreshold = params.getAtomCaThreshold(); + } public FileParsingParameters getFileParsingParameters(){ @@ -3650,4 +3651,4 @@ public FileParsingParameters getFileParsingParameters(){ } -} +} \ No newline at end of file diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/ChemCompGroupFactory.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/ChemCompGroupFactory.java index c0590e9a40..05ceff6afb 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/ChemCompGroupFactory.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/ChemCompGroupFactory.java @@ -68,9 +68,8 @@ public static ChemComp getChemComp(String recordName){ * again. Note that this change can have unexpected behavior of * code executed afterwards. *

- * Changing the provider does not reset the cache, so Chemical - * Component definitions already downloaded from previous providers - * will be used. To reset the cache see {@link #getCache()). + * Changing the provider also resets the cache, so any groups + * previously accessed will be reread or re-downloaded. * * @param provider */ @@ -84,6 +83,15 @@ public static void setChemCompProvider(ChemCompProvider provider) { public static ChemCompProvider getChemCompProvider(){ return chemCompProvider; } + + /** + * Force the in-memory cache to be reset. + * + * Note that the ChemCompProvider may have additional memory or disk caches that need to be cleared too. + */ + public static void clearCache() { + cache.clear(); + } public static Group getGroupFromChemCompDictionary(String recordName) { diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/DownloadChemCompProvider.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/DownloadChemCompProvider.java index d1dcbb08f7..680d72d83e 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/DownloadChemCompProvider.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/DownloadChemCompProvider.java @@ -42,6 +42,7 @@ import org.biojava.nbio.core.util.InputStreamProvider; import org.biojava.nbio.structure.align.util.URLConnectionTools; import org.biojava.nbio.structure.align.util.UserConfiguration; +import org.biojava.nbio.structure.io.LocalPDBDirectory; import org.biojava.nbio.structure.io.mmcif.model.ChemComp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,13 +51,13 @@ /** * This provider of chemical components can download and cache chemical component definition files from the RCSB PDB web site. - * It is the default way to access these definitions. - * If this provider is called he first time, it will download and install all chemical - * component definitions in a local directory. - * Once the definition files have been installed, it has quick startup time and low memory requirements. + * It is the default way to access these definitions. + * If this provider is called he first time, it will download and install all chemical + * component definitions in a local directory. + * Once the definition files have been installed, it has quick startup time and low memory requirements. * - * An alternative provider, that keeps all definitions in memory is the {@link AllChemCompProvider}. Another provider, that - * does not require any network access, but only can support a limited set of chemical component definitions, is the {@link ReducedChemCompProvider}. + * An alternative provider, that keeps all definitions in memory is the {@link AllChemCompProvider}. Another provider, that + * does not require any network access, but only can support a limited set of chemical component definitions, is the {@link ReducedChemCompProvider}. * * * @author Andreas Prlic @@ -93,6 +94,8 @@ public class DownloadChemCompProvider implements ChemCompProvider { protectedIDs.add("AUX"); protectedIDs.add("NUL"); } + + private static ChemCompProvider fallback = null; // Fallback provider if the download fails /** by default we will download only some of the files. User has to request that all files should be downloaded... * @@ -100,25 +103,28 @@ public class DownloadChemCompProvider implements ChemCompProvider { boolean downloadAll = false; public DownloadChemCompProvider(){ - logger.debug("Initialising DownloadChemCompProvider"); - - // note that path is static, so this is just to make sure that all non-static methods will have path initialised - initPath(); + this(null); } public DownloadChemCompProvider(String cacheFilePath){ logger.debug("Initialising DownloadChemCompProvider"); // note that path is static, so this is just to make sure that all non-static methods will have path initialised - path = new File(cacheFilePath); + if(cacheFilePath != null) { + path = new File(cacheFilePath); + } } - private static void initPath(){ - + /** + * Get this provider's cache path + * @return + */ + public static File getPath(){ if (path==null) { UserConfiguration config = new UserConfiguration(); path = new File(config.getCacheFilePath()); } + return path; } /** @@ -135,7 +141,7 @@ public void checkDoFirstInstall(){ // this makes sure there is a file separator between every component, // if path has a trailing file separator or not, it will work for both cases - File dir = new File(path, CHEM_COMP_CACHE_DIRECTORY); + File dir = new File(getPath(), CHEM_COMP_CACHE_DIRECTORY); File f = new File(dir, "components.cif.gz"); if ( ! f.exists()) { @@ -169,7 +175,7 @@ private void split() throws IOException { logger.info("Installing individual chem comp files ..."); - File dir = new File(path, CHEM_COMP_CACHE_DIRECTORY); + File dir = new File(getPath(), CHEM_COMP_CACHE_DIRECTORY); File f = new File(dir, "components.cif.gz"); @@ -220,7 +226,7 @@ private void split() throws IOException { */ private void writeID(String contents, String currentID) throws IOException{ - String localName = DownloadChemCompProvider.getLocalFileName(currentID); + String localName = getLocalFileName(currentID); try ( PrintWriter pw = new PrintWriter(new GZIPOutputStream(new FileOutputStream(localName))) ) { @@ -280,7 +286,10 @@ public ChemComp getChemComp(String recordName) { ChemComp chemComp = dict.getChemComp(recordName); - return chemComp; + // May be null if the file was corrupt. Fall back on ReducedChemCompProvider in that case + if(chemComp != null) { + return chemComp; + } } catch (IOException e) { @@ -305,9 +314,12 @@ public ChemComp getChemComp(String recordName) { // see https://github.com/biojava/biojava/issues/315 // probably a network error happened. Try to use the ReducedChemCOmpProvider - ReducedChemCompProvider reduced = new ReducedChemCompProvider(); + if( fallback == null) { + fallback = new ReducedChemCompProvider(); + } - return reduced.getChemComp(recordName); + logger.warn("Falling back to ReducedChemCompProvider for {}. This could indicate a network error.", recordName); + return fallback.getChemComp(recordName); } @@ -323,16 +335,15 @@ public static String getLocalFileName(String recordName){ recordName = "_" + recordName; } - initPath(); - - File f = new File(path, CHEM_COMP_CACHE_DIRECTORY); + File f = new File(getPath(), CHEM_COMP_CACHE_DIRECTORY); if (! f.exists()){ logger.info("Creating directory " + f); boolean success = f.mkdir(); // we've checked in initPath that path is writable, so there's no need to check if it succeeds // in the unlikely case that in the meantime it isn't writable at least we log an error - if (!success) logger.error("Directory {} could not be created",f); + if (!success) + logger.error("Directory {} could not be created",f); } @@ -347,6 +358,14 @@ private static boolean fileExists(String recordName){ File f = new File(fileName); + // delete files that are too short to have contents + if( f.length() < LocalPDBDirectory.MIN_PDB_FILE_SIZE ) { + // Delete defensively. + // Note that if delete is unsuccessful, we re-download the file anyways + f.delete(); + return false; + } + return f.exists(); } @@ -452,7 +471,7 @@ private void downloadAllDefinitions() { split(); } catch (IOException e) { logger.error("Could not split all chem comp file into individual chemical component files. Error: {}", - e.getMessage()); + e.getMessage()); // no point in reporting time loading.set(false); return; diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifConsumer.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifConsumer.java index 3ce4bde692..5db945efb2 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifConsumer.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifConsumer.java @@ -473,10 +473,10 @@ public void newAtomSite(AtomSite atom) { } // ANTHONY BRADLEY ADDED THIS -> WE ONLY WAN'T TO CHECK FOR ALT LOCS WHEN IT's NOT THE FIRST GROUP IN CHAIN else{ - // check if residue number is the same ... - // insertion code is part of residue number + // check if residue number is the same ... + // insertion code is part of residue number if ( ! residueNumber.equals(currentGroup.getResidueNumber())) { - //System.out.println("end of residue: "+current_group.getPDBCode()+" "+residueNrInt); + //System.out.println("end of residue: "+current_group.getPDBCode()+" "+residueNrInt); currentChain.addGroup(currentGroup); currentGroup.trimToSize(); currentGroup = getNewGroup(recordName,aminoCode1,seq_id,groupCode3); @@ -485,14 +485,14 @@ public void newAtomSite(AtomSite atom) { currentGroup.setHetAtomInFile(isHetAtomInFile); - } else { - // same residueNumber, but altLocs... - // test altLoc + } else { + // same residueNumber, but altLocs... + // test altLoc - if ( ! altLoc.equals(' ') && ( ! altLoc.equals('.'))) { + if ( ! altLoc.equals(' ') && ( ! altLoc.equals('.'))) { logger.debug("found altLoc! " + altLoc + " " + currentGroup + " " + altGroup); - altGroup = getCorrectAltLocGroup( altLoc,recordName,aminoCode1,groupCode3, seq_id); - if (altGroup.getChain()==null) { + altGroup = getCorrectAltLocGroup( altLoc,recordName,aminoCode1,groupCode3, seq_id); + if (altGroup.getChain()==null) { altGroup.setChain(currentChain); } } @@ -527,7 +527,7 @@ public void newAtomSite(AtomSite atom) { String atomName = a.getName(); - // make sure that main group has all atoms + // make sure that main group has all atoms // GitHub issue: #76 if ( ! currentGroup.hasAtom(atomName)) { // Unless it's microheterogenity https://github.com/rcsb/codec-devel/issues/81 @@ -729,7 +729,7 @@ public void documentEnd() { // we'll only add seqres chains that are polymeric or unknown if (type==null || type==EntityType.POLYMER ) { - seqResChains.add(seqres); + seqResChains.add(seqres); } logger.debug(" seqres: " + asym.getId() + " " + seqres + "<") ; @@ -1635,7 +1635,7 @@ public void newCell(Cell cell) { if (!xtalCell.isCellReasonable()) { // If the entry describes a structure determined by a technique other than X-ray crystallography, - // cell is (sometimes!) a = b = c = 1.0, alpha = beta = gamma = 90 degrees + // cell is (sometimes!) a = b = c = 1.0, alpha = beta = gamma = 90 degrees // if so we don't add and CrystalCell will be null logger.debug("The crystal cell read from file does not have reasonable dimensions (at least one dimension is below {}), discarding it.", CrystalCell.MIN_VALID_CELL_SIZE); @@ -1744,7 +1744,7 @@ public void newStructRefSeq(StructRefSeq sref) { r.setDatabase(structRef.getDb_name()); r.setDbIdCode(structRef.getDb_code()); } - + int seqbegin; int seqend; try{ @@ -2099,68 +2099,68 @@ private void addSites() { if (sites == null) sites = new ArrayList(); for (StructSiteGen siteGen : structSiteGens) { - // For each StructSiteGen, find the residues involved, if they exist then - String site_id = siteGen.getSite_id(); // multiple could be in same site. - if (site_id == null) site_id = ""; - String comp_id = siteGen.getLabel_comp_id(); // PDBName + // For each StructSiteGen, find the residues involved, if they exist then + String site_id = siteGen.getSite_id(); // multiple could be in same site. + if (site_id == null) site_id = ""; + String comp_id = siteGen.getLabel_comp_id(); // PDBName - // Assumption: the author chain ID and residue number for the site is consistent with the original - // author chain id and residue numbers. + // Assumption: the author chain ID and residue number for the site is consistent with the original + // author chain id and residue numbers. String asymId = siteGen.getLabel_asym_id(); // chain name String authId = siteGen.getAuth_asym_id(); // chain Id - String auth_seq_id = siteGen.getAuth_seq_id(); // Res num + String auth_seq_id = siteGen.getAuth_seq_id(); // Res num - String insCode = siteGen.getPdbx_auth_ins_code(); - if ( insCode != null && insCode.equals("?")) - insCode = null; + String insCode = siteGen.getPdbx_auth_ins_code(); + if ( insCode != null && insCode.equals("?")) + insCode = null; - // Look for asymID = chainID and seqID = seq_ID. Check that comp_id matches the resname. - Group g = null; - try { + // Look for asymID = chainID and seqID = seq_ID. Check that comp_id matches the resname. + Group g = null; + try { Chain chain = structure.getChain(asymId); - if (null != chain) { - try { - Character insChar = null; - if (null != insCode && insCode.length() > 0) insChar = insCode.charAt(0); + if (null != chain) { + try { + Character insChar = null; + if (null != insCode && insCode.length() > 0) insChar = insCode.charAt(0); g = chain.getGroupByPDB(new ResidueNumber(null, Integer.parseInt(auth_seq_id), insChar)); - } catch (NumberFormatException e) { + } catch (NumberFormatException e) { logger.warn("Could not lookup residue : " + authId + auth_seq_id); + } } + } catch (StructureException e) { + logger.warn("Problem finding residue in site entry " + siteGen.getSite_id() + " - " + e.getMessage(), e.getMessage()); } - } catch (StructureException e) { - logger.warn("Problem finding residue in site entry " + siteGen.getSite_id() + " - " + e.getMessage(), e.getMessage()); - } - if (g != null) { - // 2. find the site_id, if not existing, create anew. - Site site = null; - for (Site asite: sites) { - if (site_id.equals(asite.getSiteID())) site = asite; - } + if (g != null) { + // 2. find the site_id, if not existing, create anew. + Site site = null; + for (Site asite: sites) { + if (site_id.equals(asite.getSiteID())) site = asite; + } - boolean addSite = false; + boolean addSite = false; - // 3. add this residue to the site. - if (site == null) { - addSite = true; - site = new Site(); - site.setSiteID(site_id); - } + // 3. add this residue to the site. + if (site == null) { + addSite = true; + site = new Site(); + site.setSiteID(site_id); + } - List groups = site.getGroups(); - if (groups == null) groups = new ArrayList(); + List groups = site.getGroups(); + if (groups == null) groups = new ArrayList(); - // Check the self-consistency of the residue reference from auth_seq_id and chain_id - if (!comp_id.equals(g.getPDBName())) { + // Check the self-consistency of the residue reference from auth_seq_id and chain_id + if (!comp_id.equals(g.getPDBName())) { logger.warn("comp_id doesn't match the residue at " + authId + " " + auth_seq_id + " - skipping"); - } else { - groups.add(g); - site.setGroups(groups); + } else { + groups.add(g); + site.setGroups(groups); + } + if (addSite) sites.add(site); } - if (addSite) sites.add(site); - } } structure.setSites(sites); } diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifParser.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifParser.java index 304d6ff01c..4c94881502 100644 --- a/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifParser.java +++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/io/mmcif/SimpleMMcifParser.java @@ -211,7 +211,7 @@ public void parse(BufferedReader buf) Set loopWarnings = new HashSet(); // used only to reduce logging statements String category = null; - + boolean foundHeader = false; while ( (line = buf.readLine ()) != null ){ @@ -647,13 +647,13 @@ private void endLineChecks(String category,List loopFields, List triggerNewDatabasePDBrev(dbrev); - } else if ( category.equals("_database_PDB_rev_record")) { + } else if ( category.equals("_database_PDB_rev_record")){ DatabasePdbrevRecord dbrev = (DatabasePdbrevRecord) buildObject( DatabasePdbrevRecord.class.getName(), loopFields, lineData, loopWarnings); triggerNewDatabasePDBrevRecord(dbrev); - + // MMCIF version 5 dates } else if ( category.equals("_pdbx_audit_revision_history")) { PdbxAuditRevisionHistory history = (PdbxAuditRevisionHistory) buildObject( @@ -670,7 +670,7 @@ private void endLineChecks(String category,List loopFields, List triggerNewPdbxDatabaseStatus(status); - }else if ( category.equals("_database_PDB_remark")) { + }else if ( category.equals("_database_PDB_remark")){ DatabasePDBremark remark = (DatabasePDBremark) buildObject( DatabasePDBremark.class.getName(), loopFields, lineData, loopWarnings); @@ -1068,7 +1068,7 @@ public void triggerNewEntity(Entity entity){ c.newEntity(entity); } } - + public void triggerNewEntityPoly(EntityPoly entityPoly) { for(MMcifConsumer c : consumers){ c.newEntityPoly(entityPoly); diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestAtomCache.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestAtomCache.java index 10d42e8a5f..dedc91bd17 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestAtomCache.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestAtomCache.java @@ -46,7 +46,7 @@ public class TestAtomCache { private AtomCache cache; @Before - public void setUp() { + public void setUp() throws IOException { cache = new AtomCache(); // Delete files which were cached in previous tests diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestCalc.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestCalc.java index c834b22a61..31ae198a5a 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestCalc.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestCalc.java @@ -324,7 +324,7 @@ private static Matrix4d getSampleTransform(){ 0.0,0.0,0.0,1.0}); return sample; } - + private static Chain createDummyChain() { Group g = new AminoAcidImpl(); Atom a = getAtom("CA", 1, 1, 1); diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestDownloadChemCompProvider.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestDownloadChemCompProvider.java index c8b84b01c3..f1a9256cef 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/TestDownloadChemCompProvider.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/TestDownloadChemCompProvider.java @@ -21,6 +21,7 @@ package org.biojava.nbio.structure; import org.biojava.nbio.core.util.FlatFileCache; +import org.biojava.nbio.structure.io.LocalPDBDirectory; import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider; import org.biojava.nbio.structure.io.mmcif.model.ChemComp; import org.junit.Test; @@ -122,7 +123,7 @@ public void testIfWeCachedGarbageWeCanDetectIt() throws IOException { File file = new File(DownloadChemCompProvider.getLocalFileName("HEM")); PrintWriter pw = new PrintWriter(new GZIPOutputStream(new FileOutputStream(file))); - pw.println("A lot of garbage"); + pw.println("This must produce a compressed file of at least LocalPDBDirectory.MIN_PDB_FILE_SIZE bytes to avoid deletion."); pw.close(); DownloadChemCompProvider prov = new DownloadChemCompProvider(); 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 53c56f9a60..3b3c744adf 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 @@ -20,26 +20,52 @@ */ package org.biojava.nbio.structure.align.util; -import org.biojava.nbio.structure.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.zip.GZIPOutputStream; + +import org.biojava.nbio.core.util.FileDownloadUtils; +import org.biojava.nbio.structure.AtomPositionMap; +import org.biojava.nbio.structure.Chain; +import org.biojava.nbio.structure.Group; +import org.biojava.nbio.structure.ResidueRangeAndLength; +import org.biojava.nbio.structure.Structure; +import org.biojava.nbio.structure.StructureException; +import org.biojava.nbio.structure.StructureIO; +import org.biojava.nbio.structure.StructureIdentifier; +import org.biojava.nbio.structure.StructureTools; +import org.biojava.nbio.structure.SubstructureIdentifier; import org.biojava.nbio.structure.io.LocalPDBDirectory; import org.biojava.nbio.structure.io.LocalPDBDirectory.FetchBehavior; import org.biojava.nbio.structure.io.LocalPDBDirectory.ObsoleteBehavior; import org.biojava.nbio.structure.io.MMCIFFileReader; +import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory; +import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider; +import org.biojava.nbio.structure.io.mmcif.model.ChemComp; import org.biojava.nbio.structure.scop.ScopDatabase; import org.biojava.nbio.structure.scop.ScopFactory; +import org.biojava.nbio.structure.test.util.GlobalsHelper; import org.junit.After; import org.junit.Before; import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import static org.junit.Assert.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -49,22 +75,24 @@ */ public class AtomCacheTest { + private static Logger logger = LoggerFactory.getLogger(AtomCacheTest.class); private AtomCache cache; - private String previousPDB_DIR; @Before public void setUp() { - previousPDB_DIR = System.getProperty(UserConfiguration.PDB_DIR, null); + GlobalsHelper.pushState(); + cache = new AtomCache(); cache.setObsoleteBehavior(ObsoleteBehavior.FETCH_OBSOLETE); + StructureIO.setAtomCache(cache); + // Use a fixed SCOP version for stability ScopFactory.setScopDatabase(ScopFactory.VERSION_1_75B); } @After public void tearDown() { - if (previousPDB_DIR != null) - System.setProperty(UserConfiguration.PDB_DIR, previousPDB_DIR); + GlobalsHelper.restoreState(); } /** @@ -325,4 +353,125 @@ public void testSeqRes() throws StructureException, IOException { } + /** + * Test for #703 - Chemical component cache poisoning + * + * Handle empty CIF files + * @throws IOException + * @throws StructureException + */ + @Test + public void testEmptyChemComp() throws IOException, StructureException { + Path tmpCache = Paths.get(System.getProperty("java.io.tmpdir"),"BIOJAVA_TEST_CACHE"); + logger.info("Testing AtomCache at {}", tmpCache.toString()); + System.setProperty(UserConfiguration.PDB_DIR, tmpCache.toString()); + System.setProperty(UserConfiguration.PDB_CACHE_DIR, tmpCache.toString()); + + FileDownloadUtils.deleteDirectory(tmpCache); + Files.createDirectory(tmpCache); + try { + cache.setPath(tmpCache.toString()); + cache.setCachePath(tmpCache.toString()); + cache.setUseMmCif(true); + ChemCompGroupFactory.setChemCompProvider(new DownloadChemCompProvider(tmpCache.toString())); + + // Create an empty chemcomp + Path chemCompCif = tmpCache.resolve(Paths.get("chemcomp", "ATP.cif.gz")); + Files.createDirectories(chemCompCif.getParent()); + Files.createFile(chemCompCif); + assertTrue(Files.exists(chemCompCif)); + assertEquals(0, Files.size(chemCompCif)); + + // Copy stub file into place + Path testCif = tmpCache.resolve(Paths.get("data", "structures", "divided", "mmCIF", "ab","1abc.cif.gz")); + Files.createDirectories(testCif.getParent()); + URL resource = AtomCacheTest.class.getResource("/atp.cif.gz"); + File src = new File(resource.getPath()); + FileDownloadUtils.copy(src, testCif.toFile()); + + // Load structure + Structure s = cache.getStructure("1ABC"); + + // Should have re-downloaded the file + assertTrue(Files.size(chemCompCif) > LocalPDBDirectory.MIN_PDB_FILE_SIZE); + + // Structure should have valid ChemComp now + assertNotNull(s); + + Group g = s.getChain("A").getAtomGroup(0); + assertTrue(g.getPDBName().equals("ATP")); + + // should be unknown + ChemComp chem = g.getChemComp(); + assertNotNull(chem); + assertTrue(chem.getAtoms().size() > 0); + assertEquals("NON-POLYMER", chem.getType()); + } finally { + FileDownloadUtils.deleteDirectory(tmpCache); + } + } + + /** + * Test for #703 - Chemical component cache poisoning + * + * Handle empty CIF files + * @throws IOException + * @throws StructureException + */ + @Test + public void testEmptyGZChemComp() throws IOException, StructureException { + + Path tmpCache = Paths.get(System.getProperty("java.io.tmpdir"),"BIOJAVA_TEST_CACHE"); + logger.info("Testing AtomCache at {}", tmpCache.toString()); + System.setProperty(UserConfiguration.PDB_DIR, tmpCache.toString()); + System.setProperty(UserConfiguration.PDB_CACHE_DIR, tmpCache.toString()); + + FileDownloadUtils.deleteDirectory(tmpCache); + Files.createDirectory(tmpCache); + try { + cache.setPath(tmpCache.toString()); + cache.setCachePath(tmpCache.toString()); + cache.setUseMmCif(true); + ChemCompGroupFactory.setChemCompProvider(new DownloadChemCompProvider(tmpCache.toString())); + + + // Create an empty chemcomp + Path sub = tmpCache.resolve(Paths.get("chemcomp", "ATP.cif.gz")); + Files.createDirectories(sub.getParent()); + try(GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(sub.toFile()))) { + // don't write anything + out.flush(); + } + assertTrue(Files.exists(sub)); + assertTrue(0 < Files.size(sub) && Files.size(sub) < LocalPDBDirectory.MIN_PDB_FILE_SIZE); + + // Copy stub file into place + Path testCif = tmpCache.resolve(Paths.get("data", "structures", "divided", "mmCIF", "ab","1abc.cif.gz")); + Files.createDirectories(testCif.getParent()); + URL resource = AtomCacheTest.class.getResource("/atp.cif.gz"); + File src = new File(resource.getPath()); + FileDownloadUtils.copy(src, testCif.toFile()); + + // Load structure + Structure s = cache.getStructure("1ABC"); + + // Should have re-downloaded the file + assertTrue(Files.size(sub) > LocalPDBDirectory.MIN_PDB_FILE_SIZE); + + // Structure should have valid ChemComp + assertNotNull(s); + + Group g = s.getChain("A").getAtomGroup(0); + assertTrue(g.getPDBName().equals("ATP")); + + // should be unknown + ChemComp chem = g.getChemComp(); + assertNotNull(chem); + assertTrue(chem.getAtoms().size() > 0); + assertEquals("NON-POLYMER", chem.getType()); + } finally { + FileDownloadUtils.deleteDirectory(tmpCache); + } + } + } diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestQuaternaryStructureProviders.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestQuaternaryStructureProviders.java index 3209175eac..18aa44476c 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestQuaternaryStructureProviders.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestQuaternaryStructureProviders.java @@ -135,13 +135,13 @@ private void comparePdbVsMmcif(String pdbId, int bioMolecule, int mmSize) throws assertTrue(pMap.keySet().size()<= mMap.keySet().size()); - + assertEquals(mmSize, mMap.get(bioMolecule).getMacromolecularSize()); for ( int k : pMap.keySet()) { assertTrue(mMap.containsKey(k)); - + BioAssemblyInfo pBioAssemb = pMap.get(k); BioAssemblyInfo mBioAssemb = mMap.get(k); diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestShortLines.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestShortLines.java index 48f16397fe..3626452918 100644 --- a/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestShortLines.java +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/io/TestShortLines.java @@ -63,8 +63,8 @@ public void testConect() throws IOException { // After 4.2, CONECTS are deprecated, but there is not yet an implementation // describing how CONECTS will be replaced - will Bonds be created? - // assertEquals(1, s.getConnections().size()); - assertNotNull(s); + //assertEquals(1, s.getConnections().size()); + assertNotNull(s); } @Test diff --git a/biojava-structure/src/test/java/org/biojava/nbio/structure/test/util/GlobalsHelper.java b/biojava-structure/src/test/java/org/biojava/nbio/structure/test/util/GlobalsHelper.java new file mode 100644 index 0000000000..c756d5fd8a --- /dev/null +++ b/biojava-structure/src/test/java/org/biojava/nbio/structure/test/util/GlobalsHelper.java @@ -0,0 +1,124 @@ +package org.biojava.nbio.structure.test.util; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; + +import org.biojava.nbio.structure.StructureIO; +import org.biojava.nbio.structure.align.util.AtomCache; +import org.biojava.nbio.structure.align.util.UserConfiguration; +import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory; +import org.biojava.nbio.structure.io.mmcif.ChemCompProvider; +import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider; +import org.biojava.nbio.structure.scop.ScopDatabase; +import org.biojava.nbio.structure.scop.ScopFactory; + +/** + * Helper class to manage all the global state changes in BioJava. + * For instance, this should be used in tests before modifying PDB_PATH. + * + * Used by tests during setup and teardown to ensure a clean environment + * + * This class is a singleton. + * @author Spencer Bliven + * + */ +public final class GlobalsHelper { + + private static class PathInfo { + public final String pdbPath; + public final String pdbCachePath; + public final AtomCache atomCache; + public final ChemCompProvider chemCompProvider; + public final String downloadChemCompProviderPath; + public final ScopDatabase scop; + + public PathInfo() { + pdbPath = System.getProperty(UserConfiguration.PDB_DIR, null); + pdbCachePath = System.getProperty(UserConfiguration.PDB_CACHE_DIR, null); + atomCache = StructureIO.getAtomCache(); + chemCompProvider = ChemCompGroupFactory.getChemCompProvider(); + downloadChemCompProviderPath = DownloadChemCompProvider.getPath().getPath(); + scop = ScopFactory.getSCOP(); + } + } + + // Saves defaults as stack + private static Deque stack = new LinkedList<>(); + static { + // Save default state + pushState(); + } + + /** + * GlobalsHelper should not be instantiated. + */ + private GlobalsHelper() {} + + /** + * Save current global state to the stack + */ + public static void pushState() { + PathInfo paths = new PathInfo(); + stack.addFirst(paths); + } + + /** + * Sets a new PDB_PATH and PDB_CACHE_PATH consistently. + * + * Previous values can be restored with {@link #restoreState()}. + * @param path + */ + public static void setPdbPath(String path, String cachePath) { + pushState(); + if(path == null || cachePath == null) { + UserConfiguration config = new UserConfiguration(); + if(path == null) { + path = config.getPdbFilePath(); + } + if(cachePath == null) { + cachePath = config.getCacheFilePath(); + } + } + System.setProperty(UserConfiguration.PDB_DIR, path); + System.setProperty(UserConfiguration.PDB_CACHE_DIR, path); + + AtomCache cache = new AtomCache(path); + StructureIO.setAtomCache(cache); + + // Note side effect setting the path for all DownloadChemCompProvider due to static state + ChemCompProvider provider = new DownloadChemCompProvider(path); + ChemCompGroupFactory.setChemCompProvider(provider); + } + + /** + * Restore global state to the previous settings + * @throws NoSuchElementException if there is no prior state to restore + */ + public static void restoreState() { + PathInfo paths = stack.removeFirst(); + + if(paths.pdbPath == null) { + System.clearProperty(UserConfiguration.PDB_DIR); + } else { + System.setProperty(UserConfiguration.PDB_DIR, paths.pdbPath); + } + if(paths.pdbCachePath == null) { + System.clearProperty(UserConfiguration.PDB_CACHE_DIR); + } else { + System.setProperty(UserConfiguration.PDB_CACHE_DIR, paths.pdbCachePath); + } + + StructureIO.setAtomCache(paths.atomCache); + + // Use side effect setting the path for all DownloadChemCompProvider due to static state + new DownloadChemCompProvider(paths.downloadChemCompProviderPath); + + ChemCompGroupFactory.setChemCompProvider(paths.chemCompProvider); + + ScopFactory.setScopDatabase(paths.scop); + } + + +} diff --git a/biojava-structure/src/test/resources/atp.cif.gz b/biojava-structure/src/test/resources/atp.cif.gz new file mode 100644 index 0000000000..7167313a22 Binary files /dev/null and b/biojava-structure/src/test/resources/atp.cif.gz differ diff --git a/pom.xml b/pom.xml index 77c02f74e7..3c90c668c8 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ Andy Yates - + Anthony Bradley @@ -321,6 +321,12 @@ 3.7.1 + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + @@ -680,8 +686,8 @@ Github https://github.com/biojava/biojava/issues - + Travis https://travis-ci.org/biojava/biojava - +