Program Listing for File input_text_stream.hpp

Return to documentation for file (/tmp/ws/src/ecl_core/ecl_streams/include/ecl/streams/text_streams/input_text_stream.hpp)

/*****************************************************************************
** Ifdefs
*****************************************************************************/

#ifndef ECL_STREAMS_INPUT_TEXT_STREAM_HPP_
#define ECL_STREAMS_INPUT_TEXT_STREAM_HPP_

/*****************************************************************************
** Includes
*****************************************************************************/

#include <iostream>
#include <limits>
#include <string>
#include <ecl/config/macros.hpp>
#include <ecl/config/portable_types.hpp>
#include <ecl/exceptions/standard_exception.hpp>
#include <ecl/concepts/devices.hpp>
#include <ecl/type_traits/numeric_limits.hpp>
#include "base_text_stream.hpp"
#include "../macros.hpp"

/*****************************************************************************
** Namespaces
*****************************************************************************/

namespace ecl {
namespace interfaces {

/*****************************************************************************
** Interface [InputTextStream]
*****************************************************************************/

template <typename Device, bool InputDevice = true >
class ECL_PUBLIC InputTextStream {
};

template <typename Device>
class ECL_PUBLIC InputTextStream<Device,true> : public virtual BaseTextStream<Device> {
public:
    /*********************
    ** Constructors
    **********************/
    InputTextStream();
    virtual ~InputTextStream() {}

    /*********************
    ** Streaming Operators
    **********************/
    InputTextStream<Device>& operator >> ( char &c );
    InputTextStream<Device>& operator >> ( std::string &s );
    InputTextStream<Device>& operator >> ( short &i );
    InputTextStream<Device>& operator >> ( int &i );
    InputTextStream<Device>& operator >> ( long &i );
    InputTextStream<Device>& operator >> ( unsigned char &c );
    InputTextStream<Device>& operator >> ( unsigned short &i );
    InputTextStream<Device>& operator >> ( unsigned int &i );
    InputTextStream<Device>& operator >> ( unsigned long &i );
    InputTextStream<Device>& operator >> ( float &f );
    InputTextStream<Device>& operator >> ( double &d );
    InputTextStream<Device>& operator >> ( long long &i );
    InputTextStream<Device>& operator >> ( unsigned long long &i );
    InputTextStream<Device>& operator >> ( bool &b );

    /*********************
    ** Non-Streaming API
    **********************/
    void enableRawCharReads();
    void disableRawCharReads();

private:
    bool raw_char_reads;

    /*********************
    ** Private Parsers
    **********************/
    bool skipLeadingWhiteSpace(char &c);
    template <typename Number>
    bool getIntegerFromStream(Number &i);
    template <typename Number>
    bool parseHexInteger(Number &i);
    template <typename Number>
    bool getFloatFromStream(Number &f);
};

/*****************************************************************************
** Implementation [InputTextStream]
*****************************************************************************/
template <typename Device>
InputTextStream<Device,true>::InputTextStream() : raw_char_reads(false) {
    ecl_compile_time_concept_check(ecl::InputCharDeviceConcept<Device>);
}

/*****************************************************************************
** Implementation [InputTextStream][Streaming Operators]
*****************************************************************************/
template <typename Device>
InputTextStream<Device> & InputTextStream<Device,true>::operator >> ( char &c ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));

    if ( !raw_char_reads ) {
        if( skipLeadingWhiteSpace(c) ) {
            this->error = NoError;
        } else {
            this->error = ReadError;
        }
    } else {
        if ( this->io_device.read(c) == 1 ) {
            this->error = NoError;
        } else {
            this->error = ReadError;
        }
    }
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( std::string &s ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));

    char c;
    if ( skipLeadingWhiteSpace(c) ) {
        s.clear();
        do {
            s.push_back(c);
            if( this->io_device.read(c) < 1 ) {
                this->error = ReadError;
                break;
            }
        } while ( ( c != ' ' ) && ( c != '\n' ) );
        this->error = NoError;
    } else {
        this->error = ReadError;
    }

    // Original method - reads a whole line (must be terminated by a \n)
//    static char buffer[80];
//    this->io_device.read(&buffer[0],80);
//    s.assign(buffer);
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( short &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));

    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams

    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( int &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( long &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}
template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( long long &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}
template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( unsigned char &uc ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(uc) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( unsigned short &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( unsigned int &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( unsigned long &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( unsigned long long &i ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getIntegerFromStream(i) ) {
        this->error = NoError;
    } // Real errors are handled internally in getIntegerFromStreams
    return *this;
}


template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( float &f ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getFloatFromStream(f) ) {
        this->error = NoError;
    } // Real errors are handled internally in getFloatFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( double &d ) {

    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    if ( getFloatFromStream(d) ) {
        this->error = NoError;
    } // Real errors are handled internally in getFloatFromStreams
    return *this;
}

template <typename Device>
InputTextStream<Device>& InputTextStream<Device,true>::operator >> ( bool &b )
{
    ecl_assert_throw(this->io_device.open(),ecl::StandardException(LOC,OpenError,"The underlying stream device is not open."));
    std::string s;
    *this >> s;

    if ( this->fail() ) { // string could not be read
        return *this; // error already set
    }

    if ( ( s == "true" ) || ( s == "TRUE" ) || ( s == "True" ) ) {
        b = true;
        this->error = NoError;
    } else if ( ( s == "false" ) || ( s == "FALSE" ) || ( s == "False" ) ) {
        b = false;
        this->error = NoError;
    } else {
        this->error = ConversionError;
    }
    return *this;
}

/*****************************************************************************
** Implementation [InputTextStream][Non-Streaming Interface]
*****************************************************************************/

template <typename Device>
void InputTextStream<Device,true>::enableRawCharReads() { raw_char_reads = true; }

template <typename Device>
void InputTextStream<Device,true>::disableRawCharReads() { raw_char_reads = false; }


/*****************************************************************************
** Implementation [InputTextStream][Private Parsers]
*****************************************************************************/

template <typename Device>
bool InputTextStream<Device,true>::skipLeadingWhiteSpace(char &c)
{
    do {
        if( this->io_device.read(c) < 1 ) { // Fail if either there is a read error OR there is nothing in the device.
            this->error = ReadError;
            return false;
        }
    } while ( ( c == ' ' ) || ( c == '\n' ) );
    return true;
}

template <typename Device>
template <typename Number>
bool InputTextStream<Device,true>::parseHexInteger(Number &i)
{
    char c;
    static int digits[25];
    static const short ceiling = numeric_limits<short>::maximum/10;

    i = 0;
    int j = 0;
    while ( j < 25 ) {
        long n = this->io_device.read(c);
        if ( n < 0 ) {
            this->error = ReadError;
            return false;
        } else if ( n == 0 ) {
            break; // nothing more to read.
        }
        if ( ( c >= '0' ) && ( c <= '9') ) {
            digits[j] = c - '0';
        } else if ( ( c >= 'a' ) && ( c <= 'f') ) {
            digits[j] = 10 + c - 'a';
        } else if ( ( c >= 'A' ) && ( c <= 'F') ) {
            digits[j] = 10 + c - 'A';
        } else {
            break; // No more valid characters to read.
        }
        ++j;
    }
    if ( j == 0 ) {
        this->error = ReadError;
        return false;
    }

    short number = 0;
    for ( int k = 0; k < j; ++k ) {
        if ( number < ceiling ) {
            number = 16*number + (digits[k]);
        } else {
            this->error = OutOfRangeError;
            return false;
        }
    }
    i = number;
    return true;
}

template <typename Device>
template <typename Number>
bool InputTextStream<Device,true>::getIntegerFromStream(Number &i) {

    static char digits[25]; // 20 is the maximum needed on a 64 bit system for a long type
    static const Number ceiling = ecl::numeric_limits<Number>::maximum/10;
    char c = ' ';
    Number sign_multiplier = 1;
    Number number;

    if ( !skipLeadingWhiteSpace(c) ) { return false; }

    if ( c == '-' ) { // Negative
        if ( std::numeric_limits<Number>::min() != 0 ) {
            sign_multiplier = -1;
            if ( this->io_device.read(c) < 1 ) {
                this->error = ReadError;
                return false;
            }
        } // else do nothing and continue, an error will be found at the next step.
    } else if ( c == '+' ) { // Positive
        if ( this->io_device.read(c) < 1 ) {
            this->error = ReadError;
            return false;
        }
    } else if ( c == '0' ) {
        long n = this->io_device.read(c);
        if ( ( n == 0 ) || ( c == ' ' ) || ( c == '\n' ) ) { // It's just a single zero.
            i = 0;
            return true;
        } else if ( n < 0 ) {
            this->error = ReadError;
            return false;
        } else if ( c == 'x' ) {
            return parseHexInteger(i);
        } else {
            // its a regular integer, just continue
        }
    } else if ( c == 'x' ) {
        return parseHexInteger(i);
    }

    /*********************
    ** Parse reg. integer
    **********************/
    int j = 0;
    while ( ( c >= '0' ) && ( c <= '9') && ( j < 25 ) ) {
        digits[j] = c;
        j++;
        long n = this->io_device.read(c);
        if ( n < 0 ) {
            this->error = ReadError;
            return false;
        } else if ( n == 0 ) {
            break;
        } else { // n > 1
        }
    }
    if ( j == 0 ) {
        this->error = ConversionError;
        return false;
    }
    number = 0;
    for ( int k = 0; k < j; ++k )
    {
        if ( number < ceiling )
        {
            number = 10*number + (digits[k] - '0');
        } else {
            this->error = OutOfRangeError;
            return false;
        }
    }
    i = sign_multiplier*number;

    return true;
}

template <typename Device>
template <typename Number>
bool InputTextStream<Device,true>::getFloatFromStream(Number &f) {
    static const int bufferSize = 25;
    static char digits[bufferSize]; // 20 is the maximum needed on a 64 bit system for a long type
    char c = ' ';
    int integral_length = 0;
    int decimal_length = 0;
    Number sign_multiplier = 1;
    Number number;

    if ( !skipLeadingWhiteSpace(c) ) { return false; }

    if ( c == '-' ) {
        sign_multiplier = -1;
        if ( this->io_device.read(c) < 1 ) { this->error = ReadError; return false; }
    } else if ( c == '+' ) {
        if ( this->io_device.read(c) < 1 ) { this->error = ReadError; return false; }
    }
    number = 0;
    /******************************************
    ** Integral part
    *******************************************/
    while ( ( c >= '0' ) && ( c <= '9') && ( integral_length < bufferSize ) ) {
        digits[integral_length] = c;
        long n = this->io_device.read(c);
        if ( n < 0 ) {
            this->error = ReadError; return false;
        } else if ( n == 0 ) {
            break;
        } else { // n > 1
            integral_length++;
        }
    }
    for ( int k = 0; k < integral_length; ++k ) {
        number = 10*number + (digits[k] - '0');
    }

    if ( c == '.' ) {
        float frac_multiplier = 1;
        if ( this->io_device.read(c) < 1 ) { this->error = ReadError; return false; }
        /******************************************
        ** Decimal part
        *******************************************/
        while ( ( c >= '0' ) && ( c <= '9') && ( decimal_length < bufferSize ) ) {
            digits[decimal_length] = c;
            long n = this->io_device.read(c);
            if ( n < 0 ) {
                this->error = ReadError; return false;
            } else if ( n == 0 ) {
                break;
            } else { // n > 1
                decimal_length++;
            }
        }
        for ( int k = 0; k < decimal_length; ++k ) {
            frac_multiplier *= 0.1f;
            number = number + frac_multiplier*(digits[k] - '0');
        }
    }

    if ( (integral_length+decimal_length) == 0 ) {
        this->error = ConversionError;
        return false;
    }

    f = sign_multiplier*number;

    return true;
}

} // namespace interfaces
} // namespace ecl

#endif /* ECL_STREAMS_INPUT_TEXT_STREAM_HPP_ */