# DotKludgeReader.java

    /*
    ** Module   : DotKludgeReader.java
    ** Abstract : Implements an input reader which is guaranteed to contain
    **            the single dot character at the end.
    **
    ** Copyright (c) 2004-2017, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
    ** 001 NVS 20041224 NEW   @19100 Created. Needed a class that would guarantee
    **                               than an input reader terminates with the dot.
    ** 002 GES 20070425 CHG   @33291 If the contained reader supports mark/reset
    **                               then the state is properly maintained.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program 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 Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.uast;

    import java.io.*;

    /**
     * Implements a simple input reader based on an underlying input reader,
     * which logically appends the dot character to the underlying input in case
     * the latter terminates with anything else.
     * <p>
     * This class is used in {@link ProgressLexer} to handle files where the last
     * statement is not followed by the dot.
     * <p>
     * @author NVS
     */
    public class DotKludgeReader extends FilterReader
    {
       /** Keeps the last read character. */
       private int lastChar = -1;

       /** Signals EOF condition on the underlying reader. */
       private boolean eof = false;

       /** Keeps the last read character at the mark point. */
       private int markChar = -1;

       /** Next read after mark point will signal EOF condition. */
       private boolean markEof = false;

       /**
        * Constructor.
        *
        * @param   in
        *          an input stream of any kind.
        */
       public DotKludgeReader(Reader in)
       {
          super(in);
       }

       /**
        * Marks the current position in this input stream. A subsequent call to
        * the reset method repositions this stream at the last marked position
        * so that subsequent reads re-read the same bytes.
        * <p>
        * The general contract of mark is that, if the method markSupported
        * returns <code>true</code>, the stream remembers all the bytes read
        * after the call to mark and stands ready to supply those same bytes
        * again if and whenever the method reset is called. However, the stream
        * is not required to remember any data at all if more than readlimit
        * bytes are read from the stream before reset is called.
        * <p>
        * Maintains state necessary to restore the current read position.
        *
        * @param    readlimit
        *           The maximum limit of bytes that can be read before the mark
        *           position becomes invalid.
        *
        * @throws   IOException
        *           If the stream does not support mark(), or if some other I/O
        *           error occurs
        */
       public void mark(int readlimit)
       throws IOException
       {
          super.mark(readlimit);

          // save state
          markChar = lastChar;
          markEof  = eof;
       }

       /**
        * Repositions this stream to the position at the time the mark method
        * was last called on this input stream.
        *
        * @throws   IOException
        *           If the stream has not been marked, or if the mark has been
        *           invalidated, or if the stream does not support reset(), or if
        *           some other I/O error occurs.
        */
       public void reset()
       throws IOException
       {
          super.reset();

          // restore state if no exception occurred
          lastChar = markChar;
          eof      = markEof;
       }

       /**
        * Reads the next byte from the input stream. At EOF, checks if the last
        * read character was the dot, and inserts one before returning EOF,
        * if not. All white space characters are excluded from consideration.
        *
        * @return    byte read from the underlying stream or the dot character
        *            or -1 at EOF.
        * @throws    IOException
        */
       public int read()
       throws IOException
       {
          if (eof)
             return -1;

          int nextChar = super.read();
          if (nextChar != -1)
          {
             if (nextChar != '\r' && nextChar != '\n' &&
                 nextChar != '\t' && nextChar != ' ')
             lastChar = nextChar;
             return nextChar;
          }
          else
          {
             eof = true;
             if (lastChar == '.')
                return -1;
             else
                return '.';
          }
       }
    }