File Input Output Java

/*
  * 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  Jason Dillon
 * @author  Scott Stark
 * @author  Dimitris Andreadis

 * @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 hh,
               // where  is the passed escape character and
               // hh is the hex value of the UTF8 byte of the character.
               // You might get hhhh 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 hh case here, as we don't know
            // if hhhh 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 InputStream.
    *
    * @param stream  InputStream to attempt to close.
    * @return        True if stream was closed (or stream was null),
    *                or false 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 OutputStream.
    *
    * @param stream  OutputStream to attempt to close.
    * @return        True if stream was closed (or stream was null),
    *                or false 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 InputStream or OutputStream.
    *
    * @param stream  Stream to attempt to close.
    * @return        True if stream was closed (or stream was null),
    *                or false if an exception was thrown.
    *
    * @throws IllegalArgumentException    Stream is not an InputStream
    *                                     or OuputStream.
    */
   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 InputStreams.
    *
    * @param streams Array of InputStreams to attempt to close.
    * @return        True if all streams were closed, or false
    *                if an exception was thrown.
    */
   public static boolean close(final InputStream[] streams) {
      boolean success = true;
      for (int i=0; i         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }
      return success;
   }
   /**
    * Attempt to close an array of OutputStreams.
    *
    * @param streams Array of OutputStreams to attempt to close.
    * @return        True if all streams were closed, or false
    *                if an exception was thrown.
    */
   public static boolean close(final OutputStream[] streams) {
      boolean success = true;
      for (int i=0; i         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }
      return success;
   }
   /**
    * Attempt to close an array of InputStreama and/or 
    * OutputStreams.
    *
    * @param streams Array of streams to attempt to close.
    * @return        True if all streams were closed, or false
    *                if an exception was thrown.
    *
    * @throws IllegalArgumentException    Stream is not an InputStream
    *                                     or OuputStream.  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         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }
      return success;
   }
   /**
    * Attempt to flush and close an OutputStream.
    *
    * @param stream  OutputStream to attempt to flush and close.
    * @return        True if stream was flushed and closed, or
    *                false 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 OutputStreams.
    *
    * @param streams  OutputStreams to attempt to flush and close.
    * @return         True if all streams were flushed and closed, 
    *                 or false if an exception was thrown.
    */
   public static boolean fclose(final OutputStream[] streams) {
      boolean success = true;
      for (int i=0; i         boolean rv = fclose(streams[i]); 
         if (!rv) success = false;
      }
      return success;
   }
    
   /////////////////////////////////////////////////////////////////////////
   //                                Flushing                             //
   /////////////////////////////////////////////////////////////////////////
   /**
    * Attempt to flush an OutputStream.
    *
    * @param stream  OutputStream to attempt to flush.
    * @return        True if stream was flushed (or stream was null),
    *                or false 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 OutputStreams.
    *
    * @param streams OutputStreams to attempt to flush.
    * @return        True if all streams were flushed, or false
    *                 if an exception was thrown.
    */
   public static boolean flush(final OutputStream[] streams) {
      boolean success = true;
      for (int i=0; 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);
   }
}