ResourceWriter.java

package org.andromda.core.common;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

/**
 * Used for writing resources for the framework. Also keeps histories of
 * previous resources generated so that we can avoid regenerating if the
 * generated resources are current.
 *
 * @author Chad Brandon
 * @author Bob Fields
 */
public class ResourceWriter
{
    /**
     * The shared instance
     */
    private static final ResourceWriter instance = new ResourceWriter();

    /**
     * Gets the shared ResourceWriter instance. Normally you'll want to retrieve
     * the instance through this method.
     *
     * @return the shared instance.
     */
    public static ResourceWriter instance()
    {
        return instance;
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param file the file to which to write.
     * @param namespace the current namespace for which this resource is being
     *        written.
     * @throws IOException
     */
    public void writeStringToFile(
        final String string,
        final File file,
        final String namespace)
        throws IOException
    {
        ExceptionUtils.checkNull(
            "file",
            file);
        this.writeStringToFile(
            string,
            file.toString(),
            namespace,
            true);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param fileLocation the location of the file which to write.
     * @throws IOException
     */
    public void writeStringToFile(
        final String string,
        final String fileLocation)
        throws IOException
    {
        this.writeStringToFile(
            string,
            fileLocation,
            true);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param file the file which to write.
     * @throws IOException
     */
    public void writeStringToFile(
        final String string,
        final File file)
        throws IOException
    {
        this.writeStringToFile(
            string,
            file != null ? file.toString() : null,
            true);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param fileLocation the location of the file which to write.
     * @param recordHistory whether or not the history of the file should be
     *        recorded.
     */
    private void writeStringToFile(
        final String string,
        final String fileLocation,
        final boolean recordHistory)
        throws IOException
    {
        this.writeStringToFile(
            string,
            fileLocation,
            null,
            recordHistory);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param fileLocation the location of the file which to write.
     * @param namespace the current namespace for which this resource is being
     *        written.
     * @throws IOException
     */
    public void writeStringToFile(
        final String string,
        final String fileLocation,
        final String namespace)
        throws IOException
    {
        this.writeStringToFile(
            string,
            fileLocation,
            namespace,
            true);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param string the string to write to the file
     * @param fileLocation the location of the file which to write.
     * @param namespace the current namespace for which this resource is being
     *        written.
     * @param recordHistory whether or not the history of this file should be
     *        recorded.
     * @throws IOException
     */
    private void writeStringToFile(
        String string,
        final String fileLocation,
        final String namespace,
        final boolean recordHistory)
        throws IOException
    {
        if (string == null)
        {
            string = "";
        }
        ExceptionUtils.checkEmpty(
            "fileLocation",
            fileLocation);

        ResourceUtils.makeDirectories(fileLocation);
        final Merger merger = Merger.instance();
        if (merger.requiresMerge(namespace))
        {
            string = Merger.instance().getMergedString(
                    string,
                    namespace);
        }

        final File file = new File(fileLocation);
        FileUtils.writeStringToFile(file, string, this.encoding);

        if (recordHistory)
        {
            this.recordHistory(file);
        }
    }

    /**
     * Writes the URL contents to a file specified by the fileLocation argument.
     *
     * @param url the URL to read
     * @param fileLocation the location which to write.
     * @throws IOException
     */
    public void writeUrlToFile(
        final URL url,
        final String fileLocation)
        throws IOException
    {
        ResourceUtils.writeUrlToFile(url, fileLocation);
        this.recordHistory(new File(fileLocation));
    }

    /**
     * Stores the encoding to be used for output.
     */
    private String encoding = null;

    /**
     * Sets the encoding to which all output written from this class will be
     * written.
     *
     * @param encoding the encoding type (UTF-8, ISO-8859-1, etc).
     */
    public void setEncoding(String encoding)
    {
        this.encoding = StringUtils.trimToNull(encoding);
    }

    private StringBuffer history = new StringBuffer();

    /**
     * Resets the a history file, to write the history {@link #writeHistory()} must be called.
     *
     * @param modelUri used to construct the file name from the modelUri where the history is stored
     */
    public void resetHistory(final String modelUri)
    {
        String modelFile = modelUri.replace(
                '\\',
                '/');
        int lastSlash = modelFile.lastIndexOf('/');
        if (lastSlash != -1)
        {
            modelFile = modelFile.substring(
                    lastSlash + 1,
                    modelFile.length());
        }
        this.modelFile = modelFile;
        this.history = new StringBuffer();
        this.writtenCount = 0;
    }

    private String modelFile = null;

    /**
     * Stores the count of the resources written over this instance's history.
     */
    private long writtenCount = 0;

    /**
     * Gets the number of currently written resources over the course of this instance's history.
     *
     * @return the number of written resources.
     */
    public long getWrittenCount()
    {
        return this.writtenCount;
    }

    /**
     * The location to which history is written.
     */
    //private static final String HISTORY_LOCATION = Constants.TEMPORARY_DIRECTORY + "history/";
    private String historyDir = null;

    /**
     * Gets the file history storage location.
     * @return model generation history storage location
     */
    public String getHistoryStorage()
    {
        return this.historyDir + '/' + this.modelFile;
    }

    /**
     * Sets the file history storage location.
     * @param historyDirIn the history file storage location
     */
    public void setHistoryStorage(String historyDirIn)
    {
        this.historyDir = historyDirIn;
    }

    /**
     * Writes the output history to disk.
     *
     * @throws IOException
     */
    public void writeHistory()
        throws IOException
    {
        writeStringToFile(
            history.toString(),
            getHistoryStorage(),
            false);
    }

    /**
     * Writes the string to the file specified by the fileLocation argument.
     *
     * @param file the file to which to record the history to
     */
    private void recordHistory(File file)
    {
        // Resource files may be merged multiple times to the temp directory, before the final output file is written
        if (this.history != null && !file.getName().endsWith(".vsl"))
        {
            this.history.append(file).append(',');
        }
        this.writtenCount++;
    }

    /**
     * Checks to see if the history is before the given <code>time</code>.
     *
     * @param time the time in milliseconds to check against.
     * @return true/false
     */
    public boolean isHistoryBefore(long time)
    {
        boolean before = true;
        try
        {
            final File historyFile = new File(getHistoryStorage());
            if (historyFile.exists() && historyFile.lastModified() >= time)
            {
                final String history = ResourceUtils.getContents(new File(getHistoryStorage()).toURI().toURL());
                final String[] fileNames = history.split(",");
                long lastModified = 0;
                for (String fileName : fileNames)
                {
                    if (StringUtils.isNotBlank(fileName))
                    {
                        File file = new File(fileName.trim());

                        // if we find one file that doesn't exist then
                        // before is automatically false
                        if (!file.exists())
                        {
                            lastModified = 0;
                            break;
                        }
                        if (file.lastModified() > lastModified)
                        {
                            lastModified = file.lastModified();
                        }
                    }
                }
                before = time > lastModified;
            }
        }
        catch (IOException ex)
        {
            before = true;
        }
        return before;
    }
}