package log;

import java.io.*;

/**
  * A stream that can be used to read and process log files.
  *
  * <p>The LogInputStream will take an {@link InputStream} and try to interpret the data from that stream as
  * a log file. You can then obtain the {@link LogEntry LogEntries} in the order they were put into the log
  * by calling {@link #readEntry}.</p>
  *
  * <p><code>LogInputStreams</code> can be filtered in which case you will only see the LogEntries that are
  * {@link LogEntryFilter#accept accepted} by the {@link LogEntryFilter filter}.</p>
  *
  * @author Steffen Zschaler
  * @version 2.0 14/07/1999
  * @since v2.0
  */
public class LogInputStream extends Object {

  /**
    * The input stream that backs this stream.
    */
  protected InputStream m_isSource;

  /**
    * The object input stream that is build on top of the input stream. This objects in this stream are
    * expected to be {@link LogEntry LogEntries}.
    */
  protected ObjectInputStream m_oisSource;

  /**
    * The filter to be applied on the stream, if any.
    */
  protected LogEntryFilter m_lefFilter;

  /**
    * Create a new LogInputStream. There will be no filter.
    *
    * @param isSource the InputStream that is the base for this stream.
    *
    * @exception IOException if an error occured or the stream is not a valid log file.
    */
  public LogInputStream (InputStream isSource)
    throws IOException {
    this (isSource, null);
  }

  /**
    * Create a new LogInputStream.
    *
    * @param isSource the InputStream that is the base for this stream.
    * @param lef the filter to be applied on the stream.
    *
    * @exception IOException if an error occured or the stream is not a valid log file.
    */
  public LogInputStream (InputStream isSource,
                         LogEntryFilter lef)
    throws IOException {

    super();

    m_isSource = isSource;

    // skip the leading zero
    m_isSource.skip (1);

    m_oisSource = new ObjectInputStream (m_isSource);

    m_lefFilter = lef;
  }

  /**
    * Close the stream and all streams that it relies on.
    *
    * @exception IOException if there was an error on closing.
    *
    * @override Never
    */
  public void close()
    throws IOException {
    m_oisSource.close();
    m_isSource.close();

    m_oisSource = null;
    m_isSource = null;
  }

  /**
    * Read the next log entry that is {@link LogEntryFilter#accept accepted} by the filter from the stream.
    *
    * @exception IOException if an <code>IOException</code> occurred while reading from the underlying stream.
    * Especially, this may be an <code>EOFException</code> if the end of the stream has been reached. This is
    * the only way to find out whether there are more entries in the stream.
    * @exception ClassNotFoundException if the exception was thrown by the underlying stream.
    *
    * @override Never
    */
  public LogEntry readEntry()
    throws IOException, ClassNotFoundException {

    while (true) {
      try {
        LogEntry le = (LogEntry) m_oisSource.readObject();

        if ((m_lefFilter == null) ||
            (m_lefFilter.accept (le))) {
          return le;
        }
      }
      catch (StreamCorruptedException sce) {
        m_oisSource = new ObjectInputStream (m_isSource);
      }
    }
  }
}