X-Git-Url: http://source.jalview.org/gitweb/?p=jalviewjs.git;a=blobdiff_plain;f=src%2Fjavajs%2Futil%2FRdr.java;fp=src%2Fjavajs%2Futil%2FRdr.java;h=52969641424cc16aed6cd584dbf034d3e232d13a;hp=977bfe26f195dc038ed33716fdfc24f5d6fac3ba;hb=b9b7a352eee79b7764c3b09c9d19663075061d8c;hpb=7301a2415adab88038b291fc54caeeb3a5a47a44 diff --git a/src/javajs/util/Rdr.java b/src/javajs/util/Rdr.java index 977bfe2..5296964 100644 --- a/src/javajs/util/Rdr.java +++ b/src/javajs/util/Rdr.java @@ -1,509 +1,509 @@ -/* $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 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)); - } - - -} - +/* $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 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)); + } + + +} +