All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.console.editor.BufferImplementation Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2004 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.console.editor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.EventListener;

import net.grinder.common.Closer;
import net.grinder.common.UncheckedInterruptedException;
import net.grinder.console.common.DisplayMessageConsoleException;
import net.grinder.console.common.Resources;
import net.grinder.util.ListenerSupport;


/**
 * Implementation of {@link Buffer}.
 *
 * @author Philip Aston
 */
final class BufferImplementation implements Buffer {

  private final Resources m_resources;
  private final TextSource m_textSource;

  private final ListenerSupport m_listeners =
    new ListenerSupport();

  private String m_name;
  private File m_file;

  private long m_lastModified = -1;

  /**
   * Constructor for buffers with no associated file.
   *
   * @param resources Console resources.
   * @param textSource The text editor.
   */
  BufferImplementation(Resources resources,
                       TextSource textSource,
                       String name) {
    m_resources = resources;
    m_textSource = textSource;
    m_file = null;
    m_name = name;
  }

  /**
   * Constructor for buffers with an associated file.
   *
   * @param resources Console resources.
   * @param textSource The text editor.
   * @param file The file.
   */
  BufferImplementation(Resources resources, TextSource textSource, File file) {
    m_resources = resources;
    m_textSource = textSource;
    setFile(file);
  }

  /**
   * Return the buffer's {@link TextSource}.
   *
   * @return The text source.
   */
  public TextSource getTextSource() {
    return m_textSource;
  }

  /**
   * Update the text source from the file.
   *
   * @exception DisplayMessageConsoleException If the file could not
   * be read from.
   * @exception EditorException If an unexpected problem occurs.
   */
  public void load() throws DisplayMessageConsoleException, EditorException {
    // Should never be called if there is no associated file, but
    // check anyway.
    if (m_file == null) {
      throw new EditorException(
        "Can't load a buffer that has no associated file");
    }

    final StringWriter stringWriter = new StringWriter();
    BufferedReader reader = null;

    try {
      // We use a BufferedReader to canonicalise line endings
      reader = new BufferedReader(new FileReader(m_file));

      while (true) {
        final String line = reader.readLine();

        if (line == null) {
          break;
        }

        stringWriter.write(line);
        stringWriter.write('\n');
      }
    }
    catch (IOException e) {
      UncheckedInterruptedException.ioException(e);

      throw new DisplayMessageConsoleException(
        m_resources,
        "fileReadError.text",
        new Object[] { m_file,
                       ".\n(" + extractReasonFromIOException(e) + ")",
        },
        e);
    }
    finally {
      Closer.close(reader);
    }

    m_textSource.setText(stringWriter.toString());

    m_lastModified = m_file.lastModified();
  }

  /**
   * Update the buffer's file from the text source.
   *
   * @exception DisplayMessageConsoleException If the file could not
   * be written to.
   * @exception EditorException If an unexpected problem occurs.
   */
  public void save() throws DisplayMessageConsoleException, EditorException {
    // The UI should never call save if there is no associated file,
    // but check anyway.
    if (m_file == null) {
      throw new EditorException(
        "Can't save a buffer that has no associated file");
    }

    save(m_file);
  }

  /**
   * Update a file from the text source and, if successful, associate
   * the buffer with the new file.
   *
   * @param file The file.
   * @exception DisplayMessageConsoleException If the file could not
   * be written to.
   */
  public void save(File file) throws DisplayMessageConsoleException {
    final File oldFile = getFile();

    Writer fileWriter = null;

    try {
      // Calling getText() causes the text source to be set to "clean"
      // and a buffer changed event to be fired by the EditorModel.
      final String text = m_textSource.getText();

      // Line-oriented output using the platform line ending. In the future,
      // we may try to preserve the predominant line ending of the original
      // input.
      final String[] lines = text.split("\n", -1);

      fileWriter = new FileWriter(file);
      final PrintWriter printWriter = new PrintWriter(fileWriter);

      for (int i = 0; i < lines.length; ++i) {
        printWriter.println(lines[i]);
      }

      setFile(file);
      printWriter.close();      // Close necessary to ensure last
                                // modified time is updated?
      m_lastModified = m_file.lastModified();

      m_listeners.apply(
        new ListenerSupport.Informer() {
          public void inform(Listener l) {
            l.bufferSaved(BufferImplementation.this, oldFile);
          }
        });
    }
    catch (IOException e) {
      UncheckedInterruptedException.ioException(e);

      throw new DisplayMessageConsoleException(
        m_resources,
        "fileWriteError.text",
        new Object[] { m_file,
                       "./n(" + extractReasonFromIOException(e) + ")",
        },
        e);
    }
    finally {
      Closer.close(fileWriter);
    }
  }

  /**
   * Return whether the buffer's text has been changed since the last
   * save.
   *
   * @return true => the text has changed.
   */
  public boolean isDirty() {
    return m_textSource.isDirty();
  }

  private void setFile(File file) {
    m_file = file;
    m_name = file.getName();
  }

  /**
   * Return the buffer's associated file.
   *
   * @return The file. null if there is no associated file.
   */
  public File getFile() {
    return m_file;
  }

  /**
   * Return whether the file has been independently modified since the
   * last save.
   *
   * @return true => the file has changed independently
   * of the buffer.
   */
  public boolean isUpToDate() {
    return m_file == null || m_lastModified == m_file.lastModified();
  }

  /**
   * Get the type of the buffer.
   *
   * @return The buffer's type.
   */
  public Type getType() {

    if (m_file != null) {
      final String name = m_file.getName();
      final int lastDot = name.lastIndexOf('.');

      if (lastDot >= 0) {
        final String extension = name.substring(lastDot + 1);
        return Type.forExtension(extension);
      }
    }

    return Type.TEXT_BUFFER;
  }

  /**
   * Return display name of buffer.
   *
   * @return The buffer's name.
   */
  public String getDisplayName() {
    return m_name;
  }

    /**
     * Useful for debugging.
     *
     * @return Description of the Buffer.
     */
  @Override public String toString() {
    return "";
  }

  /**
   * Add a new listener.
   *
   * @param listener The listener.
   */
  public void addListener(Listener listener) {
    m_listeners.add(listener);
  }

  /**
   * Interface for listeners.
   */
  public interface Listener extends EventListener {

    /**
     * Called when a buffer has been saved.
     *
     * @param buffer The buffer.
     * @param oldFile The File the buffer was previously associated with.
     */
    void bufferSaved(Buffer buffer, File oldFile);
  }

  /**
   * Protected for unit tests.
   */
  static String extractReasonFromIOException(IOException e) {
    if (e instanceof FileNotFoundException) {
      final String message = e.getMessage();

      final int firstParenthesis = message.indexOf('(');
      final int secondsParenthesis = message.indexOf(')', firstParenthesis);

      if (firstParenthesis >= 0 && secondsParenthesis > firstParenthesis + 1) {
        return message.substring(firstParenthesis + 1, secondsParenthesis);
      }
    }

    return "";
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy