/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ package net.ontopia.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.URL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * INTERNAL: Utilities for working with streams and readers. * @since 1.3.3 */ public class StreamUtils { // Define a logging category. static Logger log = LoggerFactory.getLogger(StreamUtils.class.getName()); static final int BUFFER_SIZE = 16384; /** * INTERNAL: Transfers the entire contents of the InputStream to the * OutputStream without modifying them in any way. */ public static void transfer(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[BUFFER_SIZE]; while (true) { int read = in.read(buf, 0, BUFFER_SIZE); if (read == -1) break; out.write(buf, 0, read); } } /** * INTERNAL: Transfers the entire contents of the Reader to the * Writer without modifying them in any way. * * @since 2.0.4 */ public static void transfer(Reader in, Writer out) throws IOException { char[] buf = new char[BUFFER_SIZE]; while (true) { int read = in.read(buf, 0, BUFFER_SIZE); if (read == -1) break; out.write(buf, 0, read); } } /** * INTERNAL: Transfers the entire contents of the InputStream to the * OutputStream without modifying them in any way and closes the * InputStream afterwards. * @since %NEXT% */ public static void transferAndCloseInputStream(InputStream in, OutputStream out) throws IOException { try { transfer(in, out); } finally { in.close(); } } /** * INTERNAL: Transfers the entire contents of the InputStream to the * OutputStream without modifying them in any way and closes the * OutputStream afterwards. * @since %NEXT% */ public static void transferAndCloseOutputStream(InputStream in, OutputStream out) throws IOException { try { transfer(in, out); } finally { out.close(); } } /** * INTERNAL: Transfers the entire contents of the InputStream to the * OutputStream without modifying them in any way and closes the * InputStream and OutputStream afterwards. * @since %NEXT% */ public static void transferAndClose(InputStream in, OutputStream out) throws IOException { try { transferAndCloseInputStream(in, out); } finally { out.close(); } } /** * INTERNAL: Returns the entire contents of a stream (well, Reader) * as a string. * @since 1.4 */ public static String read(Reader in) throws IOException { StringBuilder buffer = new StringBuilder(); char[] buf = new char[BUFFER_SIZE]; while (true) { int read = in.read(buf, 0, BUFFER_SIZE); if (read == -1) break; buffer.append(buf, 0, read); } return buffer.toString(); } /** * INTERNAL: Returns the first given number of bytes of a stream * into a byte array. * @since 2.0 */ public static byte[] read(InputStream in, int length) throws IOException { // ISSUE: Why not just copy it all in one go? byte[] data = new byte[length]; int ix = 0; int len = Math.min(BUFFER_SIZE, data.length); while (len > 0 && in.read(data, ix, len) != -1) { ix += len; len = Math.min(BUFFER_SIZE, data.length - ix); } return data; } /** * INTERNAL: Returns the entire contents of a stream as a byte array. * @since 3.3.0 */ public static byte[] read(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE); transfer(in, out); return out.toByteArray(); } /** * INTERNAL : Returns the entire contents of a stream as a byte array * and closes the stream afterwards. * @since 5.3.0 */ public static byte[] readAndClose(InputStream in) throws IOException { try { return read(in); } finally { in.close(); } } /** * INTERNAL: Compares the contents of the two InputStreams for equality. * @since 4.0 */ public static boolean compare(InputStream r1, InputStream r2) throws IOException { byte[] buf1 = new byte[StreamUtils.BUFFER_SIZE]; byte[] buf2 = new byte[StreamUtils.BUFFER_SIZE]; while (true) { int read1 = r1.read(buf1, 0, StreamUtils.BUFFER_SIZE); int read2 = r2.read(buf2, 0, StreamUtils.BUFFER_SIZE); // compare lengths read if (read1 != read2) return false; // stop if we've reached the end if (read1 == -1) break; // compare read buffers for (int i=0; i < read1; i++) { if (buf1[i] != buf2[i]) return false; } } return true; } /** * INTERNAL: Compares the contents of the two InputStream for equality * and closes them afterwards. */ public static boolean compareAndClose(InputStream r1, InputStream r2) throws IOException { try { try { return compare(r1, r2); } finally { r1.close(); } } finally { r2.close(); } } /** * INTERNAL: Compares the contents of the two Readers for equality. * @since 4.0 */ public static boolean compare(Reader r1, Reader r2) throws IOException { char[] buf1 = new char[StreamUtils.BUFFER_SIZE]; char[] buf2 = new char[StreamUtils.BUFFER_SIZE]; while (true) { int read1 = r1.read(buf1, 0, StreamUtils.BUFFER_SIZE); int read2 = r2.read(buf2, 0, StreamUtils.BUFFER_SIZE); // compare lengths read if (read1 != read2) return false; // stop if we've reached the end if (read1 == -1) break; // compare read buffers for (int i=0; i < read1; i++) { if (buf1[i] != buf2[i]) return false; } } return true; } /** * INTERNAL: Same as getInputStream(null, name); * * @since 3.4.3 */ public static InputStream getInputStream(String name) throws IOException { return getInputStream(null, name); } /** * INTERNAL: Returns an input stream for the given url. Supports * file: and classpath:. If no scheme given then file system will be * checked before classpath. Exception will be thrown if resource is * not found when scheme is given. If no scheme was given null is * returned. File references will be interpreted relative to basedir. * If basedir is null, it will be ignored. * * @since 5.1.1 */ public static InputStream getInputStream(File basedir, String name) throws IOException { InputStream istream; if (name.startsWith("classpath:")) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); String resourceName = name.substring("classpath:".length()); istream = cl.getResourceAsStream(resourceName); if (istream == null) throw new IOException("Resource '" + resourceName + "' not found through class loader."); log.debug("File loaded through class loader: " + name); } else if (name.startsWith("file:")) { File f = makeFile(basedir, name.substring("file:".length())); if (f.exists()) { log.debug("File loaded from file system: " + name); istream = new FileInputStream(f); } else throw new IOException("File '" + f + "' not found."); } else { File f = makeFile(basedir, name); if (f.exists()) { log.debug("File loaded from file system: " + name); istream = new FileInputStream(f); } else { ClassLoader cl = Thread.currentThread().getContextClassLoader(); istream = cl.getResourceAsStream(name); if (istream != null) log.debug("File loaded through class loader: " + name); } } return istream; } private static File makeFile(File basedir, String name) { if (basedir == null) return new File(name); else return new File(basedir, name); } /** * INTERNAL: Returns an input stream for the given url. Supports * file: and classpath:. If no schema given then file system will be * checked before classpath. Exception will be thrown if resource is * not found when schema is given. If no schema was given null is * returned. * * @since 3.4.3 */ public static URL getResource(String name) throws IOException { URL url; if (name.startsWith("classpath:")) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); String resourceName = name.substring("classpath:".length()); url = cl.getResource(resourceName); if (url == null) throw new FileNotFoundException("Resource '" + resourceName + "' not found through class loader."); log.debug("File loaded through class loader: " + name); } else if (name.startsWith("file:")) { File f = new File(name.substring("file:".length())); if (f.exists()) { log.debug("File loaded from file system: " + name); url = URIUtils.toURL(f); } else throw new IOException("File '" + f + "' not found."); } else { File f = new File(name); if (f.exists()) { log.debug("File loaded from file system: " + name); url = URIUtils.toURL(f); } else { ClassLoader cl = Thread.currentThread().getContextClassLoader(); url = cl.getResource(name); if (url != null) log.debug("File loaded through class loader: " + name); } } return url; } public static String readString(Reader r, long length) throws IOException { char[] chars = new char[(int)length]; StringBuilder result = new StringBuilder((int)length); int read; while ((read = r.read(chars)) != -1) { result.append(chars, 0, read); } return result.toString(); } }