org.acmsl.commons.utils.io.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.acmsl.commons.utils.io.FileUtils.java

Source

//;-*- mode: java -*-
/*
                    ACM-SL Commons
    
Copyright (C) 2002-today  Jose San Leandro Armendariz
                          chous@acm-sl.org
    
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
    
This library 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
General Public License for more details.
    
You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-307  USA
    
Thanks to ACM S.L. for distributing this library under the LGPL license.
Contact info: jose.sanleandro@acm-sl.com
    
 ******************************************************************************
 *
 * Filename: FileUtils.java
 *
 * Author: Jose San Leandro Armendariz
 *
 * Description: Provides some useful methods when working with files.
 *
 */
package org.acmsl.commons.utils.io;

/*
 * Importing some ACM-SL classes.
 */
import org.acmsl.commons.Literals;
import org.acmsl.commons.patterns.Singleton;
import org.acmsl.commons.patterns.Utils;
import org.acmsl.commons.regexpplugin.Helper;
import org.acmsl.commons.regexpplugin.MalformedPatternException;
import org.acmsl.commons.regexpplugin.RegexpEngine;
import org.acmsl.commons.regexpplugin.RegexpEngineNotFoundException;
import org.acmsl.commons.regexpplugin.RegexpManager;
import org.acmsl.commons.regexpplugin.RegexpPluginMisconfiguredException;

/*
 * Importing some JDK1.3 classes.
 */
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.nio.channels.Channel;
import java.nio.charset.Charset;

/*
 * Importing Apache Commons-Logging classes.
 */
import org.apache.commons.logging.LogFactory;

/*
 * Importing JetBrains annotations.
 */
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Provides some useful methods when working with files.
 * @author <a href="mailto:chous@acm-sl.org"
       >Jose San Leandro Armendariz</a>
 */
public class FileUtils implements Utils, Singleton {
    /**
     * Retrieves the {@link File} associated to given {@link FileInputStream}, if possible.
     * @param inputStream the input stream.
     * @return the file.
     */
    @Nullable
    public File retrieveFile(@NotNull final FileInputStream inputStream) {
        @Nullable
        final File result;

        @Nullable
        String t_strPath = null;

        try {
            @Nullable
            final Field field = inputStream.getClass().getDeclaredField("path");
            field.setAccessible(true);
            t_strPath = (String) field.get(inputStream);
        } catch (@NotNull final Throwable noField) {
            System.err.println(noField.getMessage());
        }

        if (t_strPath == null) {
            result = null;
        } else {
            result = new File(t_strPath);
        }

        return result;
    }

    /**
     * Retrieves the file name from an absolute path.
     * @param filePath the path.
     * @return just the file name (no folder information).
     */
    @NotNull
    public String getFileName(@NotNull final String filePath) {
        @NotNull
        final String result;

        final int lastSeparatorPosition = filePath.lastIndexOf(File.separator);

        if (lastSeparatorPosition > -1) {
            if (lastSeparatorPosition < filePath.length() - 1) {
                result = filePath.substring(lastSeparatorPosition + 1);
            } else {
                result = "";
            }
        } else {
            result = filePath;
        }

        return result;
    }

    /**
     * Strips the last extension.
     * @param filePath the file path.
     * @return the file path, after removing the last extension.
     */
    @NotNull
    public String stripExtension(@NotNull final String filePath) {
        @NotNull
        final String result;

        final int lastDotPosition = filePath.lastIndexOf(".");

        if (lastDotPosition > -1) {
            result = filePath.substring(0, lastDotPosition);
        } else {
            result = filePath;
        }

        return result;
    }

    /**
     * Removes all extensions from a given file name.
     * @param filePath the path.
     * @return the file path, with no extensions.
     */
    @NotNull
    public String stripExtensions(@NotNull final String filePath) {
        @NotNull
        final String result;

        @NotNull
        String aux = filePath;
        @NotNull
        String aux2;

        do {
            aux2 = aux;
            aux = stripExtension(aux);
        } while (!aux.equals(aux2));

        result = aux;

        return result;
    }

    /**
     * Singleton implemented to avoid the double-checked locking.
     */
    private static class FileUtilsSingletonContainer {
        /**
         * The actual singleton.
         */
        @NotNull
        public static final FileUtils SINGLETON = new FileUtils();
    }

    /**
     * Retrieves a FileUtils instance.
     * @return such instance.
     */
    @NotNull
    public static FileUtils getInstance() {
        return FileUtilsSingletonContainer.SINGLETON;
    }

    /**
     * Protected constructor to avoid accidental instantiation.
     */
    protected FileUtils() {
    }

    /**
     * Reads a file pointed by given path, and returns its contents.
     * @param filePath the path to the file to be read.
     * @param charset the {@link Charset}.
     * @return the contents of the file.
     * @throws FileNotFoundException if the file is not found.
     * @throws SecurityException if the operation is forbidden because of
     * security manager settings.
     * @throws IOException if some I/O exception occurs.
     */
    @NotNull
    public String readFile(@NotNull final String filePath, @NotNull final Charset charset)
            throws SecurityException, IOException {
        return readFile(new File(filePath), charset);
    }

    /**
     * Reads a file and returns its contents.
     * @param file the file to be read.
     * @param charset the {@link Charset}.
     * @return the contents of the file.
     * @throws FileNotFoundException if the file is not found.
     * @throws SecurityException if the operation is forbidden because of
     * security manager settings.
     * @throws IOException if some I/O exception occurs.
     */
    @NotNull
    public char[] readFileContents(@NotNull final File file, @NotNull final Charset charset)
            throws SecurityException, IOException {
        @NotNull
        final char[] result;

        @Nullable
        FileInputStream t_fisFileStream = null;

        @Nullable
        InputStreamReader t_isFileReader = null;

        /*
         * To read file's contents it's better to use BufferedReader class.
         */
        @Nullable
        BufferedReader t_frPageBufferedReader = null;

        try {
            /*
             * Instantiate a FileReader object to read file's contents.
             */
            t_fisFileStream = new FileInputStream(file);

            t_isFileReader = new InputStreamReader(t_fisFileStream, charset);

            /*
             * To read file's contents it's better to use BufferedReader class.
             */
            t_frPageBufferedReader = new BufferedReader(t_isFileReader);

            if (file.length() > Integer.MAX_VALUE) {
                throw new IOException("File too large (" + file.length() + " bytes)");
            }

            /*
             * Next, I find out the necessary size of the array where file's
             * contents will be copied into.
             */
            result = new char[(int) file.length()];

            /*
             * Now I actually read the file, and fill the array.
             */
            t_frPageBufferedReader.read(result, 0, result.length);
        } finally {
            if (t_frPageBufferedReader != null) {
                try {
                    t_frPageBufferedReader.close();
                } catch (final IOException cannotCloseStream) {
                    LogFactory.getLog(FileUtils.class).warn("Cannot close file", cannotCloseStream);
                }
            }
            if (t_isFileReader != null) {
                try {
                    t_isFileReader.close();
                } catch (final IOException cannotCloseStream) {
                    LogFactory.getLog(FileUtils.class).warn("Cannot close file", cannotCloseStream);
                }
            }
            if (t_fisFileStream != null) {
                try {
                    t_fisFileStream.close();
                } catch (final IOException cannotCloseStream) {
                    LogFactory.getLog(FileUtils.class).warn("Cannot close file", cannotCloseStream);
                }
            }
        }
        return result;
    }

    /**
     * Reads a file and returns its contents.
     * @param file the file to be read.
     * @param charset the {@link Charset}.
     * @return the contents of the file.
     * @throws FileNotFoundException if the file is not found.
     * @throws SecurityException if the operation is forbidden because of
     * security manager settings.
     * @throws IOException if some I/O exception occurs.
     */
    @NotNull
    public String readFile(@NotNull final File file, @NotNull final Charset charset)
            throws SecurityException, IOException {
        /*
         * we can use a String constructor that exactly
         * fits our needs.
         */
        return new String(readFileContents(file, charset));
    }

    /**
     * Reads a file by its path and returns its contents, if possible. If some
     * exception occurs, it's ignored, and returns an empty String. This method
     * is used to avoid declaring try/catch blocks in client code.
     * @param filePath the path to the file to be read.
     * @param charset the {@link Charset}.
     * @return the contents of the file, or empty if reading cannot be
     * accomplished.
     */
    @SuppressWarnings("unused")
    @NotNull
    public String readFileIfPossible(@NotNull final String filePath, @NotNull final Charset charset) {
        String result = null;

        try {
            result = readFile(filePath, charset);
        } catch (final FileNotFoundException fileNotFoundException) {
            /*
            * We have chosen not to notify of exceptions, so this
            * block of code is only descriptive.
            */
            LogFactory.getLog(FileUtils.class).trace("Cannot read file " + filePath, fileNotFoundException);
        } catch (final SecurityException securityException) {
            /*
            * We have chosen not to notify of exceptions, so this
            * block of code is only descriptive.
            */
            LogFactory.getLog(FileUtils.class).info("Cannot read file " + filePath, securityException);
        } catch (final IOException ioException) {
            /*
            * We have chosen not to notify of exceptions, so this
            * block of code is only descriptive.
            */
            LogFactory.getLog(FileUtils.class).info("Cannot read file " + filePath, ioException);
        }

        if (result == null) {
            result = "";
        }

        return result;
    }

    /**
     * Reads a file and returns its contents, if possible. If some exception
     * occurs, it's ignored, and returns an empty String. This method is used
     * to avoid declaring try/catch blocks in client code.
     * @param file the file to be read.
     * @param charset the {@link Charset}.
     * @return the contents of the file, or empty if reading cannot be
     * accomplished.
     */
    @SuppressWarnings("unused")
    @Nullable
    public String readFileIfPossible(@NotNull final File file, @NotNull final Charset charset) {
        @Nullable
        String result = null;

        try {
            result = readFile(file, charset);
        } catch (final FileNotFoundException fileNotFoundException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).trace("Cannot read file " + file, fileNotFoundException);
        } catch (final SecurityException securityException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot read file " + file, securityException);
        } catch (final IOException ioException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot read file " + file, ioException);
        }

        return result;
    }

    /**
     * Tries to read from given stream.
     * @param inputStream the stream to read from.
     * @return the contents read.
     * @throws IOException if the input stream could not be read or closed.
     */
    @NotNull
    public String read(@NotNull final InputStream inputStream) throws IOException {
        @NotNull
        final BufferedInputStream bufferedInputStream;

        if (inputStream instanceof BufferedInputStream) {
            bufferedInputStream = (BufferedInputStream) inputStream;
        } else {
            bufferedInputStream = new BufferedInputStream(inputStream);
        }

        return read(bufferedInputStream);
    }

    /**
     * Tries to read from given stream.
     * @param bufferedInputStream the stream to read from.
     * @return the contents read.
     * @throws IOException if the input stream could not be read or closed.
     */
    @NotNull
    public String read(@NotNull final BufferedInputStream bufferedInputStream) throws IOException {
        @NotNull
        final StringWriter t_swResult = new StringWriter();

        @NotNull
        final PrintWriter t_Writer = new PrintWriter(t_swResult);

        int t_iReadChar = bufferedInputStream.read();

        while (t_iReadChar != -1) {
            t_Writer.print((char) t_iReadChar);

            t_iReadChar = bufferedInputStream.read();
        }

        bufferedInputStream.close();

        t_Writer.close();

        return t_swResult.toString();
    }

    /**
     * Saves the contents to a file.
     * @param filePath the path of the file.
     * @param contents the text to save.
     * @param charset the file charset to use.
     * @return <code>true</code> if the process is successfully accomplished.
     */
    @SuppressWarnings("unused")
    public boolean writeFileIfPossible(@NotNull final String filePath, @NotNull final String contents,
            @NotNull final Charset charset) {
        return writeFileIfPossible(new File(filePath), contents, charset);
    }

    /**
     * Saves the contents to a file.
     * @param file the file to be overwritten.
     * @param contents the text to save.
     * @param charset the file charset to use.
     * @return <code>true</code> if the process is successfully accomplished.
     */
    public boolean writeFileIfPossible(@NotNull final File file, @NotNull final String contents,
            @NotNull final Charset charset) {
        boolean result = false;

        try {
            writeFile(file, contents, charset);
            result = true;
        } catch (final FileNotFoundException fileNotFoundException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot write file " + file, fileNotFoundException);
        } catch (final SecurityException securityException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot write file " + file, securityException);
        } catch (final IOException ioException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot write file " + file, ioException);
        }

        return result;
    }

    /**
     * Writes a file referred by given path, with given contents.
     * @param filePath the path of the file.
     * @param contents the text to write.
     * @param charset the file charset to use.
     * @throws FileNotFoundException if the file is not found.
     * @throws SecurityException if the security manager forbids this
     * operation.
     * @throws IOException if any other I/O error occurs.
     */
    @SuppressWarnings("unused")
    public void writeFile(@NotNull final String filePath, @NotNull final String contents,
            @NotNull final Charset charset) throws SecurityException, IOException {
        writeFile(new File(filePath), contents, charset);
    }

    /**
     * Writes a file with given contents.
     * @param file the file to write.
     * @param contents the text to write.
     * @param charset the charset to use.
     * @throws SecurityException if the security manager forbids this
     * operation.
     * @throws IOException if any other I/O error occurs.
     */
    public void writeFile(@NotNull final File file, @NotNull final String contents, @NotNull final Charset charset)
            throws SecurityException, IOException {
        @Nullable
        FileOutputStream t_fosFileStream = null;

        @Nullable
        OutputStreamWriter t_osFileWriter = null;

        @Nullable
        final PrintWriter t_pwWriter;

        try {
            t_fosFileStream = new FileOutputStream(file);

            t_osFileWriter = new OutputStreamWriter(t_fosFileStream, charset);

            t_pwWriter = new PrintWriter(t_osFileWriter);

            t_pwWriter.println(contents);

            t_pwWriter.close();
        } finally {
            if (t_fosFileStream != null) {
                try {
                    t_fosFileStream.close();
                } catch (@NotNull final IOException cannotClose) {
                    LogFactory.getLog(FileUtils.class).error("Cannot close file", cannotClose);
                }
            }
            if (t_osFileWriter != null) {
                try {
                    t_osFileWriter.close();
                } catch (@NotNull final IOException cannotClose) {
                    LogFactory.getLog(FileUtils.class).error("Cannot close file", cannotClose);
                }
            }
        }
    }

    /**
     * Copies the contents of a file to another.
     * @param original the content to copy.
     * @param destination the file to be overwritten.
     * @param charset the {@link Charset}.
     * @throws SecurityException if the security manager forbids this
     * operation.
     * @throws IOException if any other I/O error occurs.
     */
    public void copy(@NotNull final File original, @NotNull final File destination, @NotNull final Charset charset)
            throws SecurityException, IOException {
        writeFile(destination, readFile(original, charset), charset);
    }

    /**
     * Copies the contents of a file (referred by given path) to another.
     * @param originalPath the path of the file to copy.
     * @param destinationPath the path of the file to be overwritten.
     * @param charset the {@link Charset}.
     * @return <code>true</code> if the operation ends up successfully.
     */
    @SuppressWarnings("unused")
    public boolean copyIfPossible(@NotNull final String originalPath, @NotNull final String destinationPath,
            @NotNull final Charset charset) {
        return copyIfPossible(new File(originalPath), new File(destinationPath), charset);
    }

    /**
     * Copies the contents of a file to another.
     * @param original the content to copy.
     * @param destination the file to be overwritten.
     * @param charset the {@link Charset}.
     * @return <code>true</code> if the operation ends up successfully.
     */
    public boolean copyIfPossible(@NotNull final File original, @NotNull final File destination,
            @NotNull final Charset charset) {
        boolean result = false;

        try {
            copy(original, destination, charset);

            result = true;
        } catch (final FileNotFoundException fileNotFoundException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot copy file " + original + " to " + destination,
                    fileNotFoundException);
        } catch (final SecurityException securityException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot copy file " + original + " to " + destination,
                    securityException);
        } catch (final IOException ioException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot copy file " + original + " to " + destination,
                    ioException);
        }

        return result;
    }

    /**
     * Moves a file.
     * @param originalFile the file to move.
     * @param destinationFile the new file.
     * @param charset the {@link Charset}.
     * @throws FileNotFoundException if the file is not found.
     * @throws SecurityException if the security manager forbids this
     * operation.
     * @throws IOException if any other I/O error occurs.
     */
    public void move(@NotNull final File originalFile, @NotNull final File destinationFile,
            @NotNull final Charset charset) throws SecurityException, IOException {
        copy(originalFile, destinationFile, charset);

        if (!originalFile.delete()) {
            throw new IOException("Cannot delete " + originalFile.getAbsolutePath());
        }
    }

    /**
     * Moves a file, if possible.
     * @param originalFile the file to move.
     * @param destinationFile the new file.
     * @param charset the {@link Charset}.
     * @return <code>true</code> if the operation ends up successfully.
     */
    public boolean moveIfPossible(@NotNull final File originalFile, @NotNull final File destinationFile,
            @NotNull final Charset charset) {
        boolean result = false;

        try {
            move(originalFile, destinationFile, charset);
            result = true;
        } catch (final FileNotFoundException fileNotFoundException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot move file " + originalFile + " to " + destinationFile,
                    fileNotFoundException);
        } catch (final SecurityException securityException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot move file " + originalFile + " to " + destinationFile,
                    securityException);
        } catch (final IOException ioException) {
            /*
             * We have chosen not to notify of exceptions, so this
             * block of code is only descriptive.
             */
            LogFactory.getLog(FileUtils.class).info("Cannot move file " + originalFile + " to " + destinationFile,
                    ioException);
        }

        return result;
    }

    /**
     * Moves a file from one path to another, if possible.
     * @param filePath the path of the file to move.
     * @param destinationPath the new file's path.
     * @param charset the {@link Charset}.
     * @return <code>true</code> if the operation ends up successfully.
     */
    @SuppressWarnings("unused")
    public boolean moveIfPossible(@NotNull final String filePath, @NotNull final String destinationPath,
            @NotNull final Charset charset) {
        return moveIfPossible(new File(filePath), new File(destinationPath), charset);
    }

    /**
     * Translates given package name to a relative path.
     * @param packageName the package name.
     * @return the path.
     */
    @Nullable
    public String packageToPath(@NotNull final String packageName) {
        String result = null;

        try {
            @Nullable
            final Helper t_Helper = createHelper(RegexpManager.getInstance());

            if (t_Helper != null) {
                result = t_Helper.replaceAll(packageName, "\\.", File.separator);
            }
        } catch (final MalformedPatternException malformedPatternException) {
            /*
             * This exception is about pattern mismatch. In my opinion,
             * it's an error that should be detected at compile time,
             * but regexp API design cannot provide this functionality,
             * since all patterns are defined with strings, and therefore
             * they escape all compiler checks.
             */
            LogFactory.getLog(FileUtils.class).warn(Literals.MALFORMED_STATIC_PATTERNS_ARE_FATAL_CODING_ERRORS,
                    malformedPatternException);
        } catch (final RegexpEngineNotFoundException regengNotFoundException) {
            /*
             * This exception is thrown only if no regexp library is available
             * at runtime. Not only this one, but any method provided by this
             * class that use regexps will not work.
             */
            LogFactory.getLog(FileUtils.class).fatal(Literals.CANNOT_FIND_ANY_REGEXP_ENGINE,
                    regengNotFoundException);
        } catch (final RegexpPluginMisconfiguredException regexpPluginMisconfiguredException) {
            /*
             * This exception is thrown if RegexpPlugin cannot be configured
             * properly at runtime. Not only this one, but any method provided
             * by thisclass that use regexps will not work.
             */
            LogFactory.getLog(FileUtils.class).fatal("Cannot configure RegexpPlugin.",
                    regexpPluginMisconfiguredException);
        }

        return result;
    }

    /**
     * Creates the helper.
     * @return the regexp helper.
     */
    @Nullable
    protected static synchronized Helper createHelper() {
        return createHelper(RegexpManager.getInstance());
    }

    /**
     * Creates the helper.
     * @param regexpManager the RegexpManager instance.
     * @return the regexp helper.
     */
    @Nullable
    protected static synchronized Helper createHelper(@NotNull final RegexpManager regexpManager) {
        return createHelper(regexpManager.getEngine());
    }

    /**
     * Creates the helper.
     * @param regexpEngine the RegexpEngine instance.
     * @return the regexp helper.
     */
    @Nullable
    protected static synchronized Helper createHelper(@NotNull final RegexpEngine regexpEngine) {
        return regexpEngine.createHelper();
    }
}