/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.commons.util; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.net.Socket; import java.nio.channels.Channel; import java.util.logging.Level; import java.util.logging.Logger; /** * Collects some I/O utility functions. * * @author Michael Griffel * @author Andreas Mandel */ public final class IoUtil { /** class name for use in logger */ private static final String CLASSNAME = IoUtil.class.getName(); /** logging facility */ private static final Logger logger = Logger.getLogger(CLASSNAME); private static final int BUFFER_SIZE = 4096; /** * Constructor. */ private IoUtil () { // Utility class -- only static methods } /** * Closes the input stream (safe). * * This method tries to close the given input stream and * if an IOException occurs a message with the level * {@link Level#FINE} is logged. It's safe to pass a * <code>null</code> reference for the argument. * * @param in the input stream that should be closed. */ public static void close (InputStream in) { if (in != null) { try { in.close(); } catch (IOException x) { logCloseFailedWarningMessage( InputStream.class, in.getClass(), x); } } } /** * Closes the output stream (safe). * * This method tries to close the given output stream and * if an IOException occurs a message with the level * {@link Level#FINE} is logged. It's safe to pass a * <code>null</code> reference for the argument. * * @param out the output stream that should be closed. */ public static void close (OutputStream out) { if (out != null) { try { out.close(); } catch (IOException x) { logCloseFailedWarningMessage( OutputStream.class, out.getClass(), x); } } } /** * Closes the reader (safe). * * This method tries to close the given reader and if an IOException occurs * a message with the level {@link Level#FINE} is logged. It's safe * to pass a <code>null</code> reference for the argument. * * @param reader the reader that should be closed. */ public static void close (Reader reader) { if (reader != null) { try { reader.close(); } catch (IOException x) { logCloseFailedWarningMessage( Reader.class, reader.getClass(), x); } } } /** * Closes the RandomAccessFile (safe). * * This method tries to close the given RandomAccessFile and if an * IOException occurs a message with the level {@link Level#FINE} * is logged. It's safe to pass a <code>null</code> reference * for the argument. * * @param randomAccessFile the randomAccessFile that should be closed. */ public static void close (RandomAccessFile randomAccessFile) { if (randomAccessFile != null) { try { randomAccessFile.close(); } catch (IOException x) { logCloseFailedWarningMessage( Reader.class, randomAccessFile.getClass(), x); } } } /** * Closes the writer (safe). * * This method tries to close the given writer and if an IOException occurs * a message with the level {@link Level#FINE} is logged. It's safe * to pass a <code>null</code> reference for the argument. * * @param writer the writer that should be closed. */ public static void close (Writer writer) { if (writer != null) { try { writer.close(); } catch (IOException x) { logCloseFailedWarningMessage( Writer.class, writer.getClass(), x); } } } /** * Closes the channel (safe). * * This method tries to close the given channel and if an IOException occurs * a message with the level {@link Level#FINE} is logged. It's safe * to pass a <code>null</code> reference for the argument. * * @param channel the channel that should be closed. */ public static void close (Channel channel) { if (channel != null) { try { channel.close(); } catch (IOException x) { logCloseFailedWarningMessage(Channel.class, channel.getClass(), x); } } } /** * Closes the socket (safe). * * This method tries to close the given socket and if an IOException occurs * a message with the level {@link Level#FINE} is logged. It's safe * to pass a <code>null</code> reference for the argument. * * @param socket the socket that should be closed. */ public static void close (Socket socket) { if (socket != null) { try { socket.close(); } catch (IOException x) { logCloseFailedWarningMessage(Channel.class, socket.getClass(), x); } } } /** * Reads <code>expectedLength</code> bytes from the input * stream <code>in</code>. * * @param in the input stream to read from. * @param expectedLength the expected size. * @return an byte array with <code>expectedLength</code> bytes. * @throws IOException in an I/O error occurs or if the read size is not the * expected size. */ public static byte[] readFully (InputStream in, int expectedLength) throws IOException { final byte[] buffer = new byte[expectedLength]; int pos = 0; int read = 0; while ((read = in.read(buffer, pos, expectedLength - pos)) >= 0) { pos += read; if (expectedLength == pos) { break; } } if (expectedLength != pos) { throw new IOException( "Buffer underread. Could not read " + expectedLength + " bytes from stream. Expected was " + expectedLength + " bytes, but got only " + pos + " bytes"); } return buffer; } /** * Reads all bytes from the input stream <code>in</code>. * * @param in the input stream to read from. * @return an byte array with read bytes. * @throws IOException in an I/O error occurs. */ public static byte[] readFully (InputStream in) throws IOException { final byte[] buffer = new byte[BUFFER_SIZE]; int read = 0; final ByteArrayOutputStream out = new ByteArrayOutputStream(); while ((read = in.read(buffer)) >= 0) { out.write(buffer, 0, read); } return out.toByteArray(); } /** * Reads all characters from the reader <code>in</code>. * * @param in the Reader to read from. * @return an String containing the read data * @throws IOException in an I/O error occurs. */ public static String readFully (Reader in) throws IOException { final char[] buffer = new char[BUFFER_SIZE]; int read = 0; final StringWriter out = new StringWriter(); while ((read = in.read(buffer)) >= 0) { out.write(buffer, 0, read); } return out.toString(); } /** * Reads all characters from the reader <code>in</code> and * normalizes newlines to single '\n'. * * @param in the Reader to read from. * @return an String containing the read data * @throws IOException in an I/O error occurs. */ public static String readFullyNormalizeNewLine (Reader in) throws IOException { final StringWriter out = new StringWriter(); int c; int last = 0; while ((c = in.read()) != -1) { if (c == '\n') { if (last != '\r') { out.write(c); } } else if (c == '\r' || c == '\u0085' || c == '\u2028' || c == '\u2029') { if (last != '\n') { out.write('\n'); } } else { out.write(c); } last = c; } return out.toString(); } /** * Reads all data from in and copies it to the out stream. * @param in the stream to read. * @param out the stream to write. * @throws IOException if an I/O issue occurs. */ public static void copy (InputStream in, OutputStream out) throws IOException { final byte[] buffer = new byte[BUFFER_SIZE]; int read; while ((read = in.read(buffer)) >= 0) { out.write(buffer, 0, read); } } /** * Copies file <tt>src</tt> to file <tt>dest</tt>. * @param src the source file. * @param dest the destination file. * @throws IOException if an I/O issue occurs. */ public static void copy (File src, File dest) throws IOException { final InputStream in = new FileInputStream(src); try { final OutputStream out = new FileOutputStream(dest); try { copy(in, out); } finally { close(out); } } finally { close(in); } } /** * Ensures that the given number of bytes are skipped from the given * input stream. * @param in the input stream. * @param bytes the number of bytes to skip. * @throws IOException if the stream does not support seek, * or if some other I/O error occurs. * @see InputStream#skip */ public static void skip (InputStream in, int bytes) throws IOException { long remaining = bytes; while (remaining != 0) { final long skipped = in.skip(remaining); if (skipped == 0) { throw new EOFException(); } remaining -= skipped; } } private static void logCloseFailedWarningMessage ( Class resource, Class clazz, IOException x) { logger.log(Level.FINE, "Error while closing " + resource.getName() + ": " + clazz.getName() + ".close()", x); } }