JAL-3446 adds SIFTs JAL-3625 SiftsClient
authorBobHanson <hansonr@stolaf.edu>
Wed, 3 Jun 2020 20:47:30 +0000 (15:47 -0500)
committerBobHanson <hansonr@stolaf.edu>
Wed, 3 Jun 2020 20:48:39 +0000 (15:48 -0500)
src/jalview/util/Platform.java
src/jalview/ws/sifts/SiftsClient.java
src/jalview/ws/sifts/SiftsSettings.java
test/jalview/ws/sifts/SiftsClientTest.java

index c06c755..cd24432 100644 (file)
@@ -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;
+  }
+
 
 }
index 4fb9ca9..3ec6320 100644 (file)
@@ -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<String> 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<Entry> 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<CrossRefDb> 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.
index a042bb9..029f48d 100644 (file)
@@ -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;
 
index f661bde..4540bd8 100644 (file)
@@ -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);
   }
 }