/*
 * @(#)FileUtilities.java    2.4.0   24 August 2005
 *
 * Copyright 2005
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.util;

import edu.neu.ccs.*;

import java.io.*;
import java.util.*;

/**
 * <P>Provides utility methods that encapsulate file
 * input and output operations.</P>
 *
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.4.0
 * @since   1.0
 */
public class FileUtilities {
    
    /** Prevent instantiation. */
    private FileUtilities() { }
    
    
    /**
     * Copies the file at the source path to the destination path.
     *
     * @param source the path of the source file to copy
     * @param destination the path to copy the file to
     * @param overwrite whether or not to automatically overwrite
     *      existing file contents
     * @throws FileExistsException if the file exists 
     *      and is not to be automatically overwritten
     * @throws IOException if there is an error 
     *      reading the source file or writing the destination file
     * @see #copyFile(File, File, boolean)
     */
    public static void copyFile(
            String source, 
            String destination, 
            boolean overwrite) 
        throws FileExistsException, IOException 
    {
        copyFile(new File(source), new File(destination), overwrite);
    }
    
    
    /**
     * Copies the source file to the destination file.
     *
     * @param source a source file to be copied
     * @param destination the desination file to be written
     * @param overwrite whether or not to automatically overwrite
     *      existing file contents
     * @throws FileExistsException if the file exists 
     *      and is not to be automatically overwritten
     * @throws IOException if there is an error 
     *      reading the source file or writing the destination file
     * @see #copyFile(String, String, boolean)
     */
    public static void copyFile(
            File source, 
            File destination, 
            boolean overwrite) 
        throws FileExistsException, IOException 
    {
        // check for existing file at the destination
        if (destination.exists() && !overwrite)
            throw new FileExistsException(destination);

        // create necessary objects
        FileInputStream in  = new FileInputStream(source);
        FileOutputStream out = new FileOutputStream(destination);
        byte[] buffer  = new byte[4096];

        // read/write bytes directly
        int i = in.read(buffer, 0, buffer.length);
        while (i > -1) {
            out.write(buffer, 0, i);
            i = in.read(buffer, 0, buffer.length);
        }
        
        // close the streams
        in.close();
        out.close();
    }
    
    
    /**
     * Creates the given directory.
     *
     * @param dir the directory to be created
     * @throws FileExistsException if the file or directory 
     *      already exists
     * @throws IOException if there is an error
     *      creating the directory
     * @see #createDirectory(String)
     */
    public static void createDirectory(File dir) 
        throws FileExistsException, IOException 
    {
        if (dir.exists())
            throw new FileExistsException(dir);
            
        if (!dir.mkdirs())
            throw new IOException();
    }
    
    
    /**
     * Creates a directory with the given path.
     *
     * @param dir the full path of the new directory
     * @throws FileExistsException if the file or directory
     *      already exists
     * @throws IOException if there is an error
     *      creating the directory
     * @see #createDirectory(File)
     */
    public static void createDirectory(String dir) 
        throws FileExistsException, IOException 
    {
        createDirectory(new File(dir));
    }
    
    
    /**
     * Deletes the given file.
     *
     * @param f the file to be deleted
     * @throws FileNotFoundException if the file 
     *      does not exist
     * @throws IOException if there was an error
     *      deleting the file
     * @see #deleteFile(String)
     */
    public static void deleteFile(File f) 
        throws FileNotFoundException, IOException 
    {
        if (!f.exists())
            throw new FileNotFoundException();
            
        if (!f.delete())
            throw new IOException();
    }
    
    
    /**
     * Deletes the file at the given path.
     *
     * @param path the path of the file to be deleted
     * @throws FileNotFoundException if the file 
     *      does not exist
     * @throws IOException if there was an error
     *      deleting the file
     * @see #deleteFile(File)
     */
    public static void deleteFile(String path) 
        throws FileNotFoundException, IOException 
    {
        deleteFile(new File(path));
    }
    
    
    /**
     * Returns an array of <CODE>String</CODE>s 
     * containing the names of the files
     * in the given directory.
     *
     * @param f the directory to be read  
     * @throws FileNotFoundException if the directory 
     *      does not exist
     * @throws IOException if there was an error
     *      reading the directory
     * @see #readDirectory(String)
     */
    public static String[] readDirectory(File f) 
        throws FileNotFoundException, IOException 
    {
        if (!f.exists())
            throw new FileNotFoundException();
            
        if (!f.isDirectory() || !f.canRead())
            throw new IOException();
            
        return f.list();
    }
    
    
    /**
     * Returns an array of <CODE>String</CODE>s 
     * containing the names of the files
     * in the directory at the given path.
     *
     * @param path the full path of the directory
     * @throws FileNotFoundException if the directory 
     *      does not exist
     * @throws IOException if there was an error
     *      reading the directory
     * @see #readDirectory(File)
     */
    public static String[] readDirectory(String path) 
        throws FileNotFoundException, IOException 
    {
        return readDirectory(new File(path));
    }
    
    
    /**
     * <p>Assuming the given file list array contains a list of file names,
     * return the sub-list of those names whose extensions match those in
     * the given extensions list; the extensions list consists of an array
     * of file extensions; case is ignored in the comparison.</p>
     *
     * <p>Returns <code>null</code> if the file list is <code>null</code>.</p>
     *
     * <p>Returns the file list, if the extensions list is <code>null</code>.</p>
     *
     * @param fileList the file list to filter
     * @param extensionList the extensions to define the filter
     */
    public static String[] filterFileList
        (String[] fileList, String[] extensionList)
    {
        if (fileList == null)
            return null;
        
        if (extensionList == null)
            return fileList;
        
        Vector vector = new Vector();
        
        int length1 = fileList.length;
        int length2 = extensionList.length;
        
        for (int j = 0; j < length2; j++) {
            for (int i = 0; i < length1; i++) {
                if (matchesExtension(fileList[i], extensionList[j]))
                    vector.add(fileList[i]);
            }
        }
        
        return (String[]) vector.toArray(new String[0]);
    }
    
    
    /**
     * <p>Assuming the given file list array contains a list of file names,
     * return the sub-list of those names whose extensions match those in
     * the given extensions list; the extensions list is a comma separated
     * list that is parsed to extract the extensions; case is ignored in
     * the comparison.</p>
     *
     * <p>Returns <code>null</code> if the file list is <code>null</code>.</p>
     *
     * <p>Returns the file list, if the extensions list is <code>null</code>.</p>
     *
     * @param fileList the file list to filter
     * @param extensionList the extensions in a comma separated list
     */
    public static String[] filterFileList
        (String[] fileList, String extensionList)
    {
        if (extensionList == null)
            return fileList;
        
        return filterFileList(fileList, Strings.splitCommaList(extensionList));
    }
    
    
    /**
     * <p>Returns true if the given file name matches the given file extension
     * with case ignored.</p>
     *
     * <p>Returns false if either argument is <code>null</code> or if the file
     * extension string is of length 0.</p>
     *
     * @param fileName the file name
     * @param fileExtension the extension to match
     */
    public static boolean matchesExtension
        (String fileName, String fileExtension)
    {
        if ((fileName == null) || (fileExtension == null))
            return false;
        
        int size1 = fileName.length();
        int size2 = fileExtension.length();
        
        if (size2 == 0)
            return false;
        
        int offset = size1 - size2;
        
        return fileName.regionMatches(true, offset, fileExtension, 0, size2);
    }
    
    
    /**
     * Returns a <CODE>StringBuffer</CODE> 
     * containing the contents of the given file.
     *
     * @param f the file to be read
     * @throws FileNotFoundException if the file 
     *      does not exist
     * @throws IOException if there was an error
     *      reading the file
     * @see #readFile(File)
     * @see #readFile(String)
     */
    public static StringBuffer getFileContents(File f) 
        throws FileNotFoundException, IOException 
    {
        if (!f.exists())
            throw new FileNotFoundException();
            
        if (!f.isFile() || !f.canRead())
            throw new IOException();
            
        StringBuffer    data   = new StringBuffer((int)f.length());
        byte[]          buffer = new byte[4096];
        FileInputStream stream = new FileInputStream(f);

        int in = stream.read(buffer);
        while (in > 0) {
            data.append(new String(buffer, 0, in));
            in = stream.read(buffer);
        }
        
        stream.close();
        return data;
    }
    
    
    /**
     * Returns a <CODE>String</CODE> 
     * containing the contents of the given file.
     *
     * @param f the file to be read
     * @throws FileNotFoundException if the file 
     *      does not exist
     * @throws IOException if there was an error
     *      reading the file
     * @see #readFile(String)
     */
    public static String readFile(File f) 
        throws FileNotFoundException, IOException 
    {
        return getFileContents(f).toString();
    }
    
    
    /**
     * Returns a <CODE>String</CODE> 
     * containing the contents of the file
     * at the given path.
     *
     * @param path the path of the file to be read
     * @throws FileNotFoundException if the file 
     *      does not exist
     * @throws IOException if there was an error
     *      reading the file
     * @see #readFile(File)
     */
    public static String readFile(String path) 
        throws FileNotFoundException, IOException 
    {
        return getFileContents(new File(path)).toString();
    }
    
    
    /**
     * Sets the name of the given file 
     * to the provided name.
     *
     * @param source the file to be renamed
     * @param destination the new name for the file
     * @throws FileNotFoundException if the source file 
     *      is not found
     * @throws FileExistsException if the destination file 
     *      already exists and will be overwritten
     * @throws IOException if there is an error
     *      renaming the file
     * @see #renameFile(String, String)
     */
    public static void renameFile(File source, File destination)
        throws FileNotFoundException, 
            FileExistsException,
            IOException 
    {
        if (!source.exists())
            throw new FileNotFoundException();
            
        if (destination.exists())
            throw new FileExistsException(destination);
            
        if (!source.renameTo(destination))
            throw new IOException();
    }
    
    
    /**
     * Renames the file at the given path 
     * to the given new path.
     *
     * @param source the full path of the file 
     *      to be renamed
     * @param destination the full path of the new name 
     *      for the file
     * @throws FileNotFoundException if a file at 
     *      the source path does not exist
     * @throws FileExistsException if a file exists 
     *      at the destination path and will be overwritten
     * @throws IOException if there is an error
     *      renaming the file
     * @see #renameFile(File, File)
     */
    public static void renameFile(String source, String destination)
        throws FileNotFoundException,
            FileExistsException,
            IOException 
    {
        renameFile(new File(source), new File(destination));
    }
    
    
    /**
     * Writes the given data to the given file.
     *
     * @param f the file to be written
     * @param data the data to write to the file
     * @param overwrite whether or not to automatically 
     *      overwrite existing file contents
     * @throws FileExistsException if the file exists 
     *      and is not to be automatically overwritten
     * @throws IOException if there is an error
     *      writing the file
     * @see #writeFile(String, String, boolean)
     */
    public static void writeFile(
        File f, 
            String data, 
            boolean overwrite) 
        throws FileExistsException,
            IOException 
    {
        if (f.exists() && !overwrite)
            throw new FileExistsException(f);
            
        FileOutputStream stream = new FileOutputStream(f);
        stream.write(data.getBytes());
        stream.close();
    }
    
    
    /**
     * Writes the given data to a file 
     * at the given path.
     *
     * @param destination the full path of the file 
     *      to be written
     * @param data the data to write to the file
     * @param overwrite whether or not to automatically
     *      overwrite existing file contents
     * @throws FileExistsException if the file exists 
     *      and is not to be automatically overwritten
     * @throws IOException if there is an error
     *      writing the file
     * @see #writeFile(File, String, boolean)
     */
    public static void writeFile(
        String destination, 
            String data, 
            boolean overwrite) 
        throws FileExistsException,
            IOException 
    {
        writeFile(new File(destination), data, overwrite);
    }
    
}
