Files.java Source code

Java tutorial

Introduction

Here is the source code for Files.java

Source

/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt in the distribution for a
  * full listing of individual contributors.
  *
  * This 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 software 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 software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.UnsupportedEncodingException;
import java.net.URL;
import java.util.logging.Logger;

/**
 * A collection of file utilities.
 *
 * @author  <a href="mailto:jason@planet57.com">Jason Dillon</a>
 * @author  <a href="mailto:Scott.Stark@jboss.org">Scott Stark<a/>
 * @author  <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
 * @version $Revision: 1958 $
 */
public final class Files {

    /** for byte-to-hex conversions */
    private static final char[] hexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
            'C', 'D', 'E', 'F' };

    /** The default size of the copy buffer. */
    public static final int DEFAULT_BUFFER_SIZE = 8192;

    /** Delete a file, or a directory and all of its contents.
     *
     * @param dir The directory or file to delete.
     * @return True if all delete operations were successfull.
     */
    public static boolean delete(final File dir) {
        boolean success = true;

        File files[] = dir.listFiles();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                File f = files[i];
                if (f.isDirectory() == true) {
                    // delete the directory and all of its contents.
                    if (delete(f) == false) {
                        success = false;
                        System.out.println("Failed to delete dir: " + f.getAbsolutePath());
                    }
                }
                // delete each file in the directory
                else if (f.delete() == false) {
                    success = false;
                    System.out.println("Failed to delete file: " + f.getAbsolutePath());
                }
            }
        }

        // finally delete the directory
        if (dir.delete() == false) {
            success = false;
            System.out.println("Failed to delete dir: " + dir.getAbsolutePath());
        }

        return success;
    }

    /**
     * Delete a file or directory and all of its contents.
     *
     * @param dirname  The name of the file or directory to delete.
     * @return True if all delete operations were successfull.
     */
    public static boolean delete(final String dirname) {
        return delete(new File(dirname));
    }

    /**
     * Delete a directory contaning the given file and all its contents.
     *
     * @param filename a file or directory in the containing directory to delete
     * @return true if all delete operations were successfull, false if any
     * delete failed.
     */
    public static boolean deleteContaining(final String filename) {
        File file = new File(filename);
        File containingDir = file.getParentFile();
        return delete(containingDir);
    }

    /**
     * Copy a file.
     *
     * @param source  Source file to copy.
     * @param target  Destination target file.
     * @param buff    The copy buffer.
     *
     * @throws IOException  Failed to copy file.
     */
    public static void copy(final File source, final File target, final byte buff[]) throws IOException {
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(source));
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(target));

        int read;

        try {
            while ((read = in.read(buff)) != -1) {
                out.write(buff, 0, read);
            }
        } finally {
            Streams.flush(out);
            Streams.close(in);
            Streams.close(out);
        }
    }

    /**
     * Copy a file.
     *
     * @param source  Source file to copy.
     * @param target  Destination target file.
     * @param size    The size of the copy buffer.
     *
     * @throws IOException  Failed to copy file.
     */
    public static void copy(final File source, final File target, final int size) throws IOException {
        copy(source, target, new byte[size]);
    }

    /**
     * Copy a file.
     *
     * @param source  Source file to copy.
     * @param target  Destination target file.
     *
     * @throws IOException  Failed to copy file.
     */
    public static void copy(final File source, final File target) throws IOException {
        copy(source, target, DEFAULT_BUFFER_SIZE);
    }

    /**
     * Copy a remote/local URL to a local file
     * 
     * @param src the remote or local URL
     * @param dest the local file
     * @throws IOException upon error
     */
    public static void copy(URL src, File dest) throws IOException {
        System.out.println("Copying " + src + " -> " + dest);

        // Validate that the dest parent directory structure exists
        File dir = dest.getParentFile();
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new IOException("mkdirs failed for: " + dir.getAbsolutePath());
            }
        }
        // Remove any existing dest content
        if (dest.exists()) {
            if (!Files.delete(dest)) {
                throw new IOException("delete of previous content failed for: " + dest.getAbsolutePath());
            }
        }
        // Treat local and remote URLs the same
        // prepare streams, do the copy and flush
        InputStream in = new BufferedInputStream(src.openStream());
        OutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
        Streams.copy(in, out);
        out.flush();
        out.close();
        in.close();
    }

    /**
     * Used to encode any string into a string that is safe to use as 
     * a file name on most operating systems.
     * 
     * Use decodeFileName() to get back the original string.
     * 
     * Copied by Adrian's org.jboss.mq.pm.file.PersistenceManager
     * and adapted to use hex instead of decimal digits
     * 
     * @param name the filename to encode
     * @return a filesystem-friendly filename
     */
    public static String encodeFileName(String name) {
        return encodeFileName(name, '@');
    }

    /**
     * Used to decode a file system friendly filename produced
     * by encodeFileName() method, above.
     * 
     * Copied by Adrian's org.jboss.mq.pm.file.PersistenceManager
     * and adapted to use hex instead of decimal digits
     * 
     * Note:
     *   Decoding will not work if encoding produced
     *   multi-byte encoded characters. If this is truly
     *   needed we'll have to revise the encoding.
     * 
     * @param name the filename to decode
     * @return the original name
     */
    public static String decodeFileName(String name) {
        return decodeFileName(name, '@');
    }

    /**
     * See encodeFileName(String) above.
     * 
     * @param name the filename to encode
     * @param escape the escape character to use
     * @return a filesystem-friendly filename
     */
    public static String encodeFileName(String name, char escape) {
        StringBuffer rc = new StringBuffer();
        for (int i = 0; i < name.length(); i++) {
            switch (name.charAt(i)) {
            // These are the safe characters...
            case 'a':
            case 'A':
            case 'b':
            case 'B':
            case 'c':
            case 'C':
            case 'd':
            case 'D':
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case 'g':
            case 'G':
            case 'h':
            case 'H':
            case 'i':
            case 'I':
            case 'j':
            case 'J':
            case 'k':
            case 'K':
            case 'l':
            case 'L':
            case 'm':
            case 'M':
            case 'n':
            case 'N':
            case 'o':
            case 'O':
            case 'p':
            case 'P':
            case 'q':
            case 'Q':
            case 'r':
            case 'R':
            case 's':
            case 'S':
            case 't':
            case 'T':
            case 'u':
            case 'U':
            case 'v':
            case 'V':
            case 'w':
            case 'W':
            case 'x':
            case 'X':
            case 'y':
            case 'Y':
            case 'z':
            case 'Z':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
            case '-':
            case '_':
            case '.':
                rc.append(name.charAt(i));
                break;

            // Any other character needs to be encoded.
            default:

                // We encode the characters as <esc>hh,
                // where <esc> is the passed escape character and
                // hh is the hex value of the UTF8 byte of the character.
                // You might get <esc>hh<esc>hh since UTF8 can produce multiple
                // bytes for a single character.
                try {
                    byte data[] = ("" + name.charAt(i)).getBytes("UTF8");
                    for (int j = 0; j < data.length; j++) {
                        rc.append(escape);
                        rc.append(hexDigits[(data[j] >> 4) & 0xF]); // high order digit
                        rc.append(hexDigits[(data[j]) & 0xF]); // low order digit                     
                    }
                } catch (UnsupportedEncodingException wonthappen) {
                    // nada
                }
            }
        }
        return rc.toString();
    }

    /**
     * See decodeFileName(String) above.
     *  
     * @param name the filename to decode
     * @param escape the escape character to use
     * @return the original name
     */
    public static String decodeFileName(String name, char escape) {
        if (name == null) {
            return null;
        }
        StringBuffer sbuf = new StringBuffer(name.length());

        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            if (c == escape) {
                char h1 = name.charAt(++i);
                char h2 = name.charAt(++i);

                // convert hex digits to integers
                int d1 = (h1 >= 'a') ? (10 + h1 - 'a') : ((h1 >= 'A') ? (10 + h1 - 'A') : (h1 - '0'));

                int d2 = (h2 >= 'a') ? (10 + h2 - 'a') : ((h2 >= 'A') ? (10 + h2 - 'A') : (h2 - '0'));

                // handling only the <esc>hh case here, as we don't know
                // if <esc>hh<esc>hh belong to the same character
                // (and we are lazy to change the encoding) - REVISIT
                byte[] bytes = new byte[] { (byte) (d1 * 16 + d2) };

                try {
                    String s = new String(bytes, "UTF8");
                    sbuf.append(s);
                } catch (UnsupportedEncodingException wonthappen) {
                    // nada
                }
            } else {
                sbuf.append(c);
            }
        }
        return sbuf.toString();
    }

}

final class Streams {
    private static final Logger log = Logger.getLogger("Streams.class");

    /////////////////////////////////////////////////////////////////////////
    //                               Closing                               //
    /////////////////////////////////////////////////////////////////////////

    /**
     * Attempt to close an <tt>InputStream</tt>.
     *
     * @param stream  <tt>InputStream</tt> to attempt to close.
     * @return        <tt>True</tt> if stream was closed (or stream was null),
     *                or <tt>false</tt> if an exception was thrown.
     */
    public static boolean close(final InputStream stream) {
        // do not attempt to close null stream, but return sucess
        if (stream == null) {
            return true;
        }

        boolean success = true;

        try {
            stream.close();
        } catch (IOException e) {
            success = false;
        }

        return success;
    }

    /**
     * Attempt to close an <tt>OutputStream</tt>.
     *
     * @param stream  <tt>OutputStream</tt> to attempt to close.
     * @return        <tt>True</tt> if stream was closed (or stream was null),
     *                or <tt>false</tt> if an exception was thrown.
     */
    public static boolean close(final OutputStream stream) {
        // do not attempt to close null stream, but return sucess
        if (stream == null) {
            return true;
        }

        boolean success = true;

        try {
            stream.close();
        } catch (IOException e) {
            success = false;
        }

        return success;
    }

    /**
     * Attempt to close an <tt>InputStream</tt> or <tt>OutputStream</tt>.
     *
     * @param stream  Stream to attempt to close.
     * @return        <tt>True</tt> if stream was closed (or stream was null),
     *                or <tt>false</tt> if an exception was thrown.
     *
     * @throws IllegalArgumentException    Stream is not an <tt>InputStream</tt>
     *                                     or <tt>OuputStream</tt>.
     */
    public static boolean close(final Object stream) {
        boolean success = false;

        if (stream instanceof InputStream) {
            success = close((InputStream) stream);
        } else if (stream instanceof OutputStream) {
            success = close((OutputStream) stream);
        } else {
            throw new IllegalArgumentException("stream is not an InputStream or OutputStream");
        }

        return success;
    }

    /**
     * Attempt to close an array of <tt>InputStream</tt>s.
     *
     * @param streams Array of <tt>InputStream</tt>s to attempt to close.
     * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
     *                if an exception was thrown.
     */
    public static boolean close(final InputStream[] streams) {
        boolean success = true;

        for (int i = 0; i < streams.length; i++) {
            boolean rv = close(streams[i]);
            if (!rv)
                success = false;
        }

        return success;
    }

    /**
     * Attempt to close an array of <tt>OutputStream</tt>s.
     *
     * @param streams Array of <tt>OutputStream</tt>s to attempt to close.
     * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
     *                if an exception was thrown.
     */
    public static boolean close(final OutputStream[] streams) {
        boolean success = true;

        for (int i = 0; i < streams.length; i++) {
            boolean rv = close(streams[i]);
            if (!rv)
                success = false;
        }

        return success;
    }

    /**
     * Attempt to close an array of <tt>InputStream</tt>a and/or 
     * <tt>OutputStream</tt>s.
     *
     * @param streams Array of streams to attempt to close.
     * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
     *                if an exception was thrown.
     *
     * @throws IllegalArgumentException    Stream is not an <tt>InputStream</tt>
     *                                     or <tt>OuputStream</tt>.  Closing 
     *                                     stops at the last valid stream
     *                                     object in this case.
     */
    public static boolean close(final Object[] streams) {
        boolean success = true;

        for (int i = 0; i < streams.length; i++) {
            boolean rv = close(streams[i]);
            if (!rv)
                success = false;
        }

        return success;
    }

    /**
     * Attempt to flush and close an <tt>OutputStream</tt>.
     *
     * @param stream  <tt>OutputStream</tt> to attempt to flush and close.
     * @return        <tt>True</tt> if stream was flushed and closed, or
     *                <tt>false</tt> if an exception was thrown.
     */
    public static boolean fclose(final OutputStream stream) {
        return flush(stream) && close(stream);
    }

    /**
     * Attempt to flush and close an array of <tt>OutputStream</tt>s.
     *
     * @param streams  <tt>OutputStream</tt>s to attempt to flush and close.
     * @return         <tt>True</tt> if all streams were flushed and closed, 
     *                 or <tt>false</tt> if an exception was thrown.
     */
    public static boolean fclose(final OutputStream[] streams) {
        boolean success = true;

        for (int i = 0; i < streams.length; i++) {
            boolean rv = fclose(streams[i]);
            if (!rv)
                success = false;
        }

        return success;
    }

    /////////////////////////////////////////////////////////////////////////
    //                                Flushing                             //
    /////////////////////////////////////////////////////////////////////////

    /**
     * Attempt to flush an <tt>OutputStream</tt>.
     *
     * @param stream  <tt>OutputStream</tt> to attempt to flush.
     * @return        <tt>True</tt> if stream was flushed (or stream was null),
     *                or <tt>false</tt> if an exception was thrown.
     */
    public static boolean flush(final OutputStream stream) {
        // do not attempt to close null stream, but return sucess
        if (stream == null) {
            return true;
        }

        boolean success = true;

        try {
            stream.flush();
        } catch (IOException e) {
            success = false;
        }

        return success;
    }

    /**
     * Attempt to flush an array of <tt>OutputStream</tt>s.
     *
     * @param streams <tt>OutputStream</tt>s to attempt to flush.
     * @return        <tt>True</tt> if all streams were flushed, or <tt>false</tt>
     *                 if an exception was thrown.
     */
    public static boolean flush(final OutputStream[] streams) {
        boolean success = true;

        for (int i = 0; i < streams.length; i++) {
            boolean rv = flush(streams[i]);
            if (!rv)
                success = false;
        }

        return success;
    }

    /////////////////////////////////////////////////////////////////////////
    //                                  Misc                               //
    /////////////////////////////////////////////////////////////////////////

    /** The default buffer size that will be used for buffered operations. */
    public static final int DEFAULT_BUFFER_SIZE = 2048;

    /**
     * Copy all of the bytes from the input stream to the output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @param buffer  The buffer to use while copying.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copy(final InputStream input, final OutputStream output, final byte buffer[])
            throws IOException {
        long total = 0;
        int read;

        System.out.println("copying " + input + " to " + output + " with buffer size: " + buffer.length);

        while ((read = input.read(buffer)) != -1) {
            output.write(buffer, 0, read);
            total += read;

            System.out.println("bytes read: " + read + "; total bytes read: " + total);
        }

        return total;
    }

    /**
     * Copy all of the bytes from the input stream to the output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @param size    The size of the buffer to use while copying.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copy(final InputStream input, final OutputStream output, final int size) throws IOException {
        return copy(input, output, new byte[size]);
    }

    /**
     * Copy all of the bytes from the input stream to the output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copy(final InputStream input, final OutputStream output) throws IOException {
        return copy(input, output, DEFAULT_BUFFER_SIZE);
    }

    /**
     * Copy all of the bytes from the input stream to the output stream
     * wrapping streams in buffers as needed.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copyb(InputStream input, OutputStream output) throws IOException {
        if (!(input instanceof BufferedInputStream)) {
            input = new BufferedInputStream(input);
        }

        if (!(output instanceof BufferedOutputStream)) {
            output = new BufferedOutputStream(output);
        }

        long bytes = copy(input, output, DEFAULT_BUFFER_SIZE);

        output.flush();

        return bytes;
    }

    /**
     * Copy a limited number of bytes from the input stream to the 
     * output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @param buffer  The buffer to use while copying.
     * @param length  The maximum number of bytes to copy.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copySome(final InputStream input, final OutputStream output, final byte buffer[],
            final long length) throws IOException {
        long total = 0;
        int read;
        int readLength;

        // setup the initial readLength, if length is less than the buffer
        // size, then we only want to read that much
        readLength = Math.min((int) length, buffer.length);
        System.out.println("initial read length: " + readLength);

        while (readLength != 0 && (read = input.read(buffer, 0, readLength)) != -1) {
            System.out.println("read bytes: " + read);
            output.write(buffer, 0, read);
            total += read;
            System.out.println("total bytes read: " + total);

            // update the readLength
            readLength = Math.min((int) (length - total), buffer.length);
            System.out.println("next read length: " + readLength);
        }

        return total;
    }

    /**
     * Copy a limited number of bytes from the input stream to the 
     * output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @param size    The size of the buffer to use while copying.
     * @param length  The maximum number of bytes to copy.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copySome(final InputStream input, final OutputStream output, final int size,
            final long length) throws IOException {
        return copySome(input, output, new byte[size], length);
    }

    /**
     * Copy a limited number of bytes from the input stream to the 
     * output stream.
     *
     * @param input   Stream to read bytes from.
     * @param output  Stream to write bytes to.
     * @param length  The maximum number of bytes to copy.
     * @return        The total number of bytes copied.
     *
     * @throws IOException  Failed to copy bytes.
     */
    public static long copySome(final InputStream input, final OutputStream output, final long length)
            throws IOException {
        return copySome(input, output, DEFAULT_BUFFER_SIZE, length);
    }
}