#pragma ident "$Id: FFTextStream.hpp 274 2006-10-27 14:24:35Z rickmach $"



/**
 * @file FFTextStream.hpp
 * An FFStream for text files
 */

#ifndef GPSTK_FFTEXTSTREAM_HPP
#define GPSTK_FFTEXTSTREAM_HPP

//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 2.1 of the License, or
//  any later version.
//
//  The GPSTk 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 Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//  
//  Copyright 2004, The University of Texas at Austin
//
//============================================================================

//============================================================================
//
//This software developed by Applied Research Laboratories at the University of
//Texas at Austin, under contract to an agency or agencies within the U.S. 
//Department of Defense. The U.S. Government retains all rights to use,
//duplicate, distribute, disclose, or release this software. 
//
//Pursuant to DoD Directive 523024 
//
// DISTRIBUTION STATEMENT A: This software has been approved for public 
//                           release, distribution is unlimited.
//
//=============================================================================






#include "FFStream.hpp"

namespace gpstk
{
   /** @addtogroup formattedfile */
   //@{

      /**
       * An FFStream that is meant for reading text.  This also includes
       * an internal line count and a read line method. When reading and
       * using the formattedGetLine() call, the lineNumber automatically
       * increments.  However, any other read and all write calls do not
       * update the line number - the derived class or programmer
       * needs to make sure
       * that the reader or writer increments lineNumber in these cases.
       */
   class FFTextStream : public FFStream
   {
   public:
         /// destructor
      virtual ~FFTextStream() {}
      
         /// Default constructor
      FFTextStream()
            : lineNumber(0)
         {}

         /**
          * Constructor.
          * @param fn file name.
          * @param mode file open mode (std::ios)
          */
      FFTextStream(const char* fn, std::ios::openmode mode=std::ios::in)
         : FFStream(fn, mode), lineNumber(0)
         {}

         /// Overrides open to reset the line number.
      virtual void open(const char* fn, std::ios::openmode mode)
         { FFStream::open(fn, mode); lineNumber = 0; }

         /// the internal line count. When writing, make sure
         /// to increment this.
      unsigned int lineNumber;

         /**
          * Like std::istream::getline but checks for EOF and removes '/r'.
          * Also increments lineNumber.  When \a expectEOF is true and EOF
          * is found, an gpstk::EndOfFile exception is thrown.  If
          * \a expectEOF is false and an EOF is encountered, an
          * gpstk::FFStreamError is thrown.
          * @param line is set to the value of the line read from the file.
          * @param expectEOF set true if finding EOF on this read is acceptable.
          * @throw EndOfFile if \a expectEOF is true and an EOF is encountered.
          * @throw FFStreamError if EOF is found and \a expectEOF is false
          * @throw gpstk::StringUtils::StringException when a string error occurs
          * or if any other error happens.
          * @warning There is a maximum line length of 256 characters when
          * using this function.
          */
      inline void formattedGetLine(std::string& line, 
                                   const bool expectEOF = false)
         throw(EndOfFile, FFStreamError, gpstk::StringUtils::StringException);
   

   protected:
         /// calls FFStream::tryFFStreamGet and adds line number information
      virtual void tryFFStreamGet(FFData& rec) 
         throw(FFStreamError, gpstk::StringUtils::StringException)
         {
            unsigned int initialLineNumber = lineNumber;

            try
            {
               FFStream::tryFFStreamGet(rec);
            }
            catch(gpstk::Exception& e)
            {
               e.addText(std::string("Near file line ") + 
                         gpstk::StringUtils::asString(lineNumber));
               lineNumber = initialLineNumber;
               mostRecentException = e;
               conditionalThrow();
            }
         }

         /// calls FFStream::tryFFStreamPut and adds line number information
      virtual void tryFFStreamPut(const FFData& rec) 
         throw(FFStreamError, gpstk::StringUtils::StringException)
         {
            unsigned int initialLineNumber = lineNumber;

            try
            {
               FFStream::tryFFStreamPut(rec);
            }
            catch(gpstk::Exception& e)
            {
               e.addText(std::string("Near file line ") + 
                         gpstk::StringUtils::asString(lineNumber));
               lineNumber = initialLineNumber;
               mostRecentException = e;
               conditionalThrow();
            }
         }

   };

      // the reason for checking ffs.eof() in the try AND catch block is
      // because if the user enabled exceptions on the stream with exceptions()
      // then eof could throw an exception, in which case we need to catch it
      // and rethrow an EOF or FFStream exception.  In any event, EndOfFile
      // gets thrown whenever there's an EOF and expectEOF is true
   void FFTextStream::formattedGetLine(std::string& line, 
                                       const bool expectEOF)
         throw(EndOfFile, FFStreamError, gpstk::StringUtils::StringException)
   {
      try
      {
         const int MAX_LINE_LENGTH = 256;
         char templine[MAX_LINE_LENGTH + 1];
         getline(templine, MAX_LINE_LENGTH);
         lineNumber++;
         line = templine;
         gpstk::StringUtils::stripTrailing(line, '\r');
            // catch EOF when stream exceptions are disabled
         if ((gcount() == 0) && eof())
         {
            if (expectEOF)
            {
               EndOfFile err("EOF encountered");
               GPSTK_THROW(err);
            }
            else
            {
               FFStreamError err("Unexpected EOF encountered");
               GPSTK_THROW(err);
            }
         }
      }
      catch(std::exception &e)
      {
            // catch EOF when exceptions are enabled
         if ( (gcount() == 0) && eof())
         {
            if (expectEOF)
            {
               EndOfFile err("EOF encountered");
               GPSTK_THROW(err);
            }
            else
            {
               FFStreamError err("Unexpected EOF");
               GPSTK_THROW(err);
            }
         } 
         else
         {
            FFStreamError err("Critical file error: " +
                              std::string(e.what()));
            GPSTK_THROW(err);
         }
      }
   }

   //@}

}  // namespace

#endif
