Merge branch 'master' of https://source.jalview.org/git/jalviewjs.git
[jalviewjs.git] / src / javajs / util / Rdr.java
index 977bfe2..5296964 100644 (file)
-/* $RCSfile$\r
- * $Author: hansonr $\r
- * $Date: 2007-04-05 09:07:28 -0500 (Thu, 05 Apr 2007) $\r
- * $Revision: 7326 $\r
- *\r
- * Copyright (C) 2003-2005  The Jmol Development Team\r
- *\r
- * Contact: jmol-developers@lists.sf.net\r
- *\r
- *  This library is free software; you can redistribute it and/or\r
- *  modify it under the terms of the GNU Lesser General Public\r
- *  License as published by the Free Software Foundation; either\r
- *  version 2.1 of the License, or (at your option) any later version.\r
- *\r
- *  This library is distributed in the hope that it will be useful,\r
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- *  Lesser General Public License for more details.\r
- *\r
- *  You should have received a copy of the GNU Lesser General Public\r
- *  License along with this library; if not, write to the Free Software\r
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r
- */\r
-package javajs.util;\r
-\r
-import java.io.BufferedInputStream;\r
-import java.io.BufferedReader;\r
-import java.io.ByteArrayInputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.InputStreamReader;\r
-import java.io.StringReader;\r
-import java.io.UnsupportedEncodingException;\r
-\r
-import java.util.Map;\r
-\r
-\r
-import javajs.api.GenericCifDataParser;\r
-import javajs.api.GenericLineReader;\r
-import javajs.api.GenericZipTools;\r
-\r
-/**\r
- * A general helper class for a variety of stream and reader functionality\r
- * including:\r
- * \r
- *  stream and byte magic-number decoding for PNG, PNGJ, ZIP, and GZIP streams\r
- *  \r
- *  various stream/reader methods, including UTF-encoded stream reading\r
- *  \r
- *  reflection-protected access to a CIF parser and ZIP tools\r
- *  \r
- *   \r
- * \r
- * \r
- */\r
-public class Rdr implements GenericLineReader {\r
-\r
-  BufferedReader reader;\r
-\r
-  public Rdr(BufferedReader reader) {\r
-    this.reader = reader;\r
-  }\r
-  \r
-  @Override\r
-  public String readNextLine() throws Exception {\r
-    return reader.readLine();\r
-  }\r
-\r
-  public static Map<String, Object> readCifData(GenericCifDataParser parser, BufferedReader br) {\r
-    return parser.set(null, br).getAllCifData();\r
-  }\r
-  \r
-  \r
-  ///////////\r
-  \r
-  public static String fixUTF(byte[] bytes) {    \r
-    Encoding encoding = getUTFEncoding(bytes);\r
-    if (encoding != Encoding.NONE)\r
-    try {\r
-      String s = new String(bytes, encoding.name().replace('_', '-'));\r
-      switch (encoding) {\r
-      case UTF8:\r
-      case UTF_16BE:\r
-      case UTF_16LE:\r
-        // extra byte at beginning removed\r
-        s = s.substring(1);\r
-        break;\r
-      default:\r
-        break;        \r
-      }\r
-      return s;\r
-    } catch (UnsupportedEncodingException e) {\r
-      System.out.println(e);\r
-    }\r
-    return new String(bytes);\r
-  }\r
-\r
-  private static Encoding getUTFEncoding(byte[] bytes) {\r
-    if (bytes.length >= 3 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF)\r
-      return Encoding.UTF8;\r
-    if (bytes.length >= 4 && bytes[0] == (byte) 0 && bytes[1] == (byte) 0 \r
-        && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF)\r
-      return Encoding.UTF_32BE;\r
-    if (bytes.length >= 4 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE \r
-        && bytes[2] == (byte) 0 && bytes[3] == (byte) 0)\r
-      return Encoding.UTF_32LE;\r
-    if (bytes.length >= 2 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE)\r
-      return Encoding.UTF_16LE;\r
-    if (bytes.length >= 2 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF)\r
-      return Encoding.UTF_16BE;\r
-    return Encoding.NONE;\r
-  \r
-  }\r
-  \r
-  ////////// stream type checking //////////\r
-  \r
-\r
-  private static Encoding getUTFEncodingForStream(BufferedInputStream is) throws IOException {\r
-    /**\r
-     * @j2sNative\r
-     * \r
-     *  is.resetStream();\r
-     * \r
-     */\r
-    {\r
-    }\r
-    byte[] abMagic = new byte[4];\r
-    abMagic[3] = 1;\r
-    try{\r
-    is.mark(5);\r
-    } catch (Exception e) {\r
-      return Encoding.NONE;\r
-    }\r
-    is.read(abMagic, 0, 4);\r
-    is.reset();\r
-    return getUTFEncoding(abMagic);\r
-  }\r
-\r
-  public static boolean isBase64(SB sb) {\r
-    return (sb.indexOf(";base64,") == 0);\r
-  }\r
-\r
-  public static boolean isCompoundDocumentS(InputStream is) {\r
-    return isCompoundDocumentB(getMagic(is, 8));\r
-  }\r
-\r
-  public static boolean isCompoundDocumentB(byte[] bytes) {\r
-    return (bytes.length >= 8 && bytes[0] == (byte) 0xD0\r
-        && bytes[1] == (byte) 0xCF && bytes[2] == (byte) 0x11\r
-        && bytes[3] == (byte) 0xE0 && bytes[4] == (byte) 0xA1\r
-        && bytes[5] == (byte) 0xB1 && bytes[6] == (byte) 0x1A \r
-        && bytes[7] == (byte) 0xE1);\r
-  }\r
-\r
-  public static boolean isGzipS(InputStream is) {\r
-    return isGzipB(getMagic(is, 2));\r
-  }\r
-\r
-  public static boolean isGzipB(byte[] bytes) {    \r
-      return (bytes != null && bytes.length >= 2 \r
-          && bytes[0] == (byte) 0x1F && bytes[1] == (byte) 0x8B);\r
-  }\r
-\r
-  public static boolean isPickleS(InputStream is) {\r
-    return Rdr.isPickleB(getMagic(is, 2));\r
-  }\r
-\r
-  public static boolean isPickleB(byte[] bytes) {    \r
-      return (bytes != null && bytes.length >= 2 \r
-          && bytes[0] == (byte) 0x7D && bytes[1] == (byte) 0x71);\r
-  }\r
-\r
-  public static boolean isPngZipStream(InputStream is) {\r
-    return isPngZipB(getMagic(is, 55));\r
-  }\r
-\r
-  public static boolean isPngZipB(byte[] bytes) {\r
-    // \0PNGJ starting at byte 50\r
-    return (bytes[50] == 0 && bytes[51] == 0x50 && bytes[52] == 0x4E && bytes[53] == 0x47 && bytes[54] == 0x4A);\r
-  }\r
-\r
-  public static boolean isZipS(InputStream is) {\r
-    return isZipB(getMagic(is, 4));\r
-  }\r
-\r
-  public static boolean isZipB(byte[] bytes) {\r
-    return (bytes.length >= 4 \r
-        && bytes[0] == 0x50  //PK<03><04> \r
-        && bytes[1] == 0x4B\r
-        && bytes[2] == 0x03 \r
-        && bytes[3] == 0x04);\r
-  }\r
-\r
-  private static byte[] getMagic(InputStream is, int n) {\r
-    byte[] abMagic = new byte[n];\r
-    /**\r
-     * @j2sNative\r
-     * \r
-     * is.resetStream();\r
-     * \r
-     */\r
-    {\r
-    }\r
-    try {\r
-      is.mark(n + 1);\r
-      is.read(abMagic, 0, n);\r
-    } catch (IOException e) {\r
-    }\r
-    try {\r
-      is.reset();\r
-    } catch (IOException e) {\r
-    }\r
-    return abMagic;\r
-  }\r
-\r
-  public static String guessMimeTypeForBytes(byte[] bytes) {\r
-     // only options here are JPEG, PNG, GIF, and BMP\r
-    switch (bytes.length < 2 ? -1 : bytes[1]) {\r
-    case 0:\r
-      return "image/jpg"; // 0xFF 0x00 ...\r
-    case 0x49:\r
-      return "image/gif"; // GIF89a...\r
-    case 0x4D:\r
-      return "image/BMP"; // BM...\r
-    case 0x50:\r
-      return "image/png";\r
-    default:\r
-      return  "image/unknown";\r
-    }\r
-  }\r
-\r
-\r
-  ////////// stream/byte methods ///////////\r
-  \r
-  public static BufferedInputStream getBIS(byte[] bytes) {\r
-    return new BufferedInputStream(new ByteArrayInputStream(bytes));\r
-  }\r
-\r
-  public static BufferedReader getBR(String string) {\r
-    return new BufferedReader(new StringReader(string));\r
-  }\r
-\r
-  /**\r
-   * Drill down into a GZIP stack until no more layers.\r
-   * @param jzt \r
-   * \r
-   * @param bis\r
-   * @return non-gzipped buffered input stream.\r
-   * \r
-   * @throws IOException\r
-   */\r
-  public static BufferedInputStream getUnzippedInputStream(GenericZipTools jzt, BufferedInputStream bis) throws IOException {\r
-    while (isGzipS(bis))\r
-      bis = new BufferedInputStream(jzt.newGZIPInputStream(bis));\r
-    return bis;\r
-  }\r
-\r
-  /**\r
-   * Allow for base64-encoding check.\r
-   * \r
-   * @param sb\r
-   * @return byte array\r
-   */\r
-  public static byte[] getBytesFromSB(SB sb) {\r
-    return (isBase64(sb) ? Base64.decodeBase64(sb.substring(8)) : sb.toBytes(0, -1));    \r
-  }\r
-\r
- /**\r
-   * Read a an entire BufferedInputStream for its bytes, and \r
-   * either return them or leave them in the designated output channel.\r
-   *  \r
-   * @param bis\r
-   * @param out a destination output channel, or null\r
-   * @return byte[] (if out is null) or a message indicating length (if not)\r
-   * \r
-   * @throws IOException\r
-   */\r
-  public static Object getStreamAsBytes(BufferedInputStream bis,\r
-                                         OC out) throws IOException {\r
-    byte[] buf = new byte[1024];\r
-    byte[] bytes = (out == null ? new byte[4096] : null);\r
-    int len = 0;\r
-    int totalLen = 0;\r
-    while ((len = bis.read(buf, 0, 1024)) > 0) {\r
-      totalLen += len;\r
-      if (out == null) {\r
-        if (totalLen >= bytes.length)\r
-          bytes = AU.ensureLengthByte(bytes, totalLen * 2);\r
-        System.arraycopy(buf, 0, bytes, totalLen - len, len);\r
-      } else {\r
-        out.write(buf, 0, len);\r
-      }\r
-    }\r
-    bis.close();\r
-    if (out == null) {\r
-      return AU.arrayCopyByte(bytes, totalLen);\r
-    }\r
-    return totalLen + " bytes";\r
-  }\r
-\r
-  /**\r
-   * Read an input stream fully, saving a byte array, then\r
-   * return a buffered reader to those bytes converted to string form.\r
-   * \r
-   * @param bis\r
-   * @param charSet\r
-   * @return Reader\r
-   * @throws IOException\r
-   */\r
-  public static BufferedReader getBufferedReader(BufferedInputStream bis, String charSet)\r
-      throws IOException {\r
-    // could also just make sure we have a buffered input stream here.\r
-    if (getUTFEncodingForStream(bis) == Encoding.NONE)\r
-      return new BufferedReader(new InputStreamReader(bis, (charSet == null ? "UTF-8" : charSet)));\r
-    byte[] bytes = getLimitedStreamBytes(bis, -1);\r
-    bis.close();\r
-    return getBR(charSet == null ? fixUTF(bytes) : new String(bytes, charSet));\r
-  }\r
-\r
-  /**\r
-   * Read a possibly limited number of bytes (when n > 0) from a stream, \r
-   * leaving the stream open.\r
-   * \r
-   * @param is an input stream, not necessarily buffered.\r
-   * @param n the maximum number of bytes to read, or -1 for all\r
-   * @return the bytes read\r
-   * \r
-   * @throws IOException\r
-   */\r
-  public static byte[] getLimitedStreamBytes(InputStream is, long n)\r
-      throws IOException {\r
-\r
-    //Note: You cannot use InputStream.available() to reliably read\r
-    //      zip data from the web. \r
-\r
-    int buflen = (n > 0 && n < 1024 ? (int) n : 1024);\r
-    byte[] buf = new byte[buflen];\r
-    byte[] bytes = new byte[n < 0 ? 4096 : (int) n];\r
-    int len = 0;\r
-    int totalLen = 0;\r
-    if (n < 0)\r
-      n = Integer.MAX_VALUE;\r
-    while (totalLen < n && (len = is.read(buf, 0, buflen)) > 0) {\r
-      totalLen += len;\r
-      if (totalLen > bytes.length)\r
-        bytes = AU.ensureLengthByte(bytes, totalLen * 2);\r
-      System.arraycopy(buf, 0, bytes, totalLen - len, len);\r
-      if (n != Integer.MAX_VALUE && totalLen + buflen > bytes.length)\r
-        buflen = bytes.length - totalLen;\r
-\r
-    }\r
-    if (totalLen == bytes.length)\r
-      return bytes;\r
-    buf = new byte[totalLen];\r
-    System.arraycopy(bytes, 0, buf, 0, totalLen);\r
-    return buf;\r
-  }\r
-\r
-  /**\r
-   * \r
-   * Read a UTF-8 stream fully, converting it to a String.\r
-   * Called by Jmol's XMLReaders\r
-   * \r
-   * @param bis\r
-   * @return a UTF-8 string\r
-   */\r
-  public static String StreamToUTF8String(BufferedInputStream bis) {\r
-    String[] data = new String[1];\r
-    try {\r
-      readAllAsString(getBufferedReader(bis, "UTF-8"), -1, true, data, 0);\r
-    } catch (IOException e) {\r
-    }\r
-    return data[0];\r
-  }\r
-\r
-  /**\r
-   * This method fills data[i] with string data from a file that may or may not\r
-   * be binary even though it is being read by a reader. It is meant to be used\r
-   * simple text-based files only.\r
-   *  \r
-   * @param br\r
-   * @param nBytesMax\r
-   * @param allowBinary\r
-   * @param data\r
-   * @param i\r
-   * @return true if data[i] holds the data; false if data[i] holds an error message. \r
-   */\r
-  public static boolean readAllAsString(BufferedReader br, int nBytesMax, boolean allowBinary, String[] data, int i) {\r
-    try {\r
-      SB sb = SB.newN(8192);\r
-      String line;\r
-      if (nBytesMax < 0) {\r
-        line = br.readLine();\r
-        if (allowBinary || line != null && line.indexOf('\0') < 0\r
-            && (line.length() != 4 || line.charAt(0) != 65533\r
-            || line.indexOf("PNG") != 1)) {\r
-          sb.append(line).appendC('\n');\r
-          while ((line = br.readLine()) != null)\r
-            sb.append(line).appendC('\n');\r
-        }\r
-      } else {\r
-        int n = 0;\r
-        int len;\r
-        while (n < nBytesMax && (line = br.readLine()) != null) {\r
-          if (nBytesMax - n < (len = line.length()) + 1)\r
-            line = line.substring(0, nBytesMax - n - 1);\r
-          sb.append(line).appendC('\n');\r
-          n += len + 1;\r
-        }\r
-      }\r
-      br.close();\r
-      data[i] = sb.toString();\r
-      return true;\r
-    } catch (Exception ioe) {\r
-      data[i] = ioe.toString();\r
-      return false;\r
-    }\r
-  }\r
-\r
-  \r
-  /////////// PNGJ support /////////////\r
-  \r
-  \r
-  /**\r
-   * Look at byte 50 for "\0PNGJxxxxxxxxx+yyyyyyyyy" where xxxxxxxxx is a byte\r
-   * offset to the JMOL data and yyyyyyyyy is the length of the data.\r
-   * \r
-   * @param bis\r
-   * @return same stream or byte stream\r
-   */\r
-\r
-  /**\r
-   * Retrieve the two numbers in a PNG iTXt tag indicating the \r
-   * file pointer for the start of the ZIP data as well as its length.\r
-   * \r
-   * @param bis\r
-   * @param pt_count\r
-   */\r
-  static void getPngZipPointAndCount(BufferedInputStream bis, int[] pt_count) {\r
-    bis.mark(75);\r
-    try {\r
-      byte[] data = getLimitedStreamBytes(bis, 74);\r
-      bis.reset();\r
-      int pt = 0;\r
-      for (int i = 64, f = 1; --i > 54; f *= 10)\r
-        pt += (data[i] - '0') * f;\r
-      int n = 0;\r
-      for (int i = 74, f = 1; --i > 64; f *= 10)\r
-        n += (data[i] - '0') * f;\r
-      pt_count[0] = pt;\r
-      pt_count[1] = n;\r
-    } catch (Throwable e) {\r
-      pt_count[1] = 0;\r
-    }\r
-  }\r
-\r
-  /**\r
-   * Either advance a PNGJ stream to its zip file data or pull out the ZIP data\r
-   * bytes and create a new stream for them from which a ZIP utility can start\r
-   * extracting files.\r
-   * \r
-   * @param bis\r
-   * @param asNewStream  \r
-   * @return new buffered ByteArrayInputStream, possibly with no data if there is an error\r
-   */\r
-  public static BufferedInputStream getPngZipStream(BufferedInputStream bis, boolean asNewStream) {\r
-    if (!isPngZipStream(bis))\r
-      return bis;\r
-    byte[] data = new byte[0];\r
-    bis.mark(75);\r
-    try {\r
-      int pt_count[] = new int[2];\r
-      getPngZipPointAndCount(bis, pt_count);\r
-      if (pt_count[1] != 0) {\r
-        int pt = pt_count[0];\r
-        while (pt > 0)\r
-          pt -= bis.skip(pt);\r
-        if (!asNewStream)\r
-          return bis;\r
-        data = getLimitedStreamBytes(bis, pt_count[1]);\r
-      }\r
-    } catch (Throwable e) {\r
-    } finally {\r
-      try {\r
-        if (asNewStream)\r
-          bis.close();\r
-      } catch (Exception e) {\r
-        // ignore\r
-      }\r
-    }\r
-    return getBIS(data);\r
-  }\r
-\r
-  /** We define a request for zip file extraction by vertical bar:\r
-   *  zipName|interiorFileName. These may be nested if there is a\r
-   *  zip file contained in a zip file. \r
-   *  \r
-   * @param fileName\r
-   * @return filename trimmed of interior fileName\r
-   * \r
-   */\r
-  public static String getZipRoot(String fileName) {\r
-    int pt = fileName.indexOf("|");\r
-    return (pt < 0 ? fileName : fileName.substring(0, pt));\r
-  }\r
-\r
-  \r
-}\r
-\r
+/* $RCSfile$
+ * $Author: hansonr $
+ * $Date: 2007-04-05 09:07:28 -0500 (Thu, 05 Apr 2007) $
+ * $Revision: 7326 $
+ *
+ * Copyright (C) 2003-2005  The Jmol Development Team
+ *
+ * Contact: jmol-developers@lists.sf.net
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+package javajs.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
+import java.util.Map;
+
+
+import javajs.api.GenericCifDataParser;
+import javajs.api.GenericLineReader;
+import javajs.api.GenericZipTools;
+
+/**
+ * A general helper class for a variety of stream and reader functionality
+ * including:
+ * 
+ *  stream and byte magic-number decoding for PNG, PNGJ, ZIP, and GZIP streams
+ *  
+ *  various stream/reader methods, including UTF-encoded stream reading
+ *  
+ *  reflection-protected access to a CIF parser and ZIP tools
+ *  
+ *   
+ * 
+ * 
+ */
+public class Rdr implements GenericLineReader {
+
+  BufferedReader reader;
+
+  public Rdr(BufferedReader reader) {
+    this.reader = reader;
+  }
+  
+  @Override
+  public String readNextLine() throws Exception {
+    return reader.readLine();
+  }
+
+  public static Map<String, Object> readCifData(GenericCifDataParser parser, BufferedReader br) {
+    return parser.set(null, br).getAllCifData();
+  }
+  
+  
+  ///////////
+  
+  public static String fixUTF(byte[] bytes) {    
+    Encoding encoding = getUTFEncoding(bytes);
+    if (encoding != Encoding.NONE)
+    try {
+      String s = new String(bytes, encoding.name().replace('_', '-'));
+      switch (encoding) {
+      case UTF8:
+      case UTF_16BE:
+      case UTF_16LE:
+        // extra byte at beginning removed
+        s = s.substring(1);
+        break;
+      default:
+        break;        
+      }
+      return s;
+    } catch (UnsupportedEncodingException e) {
+      System.out.println(e);
+    }
+    return new String(bytes);
+  }
+
+  private static Encoding getUTFEncoding(byte[] bytes) {
+    if (bytes.length >= 3 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF)
+      return Encoding.UTF8;
+    if (bytes.length >= 4 && bytes[0] == (byte) 0 && bytes[1] == (byte) 0 
+        && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF)
+      return Encoding.UTF_32BE;
+    if (bytes.length >= 4 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE 
+        && bytes[2] == (byte) 0 && bytes[3] == (byte) 0)
+      return Encoding.UTF_32LE;
+    if (bytes.length >= 2 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE)
+      return Encoding.UTF_16LE;
+    if (bytes.length >= 2 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF)
+      return Encoding.UTF_16BE;
+    return Encoding.NONE;
+  
+  }
+  
+  ////////// stream type checking //////////
+  
+
+  private static Encoding getUTFEncodingForStream(BufferedInputStream is) throws IOException {
+    /**
+     * @j2sNative
+     * 
+     *  is.resetStream();
+     * 
+     */
+    {
+    }
+    byte[] abMagic = new byte[4];
+    abMagic[3] = 1;
+    try{
+    is.mark(5);
+    } catch (Exception e) {
+      return Encoding.NONE;
+    }
+    is.read(abMagic, 0, 4);
+    is.reset();
+    return getUTFEncoding(abMagic);
+  }
+
+  public static boolean isBase64(SB sb) {
+    return (sb.indexOf(";base64,") == 0);
+  }
+
+  public static boolean isCompoundDocumentS(InputStream is) {
+    return isCompoundDocumentB(getMagic(is, 8));
+  }
+
+  public static boolean isCompoundDocumentB(byte[] bytes) {
+    return (bytes.length >= 8 && bytes[0] == (byte) 0xD0
+        && bytes[1] == (byte) 0xCF && bytes[2] == (byte) 0x11
+        && bytes[3] == (byte) 0xE0 && bytes[4] == (byte) 0xA1
+        && bytes[5] == (byte) 0xB1 && bytes[6] == (byte) 0x1A 
+        && bytes[7] == (byte) 0xE1);
+  }
+
+  public static boolean isGzipS(InputStream is) {
+    return isGzipB(getMagic(is, 2));
+  }
+
+  public static boolean isGzipB(byte[] bytes) {    
+      return (bytes != null && bytes.length >= 2 
+          && bytes[0] == (byte) 0x1F && bytes[1] == (byte) 0x8B);
+  }
+
+  public static boolean isPickleS(InputStream is) {
+    return Rdr.isPickleB(getMagic(is, 2));
+  }
+
+  public static boolean isPickleB(byte[] bytes) {    
+      return (bytes != null && bytes.length >= 2 
+          && bytes[0] == (byte) 0x7D && bytes[1] == (byte) 0x71);
+  }
+
+  public static boolean isPngZipStream(InputStream is) {
+    return isPngZipB(getMagic(is, 55));
+  }
+
+  public static boolean isPngZipB(byte[] bytes) {
+    // \0PNGJ starting at byte 50
+    return (bytes[50] == 0 && bytes[51] == 0x50 && bytes[52] == 0x4E && bytes[53] == 0x47 && bytes[54] == 0x4A);
+  }
+
+  public static boolean isZipS(InputStream is) {
+    return isZipB(getMagic(is, 4));
+  }
+
+  public static boolean isZipB(byte[] bytes) {
+    return (bytes.length >= 4 
+        && bytes[0] == 0x50  //PK<03><04> 
+        && bytes[1] == 0x4B
+        && bytes[2] == 0x03 
+        && bytes[3] == 0x04);
+  }
+
+  private static byte[] getMagic(InputStream is, int n) {
+    byte[] abMagic = new byte[n];
+    /**
+     * @j2sNative
+     * 
+     * is.resetStream();
+     * 
+     */
+    {
+    }
+    try {
+      is.mark(n + 1);
+      is.read(abMagic, 0, n);
+    } catch (IOException e) {
+    }
+    try {
+      is.reset();
+    } catch (IOException e) {
+    }
+    return abMagic;
+  }
+
+  public static String guessMimeTypeForBytes(byte[] bytes) {
+     // only options here are JPEG, PNG, GIF, and BMP
+    switch (bytes.length < 2 ? -1 : bytes[1]) {
+    case 0:
+      return "image/jpg"; // 0xFF 0x00 ...
+    case 0x49:
+      return "image/gif"; // GIF89a...
+    case 0x4D:
+      return "image/BMP"; // BM...
+    case 0x50:
+      return "image/png";
+    default:
+      return  "image/unknown";
+    }
+  }
+
+
+  ////////// stream/byte methods ///////////
+  
+  public static BufferedInputStream getBIS(byte[] bytes) {
+    return new BufferedInputStream(new ByteArrayInputStream(bytes));
+  }
+
+  public static BufferedReader getBR(String string) {
+    return new BufferedReader(new StringReader(string));
+  }
+
+  /**
+   * Drill down into a GZIP stack until no more layers.
+   * @param jzt 
+   * 
+   * @param bis
+   * @return non-gzipped buffered input stream.
+   * 
+   * @throws IOException
+   */
+  public static BufferedInputStream getUnzippedInputStream(GenericZipTools jzt, BufferedInputStream bis) throws IOException {
+    while (isGzipS(bis))
+      bis = new BufferedInputStream(jzt.newGZIPInputStream(bis));
+    return bis;
+  }
+
+  /**
+   * Allow for base64-encoding check.
+   * 
+   * @param sb
+   * @return byte array
+   */
+  public static byte[] getBytesFromSB(SB sb) {
+    return (isBase64(sb) ? Base64.decodeBase64(sb.substring(8)) : sb.toBytes(0, -1));    
+  }
+
+ /**
+   * Read a an entire BufferedInputStream for its bytes, and 
+   * either return them or leave them in the designated output channel.
+   *  
+   * @param bis
+   * @param out a destination output channel, or null
+   * @return byte[] (if out is null) or a message indicating length (if not)
+   * 
+   * @throws IOException
+   */
+  public static Object getStreamAsBytes(BufferedInputStream bis,
+                                         OC out) throws IOException {
+    byte[] buf = new byte[1024];
+    byte[] bytes = (out == null ? new byte[4096] : null);
+    int len = 0;
+    int totalLen = 0;
+    while ((len = bis.read(buf, 0, 1024)) > 0) {
+      totalLen += len;
+      if (out == null) {
+        if (totalLen >= bytes.length)
+          bytes = AU.ensureLengthByte(bytes, totalLen * 2);
+        System.arraycopy(buf, 0, bytes, totalLen - len, len);
+      } else {
+        out.write(buf, 0, len);
+      }
+    }
+    bis.close();
+    if (out == null) {
+      return AU.arrayCopyByte(bytes, totalLen);
+    }
+    return totalLen + " bytes";
+  }
+
+  /**
+   * Read an input stream fully, saving a byte array, then
+   * return a buffered reader to those bytes converted to string form.
+   * 
+   * @param bis
+   * @param charSet
+   * @return Reader
+   * @throws IOException
+   */
+  public static BufferedReader getBufferedReader(BufferedInputStream bis, String charSet)
+      throws IOException {
+    // could also just make sure we have a buffered input stream here.
+    if (getUTFEncodingForStream(bis) == Encoding.NONE)
+      return new BufferedReader(new InputStreamReader(bis, (charSet == null ? "UTF-8" : charSet)));
+    byte[] bytes = getLimitedStreamBytes(bis, -1);
+    bis.close();
+    return getBR(charSet == null ? fixUTF(bytes) : new String(bytes, charSet));
+  }
+
+  /**
+   * Read a possibly limited number of bytes (when n > 0) from a stream, 
+   * leaving the stream open.
+   * 
+   * @param is an input stream, not necessarily buffered.
+   * @param n the maximum number of bytes to read, or -1 for all
+   * @return the bytes read
+   * 
+   * @throws IOException
+   */
+  public static byte[] getLimitedStreamBytes(InputStream is, long n)
+      throws IOException {
+
+    //Note: You cannot use InputStream.available() to reliably read
+    //      zip data from the web. 
+
+    int buflen = (n > 0 && n < 1024 ? (int) n : 1024);
+    byte[] buf = new byte[buflen];
+    byte[] bytes = new byte[n < 0 ? 4096 : (int) n];
+    int len = 0;
+    int totalLen = 0;
+    if (n < 0)
+      n = Integer.MAX_VALUE;
+    while (totalLen < n && (len = is.read(buf, 0, buflen)) > 0) {
+      totalLen += len;
+      if (totalLen > bytes.length)
+        bytes = AU.ensureLengthByte(bytes, totalLen * 2);
+      System.arraycopy(buf, 0, bytes, totalLen - len, len);
+      if (n != Integer.MAX_VALUE && totalLen + buflen > bytes.length)
+        buflen = bytes.length - totalLen;
+
+    }
+    if (totalLen == bytes.length)
+      return bytes;
+    buf = new byte[totalLen];
+    System.arraycopy(bytes, 0, buf, 0, totalLen);
+    return buf;
+  }
+
+  /**
+   * 
+   * Read a UTF-8 stream fully, converting it to a String.
+   * Called by Jmol's XMLReaders
+   * 
+   * @param bis
+   * @return a UTF-8 string
+   */
+  public static String StreamToUTF8String(BufferedInputStream bis) {
+    String[] data = new String[1];
+    try {
+      readAllAsString(getBufferedReader(bis, "UTF-8"), -1, true, data, 0);
+    } catch (IOException e) {
+    }
+    return data[0];
+  }
+
+  /**
+   * This method fills data[i] with string data from a file that may or may not
+   * be binary even though it is being read by a reader. It is meant to be used
+   * simple text-based files only.
+   *  
+   * @param br
+   * @param nBytesMax
+   * @param allowBinary
+   * @param data
+   * @param i
+   * @return true if data[i] holds the data; false if data[i] holds an error message. 
+   */
+  public static boolean readAllAsString(BufferedReader br, int nBytesMax, boolean allowBinary, String[] data, int i) {
+    try {
+      SB sb = SB.newN(8192);
+      String line;
+      if (nBytesMax < 0) {
+        line = br.readLine();
+        if (allowBinary || line != null && line.indexOf('\0') < 0
+            && (line.length() != 4 || line.charAt(0) != 65533
+            || line.indexOf("PNG") != 1)) {
+          sb.append(line).appendC('\n');
+          while ((line = br.readLine()) != null)
+            sb.append(line).appendC('\n');
+        }
+      } else {
+        int n = 0;
+        int len;
+        while (n < nBytesMax && (line = br.readLine()) != null) {
+          if (nBytesMax - n < (len = line.length()) + 1)
+            line = line.substring(0, nBytesMax - n - 1);
+          sb.append(line).appendC('\n');
+          n += len + 1;
+        }
+      }
+      br.close();
+      data[i] = sb.toString();
+      return true;
+    } catch (Exception ioe) {
+      data[i] = ioe.toString();
+      return false;
+    }
+  }
+
+  
+  /////////// PNGJ support /////////////
+  
+  
+  /**
+   * Look at byte 50 for "\0PNGJxxxxxxxxx+yyyyyyyyy" where xxxxxxxxx is a byte
+   * offset to the JMOL data and yyyyyyyyy is the length of the data.
+   * 
+   * @param bis
+   * @return same stream or byte stream
+   */
+
+  /**
+   * Retrieve the two numbers in a PNG iTXt tag indicating the 
+   * file pointer for the start of the ZIP data as well as its length.
+   * 
+   * @param bis
+   * @param pt_count
+   */
+  static void getPngZipPointAndCount(BufferedInputStream bis, int[] pt_count) {
+    bis.mark(75);
+    try {
+      byte[] data = getLimitedStreamBytes(bis, 74);
+      bis.reset();
+      int pt = 0;
+      for (int i = 64, f = 1; --i > 54; f *= 10)
+        pt += (data[i] - '0') * f;
+      int n = 0;
+      for (int i = 74, f = 1; --i > 64; f *= 10)
+        n += (data[i] - '0') * f;
+      pt_count[0] = pt;
+      pt_count[1] = n;
+    } catch (Throwable e) {
+      pt_count[1] = 0;
+    }
+  }
+
+  /**
+   * Either advance a PNGJ stream to its zip file data or pull out the ZIP data
+   * bytes and create a new stream for them from which a ZIP utility can start
+   * extracting files.
+   * 
+   * @param bis
+   * @param asNewStream  
+   * @return new buffered ByteArrayInputStream, possibly with no data if there is an error
+   */
+  public static BufferedInputStream getPngZipStream(BufferedInputStream bis, boolean asNewStream) {
+    if (!isPngZipStream(bis))
+      return bis;
+    byte[] data = new byte[0];
+    bis.mark(75);
+    try {
+      int pt_count[] = new int[2];
+      getPngZipPointAndCount(bis, pt_count);
+      if (pt_count[1] != 0) {
+        int pt = pt_count[0];
+        while (pt > 0)
+          pt -= bis.skip(pt);
+        if (!asNewStream)
+          return bis;
+        data = getLimitedStreamBytes(bis, pt_count[1]);
+      }
+    } catch (Throwable e) {
+    } finally {
+      try {
+        if (asNewStream)
+          bis.close();
+      } catch (Exception e) {
+        // ignore
+      }
+    }
+    return getBIS(data);
+  }
+
+  /** We define a request for zip file extraction by vertical bar:
+   *  zipName|interiorFileName. These may be nested if there is a
+   *  zip file contained in a zip file. 
+   *  
+   * @param fileName
+   * @return filename trimmed of interior fileName
+   * 
+   */
+  public static String getZipRoot(String fileName) {
+    int pt = fileName.indexOf("|");
+    return (pt < 0 ? fileName : fileName.substring(0, pt));
+  }
+
+  
+}
+