From: BobHanson Date: Wed, 3 Jun 2020 20:47:30 +0000 (-0500) Subject: JAL-3446 adds SIFTs JAL-3625 SiftsClient X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=775fa10f6afa3da6dd105e9528a7039ac1d9089f;p=jalview.git JAL-3446 adds SIFTs JAL-3625 SiftsClient --- diff --git a/src/jalview/util/Platform.java b/src/jalview/util/Platform.java index c06c755..cd24432 100644 --- a/src/jalview/util/Platform.java +++ b/src/jalview/util/Platform.java @@ -20,8 +20,6 @@ */ package jalview.util; -import jalview.javascript.json.JSON; - import java.awt.Component; import java.awt.Dimension; import java.awt.Toolkit; @@ -35,6 +33,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Date; import java.util.Properties; import java.util.logging.ConsoleHandler; import java.util.logging.Level; @@ -47,6 +49,7 @@ import org.json.simple.parser.ParseException; import com.stevesoft.pat.Regex; +import jalview.javascript.json.JSON; import swingjs.api.JSUtilI; /** @@ -71,6 +74,8 @@ public class Platform if (isJS) { try { + // this is ok - it's a highly embedded method in Java; the deprecation is + // really a recommended best practice. jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance()); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) @@ -833,5 +838,72 @@ public class Platform return (isJS ? jsutil.getCodeBase() : null); } + public static String getUserPath(String subpath) + { + char sep = File.separatorChar; + return System.getProperty("user.home") + sep + subpath.replace('/', sep); + } + + /** + * This method enables checking if a cached file has exceeded a certain + * threshold(in days) + * + * @param file + * the cached file + * @param noOfDays + * the threshold in days + * @return + */ + public static boolean isFileOlderThanThreshold(File file, int noOfDays) + { + if (isJS()) { + // not meaningful in SwingJS -- this is a session-specific temp file. It doesn't have a timestamp. + return false; + } + Path filePath = file.toPath(); + BasicFileAttributes attr; + int diffInDays = 0; + try + { + attr = Files.readAttributes(filePath, BasicFileAttributes.class); + diffInDays = (int) ((new Date().getTime() + - attr.lastModifiedTime().toMillis()) + / (1000 * 60 * 60 * 24)); + // System.out.println("Diff in days : " + diffInDays); + } catch (IOException e) + { + e.printStackTrace(); + } + return noOfDays <= diffInDays; + } + + /** + * Get the leading integer part of a string that begins with an integer. + * + * @param input + * - the string input to process + * @param failValue + * - value returned if unsuccessful + * @return + */ + public static int getLeadingIntegerValue(String input, int failValue) + { + if (input == null) + { + return failValue; + } + if (isJS) { + int val = /** @j2sNative 1 ? parseInt(input) : */ 0; + return (val == val + 0 ? val : failValue); + } + // JavaScript does not support Regex ? lookahead + String[] parts = input.split("(?=\\D)(?<=\\d)"); + if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+")) + { + return Integer.valueOf(parts[0]); + } + return failValue; + } + } diff --git a/src/jalview/ws/sifts/SiftsClient.java b/src/jalview/ws/sifts/SiftsClient.java index 4fb9ca9..3ec6320 100644 --- a/src/jalview/ws/sifts/SiftsClient.java +++ b/src/jalview/ws/sifts/SiftsClient.java @@ -22,20 +22,15 @@ package jalview.ws.sifts; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; import java.net.URLConnection; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -101,8 +96,6 @@ public class SiftsClient implements SiftsClientI */ private jalview.datamodel.Mapping seqFromPdbMapping; - private static final int BUFFER_SIZE = 4096; - public static final int UNASSIGNED = Integer.MIN_VALUE; private static final int PDB_RES_POS = 0; @@ -117,10 +110,15 @@ public class SiftsClient implements SiftsClientI private final static String NEWLINE = System.lineSeparator(); + private static final boolean GET_STREAM = false; + private static final boolean CACHE_FILE = true; + private String curSourceDBRef; private HashSet curDBRefAccessionIdsString; + private boolean doCache = false; + private enum CoordinateSys { UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe"); @@ -165,8 +163,31 @@ public class SiftsClient implements SiftsClientI { this.pdb = pdb; this.pdbId = pdb.getId(); - File siftsFile = getSiftsFile(pdbId); - siftsEntry = parseSIFTs(siftsFile); + if (doCache) { + File siftsFile = getSiftsFile(pdbId); + siftsEntry = parseSIFTs(siftsFile); + } else { + siftsEntry = parseSIFTSStreamFor(pdbId); + } + } + + /** + * A more streamlined version of SIFT reading that allows for streaming of the data. + * + * @param pdbId + * @return + * @throws SiftsException + */ + private static Entry parseSIFTSStreamFor(String pdbId) throws SiftsException + { + try + { + InputStream is = (InputStream) downloadSifts(pdbId, GET_STREAM); + return parseSIFTs(is); + } catch (Exception e) + { + throw new SiftsException(e.getMessage()); + } } /** @@ -180,8 +201,17 @@ public class SiftsClient implements SiftsClientI */ private Entry parseSIFTs(File siftFile) throws SiftsException { - try (InputStream in = new FileInputStream(siftFile); - GZIPInputStream gzis = new GZIPInputStream(in);) + try (InputStream in = new FileInputStream(siftFile)) { + return parseSIFTs(in); + } catch (Exception e) + { + e.printStackTrace(); + throw new SiftsException(e.getMessage()); + } + } + + private static Entry parseSIFTs(InputStream in) throws Exception { + try (GZIPInputStream gzis = new GZIPInputStream(in);) { // System.out.println("File : " + siftFile.getAbsolutePath()); JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.sifts"); @@ -190,10 +220,6 @@ public class SiftsClient implements SiftsClientI Unmarshaller um = jc.createUnmarshaller(); JAXBElement jbe = um.unmarshal(streamReader, Entry.class); return jbe.getValue(); - } catch (Exception e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); } } @@ -223,14 +249,14 @@ public class SiftsClient implements SiftsClientI // The line below is required for unit testing... don't comment it out!!! System.out.println(">>> SIFTS File already downloaded for " + pdbId); - if (isFileOlderThanThreshold(siftsFile, + if (Platform.isFileOlderThanThreshold(siftsFile, SiftsSettings.getCacheThresholdInDays())) { File oldSiftsFile = new File(siftsFileName + "_old"); siftsFile.renameTo(oldSiftsFile); try { - siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + siftsFile = downloadSiftsFile(pdbId); oldSiftsFile.delete(); return siftsFile; } catch (IOException e) @@ -247,7 +273,7 @@ public class SiftsClient implements SiftsClientI } try { - siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + siftsFile = downloadSiftsFile(pdbId); } catch (IOException e) { throw new SiftsException(e.getMessage()); @@ -256,35 +282,6 @@ public class SiftsClient implements SiftsClientI } /** - * This method enables checking if a cached file has exceeded a certain - * threshold(in days) - * - * @param file - * the cached file - * @param noOfDays - * the threshold in days - * @return - */ - public static boolean isFileOlderThanThreshold(File file, int noOfDays) - { - Path filePath = file.toPath(); - BasicFileAttributes attr; - int diffInDays = 0; - try - { - attr = Files.readAttributes(filePath, BasicFileAttributes.class); - diffInDays = (int) ((new Date().getTime() - - attr.lastModifiedTime().toMillis()) - / (1000 * 60 * 60 * 24)); - // System.out.println("Diff in days : " + diffInDays); - } catch (IOException e) - { - e.printStackTrace(); - } - return noOfDays <= diffInDays; - } - - /** * Download a SIFTs XML file for a given PDB Id from an FTP repository * * @param pdbId @@ -294,53 +291,47 @@ public class SiftsClient implements SiftsClientI */ public static File downloadSiftsFile(String pdbId) throws SiftsException, IOException + { + return (File) downloadSifts(pdbId, CACHE_FILE); + } + + /** + * Download SIFTs XML with the option to cache a file or to get a stream. + * + * @param pdbId + * @param asFile + * @return + * @throws IOException + */ + private static Object downloadSifts(String pdbId, boolean asFile) throws IOException { + pdbId = pdbId.toLowerCase(); if (pdbId.contains(".cif")) { pdbId = pdbId.replace(".cif", ""); } String siftFile = pdbId + ".xml.gz"; - String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile; - - /* - * Download the file from URL to either - * Java: directory of cached downloaded SIFTS files - * Javascript: temporary 'file' (in-memory cache) - */ + File downloadTo = null; - if (Platform.isJS()) - { - downloadTo = File.createTempFile(siftFile, ".xml.gz"); - } - else + if (asFile) { downloadTo = new File( SiftsSettings.getSiftDownloadDirectory() + siftFile); - File siftsDownloadDir = new File( - SiftsSettings.getSiftDownloadDirectory()); + File siftsDownloadDir = new File(SiftsSettings.getSiftDownloadDirectory()); if (!siftsDownloadDir.exists()) { siftsDownloadDir.mkdirs(); } } - - // System.out.println(">> Download ftp url : " + siftsFileFTPURL); - // long now = System.currentTimeMillis(); + String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile; URL url = new URL(siftsFileFTPURL); URLConnection conn = url.openConnection(); - InputStream inputStream = conn.getInputStream(); - FileOutputStream outputStream = new FileOutputStream( - downloadTo); - byte[] buffer = new byte[BUFFER_SIZE]; - int bytesRead = -1; - while ((bytesRead = inputStream.read(buffer)) != -1) - { - outputStream.write(buffer, 0, bytesRead); - } - outputStream.close(); - inputStream.close(); - // System.out.println(">>> File downloaded : " + downloadedSiftsFile - // + " took " + (System.currentTimeMillis() - now) + "ms"); + InputStream is = conn.getInputStream(); + if (!asFile) + return is; + // This is MUCH more efficent in JavaScript, as we already have the bytes + Platform.streamToFile(is, downloadTo); + is.close(); return downloadTo; } @@ -648,7 +639,7 @@ public class SiftsClient implements SiftsClientI for (Residue residue : residues) { boolean isObserved = isResidueObserved(residue); - int pdbeIndex = getLeadingIntegerValue(residue.getDbResNum(), + int pdbeIndex = Platform.getLeadingIntegerValue(residue.getDbResNum(), UNASSIGNED); int currSeqIndex = UNASSIGNED; List cRefDbs = residue.getCrossRefDb(); @@ -660,7 +651,7 @@ public class SiftsClient implements SiftsClientI pdbRefDb = cRefDb; if (firstPDBResNum == UNASSIGNED) { - firstPDBResNum = getLeadingIntegerValue(cRefDb.getDbResNum(), + firstPDBResNum = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(), UNASSIGNED); } else @@ -675,7 +666,7 @@ public class SiftsClient implements SiftsClientI if (cRefDb.getDbCoordSys().equalsIgnoreCase(seqCoordSys.getName()) && isAccessionMatched(cRefDb.getDbAccessionId())) { - currSeqIndex = getLeadingIntegerValue(cRefDb.getDbResNum(), + currSeqIndex = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(), UNASSIGNED); if (pdbRefDb != null) { @@ -725,9 +716,9 @@ public class SiftsClient implements SiftsClientI { int resNum = (pdbRefDb == null) - ? getLeadingIntegerValue(residue.getDbResNum(), + ? Platform.getLeadingIntegerValue(residue.getDbResNum(), UNASSIGNED) - : getLeadingIntegerValue(pdbRefDb.getDbResNum(), + : Platform.getLeadingIntegerValue(pdbRefDb.getDbResNum(), UNASSIGNED); if (isObserved) @@ -748,29 +739,6 @@ public class SiftsClient implements SiftsClientI } /** - * Get the leading integer part of a string that begins with an integer. - * - * @param input - * - the string input to process - * @param failValue - * - value returned if unsuccessful - * @return - */ - static int getLeadingIntegerValue(String input, int failValue) - { - if (input == null) - { - return failValue; - } - String[] parts = input.split("(?=\\D)(?<=\\d)"); - if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+")) - { - return Integer.valueOf(parts[0]); - } - return failValue; - } - - /** * * @param chainId * Target chain to populate mapping of its atom positions. diff --git a/src/jalview/ws/sifts/SiftsSettings.java b/src/jalview/ws/sifts/SiftsSettings.java index a042bb9..029f48d 100644 --- a/src/jalview/ws/sifts/SiftsSettings.java +++ b/src/jalview/ws/sifts/SiftsSettings.java @@ -22,6 +22,7 @@ package jalview.ws.sifts; import jalview.bin.ApplicationSingletonProvider; import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI; +import jalview.util.Platform; import java.util.Objects; diff --git a/test/jalview/ws/sifts/SiftsClientTest.java b/test/jalview/ws/sifts/SiftsClientTest.java index f661bde..4540bd8 100644 --- a/test/jalview/ws/sifts/SiftsClientTest.java +++ b/test/jalview/ws/sifts/SiftsClientTest.java @@ -32,6 +32,7 @@ import jalview.datamodel.SequenceI; import jalview.gui.JvOptionPane; import jalview.io.DataSourceType; import jalview.structure.StructureMapping; +import jalview.util.Platform; import jalview.xml.binding.sifts.Entry.Entity; import java.io.File; @@ -518,17 +519,17 @@ groups = { "Network" }, public void getLeadingIntegerFromString() { Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("1234abcd", -1), 1234); + Platform.getLeadingIntegerValue("1234abcd", -1), 1234); Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("1234", -1), + Platform.getLeadingIntegerValue("1234", -1), 1234); Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("abcd", -1), -1); + Platform.getLeadingIntegerValue("abcd", -1), -1); Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("abcd1234", -1), -1); + Platform.getLeadingIntegerValue("abcd1234", -1), -1); Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("None", -1), -1); + Platform.getLeadingIntegerValue("None", -1), -1); Assert.assertEquals( - SiftsClient.getLeadingIntegerValue("Null", -1), -1); + Platform.getLeadingIntegerValue("Null", -1), -1); } }