summaryrefslogtreecommitdiffstats
path: root/kspread/kspread_value.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kspread/kspread_value.cpp')
-rw-r--r--kspread/kspread_value.cpp970
1 files changed, 970 insertions, 0 deletions
diff --git a/kspread/kspread_value.cpp b/kspread/kspread_value.cpp
new file mode 100644
index 00000000..898abd11
--- /dev/null
+++ b/kspread/kspread_value.cpp
@@ -0,0 +1,970 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kspread_value.h"
+
+#include <kdebug.h>
+
+#include <tqstring.h>
+#include <tqtextstream.h>
+
+#include <float.h>
+#include <math.h>
+#include <limits.h>
+
+using namespace KSpread;
+
+// helper struct for array implementation
+// this struct holds one piece of an array of size CHUNK_COLS x CHUNK_ROWS
+// or less
+struct arrayChunk {
+ arrayChunk (int c, int r) {
+ cols = c; rows = r;
+ ptr = new Value* [c*r];
+ for (int i = 0; i < c*r; ++i) ptr[i] = 0;
+ }
+
+ ~arrayChunk () {
+ if (!ptr) return;
+ unsigned count = cols * rows;
+ for (unsigned i = 0; i < count; i++)
+ delete ptr[i];
+ delete [] ptr;
+ }
+
+ arrayChunk( const arrayChunk& ac )
+ {
+ operator=( ac );
+ }
+
+ arrayChunk& operator= ( const arrayChunk& ac )
+ {
+ cols = ac.cols; rows = ac.rows;
+ ptr = new Value* [cols*rows];
+ unsigned count = cols * rows;
+ for( unsigned i = 0; i < count; i++ )
+ if( ac.ptr[i] )
+ ptr[i] = new Value( *ac.ptr[i] );
+ else
+ ptr[i] = 0;
+ return *this;
+ }
+
+ Value **ptr;
+ unsigned cols, rows;
+};
+
+#define CHUNK_COLS 128
+#define CHUNK_ROWS 128
+// helper class for array implementation
+class ValueArray
+{
+public:
+ arrayChunk **chunks;
+ unsigned columns;
+ unsigned rows;
+ unsigned chunkCols, chunkRows;
+
+ ValueArray(): chunks(0), columns(0), rows(0), chunkCols(0), chunkRows(0) {};
+
+ ~ValueArray()
+ {
+ clear();
+ };
+
+ ValueArray( const ValueArray& va )
+ : chunks(0), columns(0), rows(0), chunkCols(0), chunkRows(0)
+ {
+ operator=( va );
+ }
+
+ ValueArray& operator= ( const ValueArray& va )
+ {
+ init( va.columns, va.rows );
+ unsigned count = chunkCols * chunkRows;
+ for( unsigned i = 0; i < count; i++ )
+ if( va.chunks[i] )
+ chunks[i] = new arrayChunk (*va.chunks[i]);
+ else
+ chunks[i] = 0;
+ return *this;
+ }
+
+ void clear()
+ {
+ int c = columns / CHUNK_COLS;
+ int r = rows / CHUNK_ROWS;
+ if (columns % CHUNK_COLS != 0) c++;
+ if (rows % CHUNK_ROWS != 0) r++;
+ if( !chunks ) return;
+ unsigned count = c*r;
+ if( !count ) return;
+ for (unsigned i = 0; i < count; i++)
+ delete chunks[i];
+ delete [] chunks;
+ chunks = 0;
+ columns = rows = chunkCols = chunkRows = 0;
+ }
+
+ void init( unsigned c, unsigned r )
+ {
+ if (chunks) clear();
+ columns = c; rows = r;
+ int cc = columns / CHUNK_COLS;
+ int rr = rows / CHUNK_ROWS;
+ if (columns % CHUNK_COLS != 0) cc++;
+ if (rows % CHUNK_ROWS != 0) rr++;
+ chunkCols = cc;
+ chunkRows = rr;
+ unsigned count = cc*rr;
+ chunks = new arrayChunk* [count];
+ for( unsigned i = 0; i < count; i++ )
+ chunks[i] = 0;
+ }
+
+ Value* at( unsigned c, unsigned r ) const
+ {
+ if( !chunks ) return 0;
+ if( c >= columns ) return 0;
+ if( r >= rows ) return 0;
+
+ int col = c / CHUNK_COLS;
+ int row = r / CHUNK_ROWS;
+ int cpos = c % CHUNK_COLS;
+ int rpos = r % CHUNK_ROWS;
+ arrayChunk *chunk = chunks[row * chunkCols + col];
+ if (!chunk) return 0;
+ return chunk->ptr[rpos * chunk->cols + cpos];
+ };
+
+ void set( unsigned c, unsigned r, Value* v )
+ {
+ if (!chunks) return;
+ if( c >= columns ) return;
+ if( r >= rows ) return;
+ unsigned col = c / CHUNK_COLS;
+ unsigned row = r / CHUNK_ROWS;
+ unsigned cpos = c % CHUNK_COLS;
+ unsigned rpos = r % CHUNK_ROWS;
+ arrayChunk *chunk = chunks[row * chunkCols + col];
+ if (!chunk) {
+ unsigned cc = (col==chunkCols-1) ? (columns % CHUNK_COLS) : CHUNK_COLS;
+ unsigned rr = (row==chunkRows-1) ? (rows % CHUNK_ROWS) : CHUNK_ROWS;
+ chunk = new arrayChunk (cc, rr);
+ chunks[row * chunkCols + col] = chunk;
+ }
+ delete chunk->ptr[rpos * chunk->cols + cpos];
+ chunk->ptr[rpos * chunk->cols + cpos] = v;
+ }
+
+ bool operator==( const ValueArray& other ) const
+ {
+ if ( columns != other.columns || rows != other.rows )
+ return false;
+ for ( unsigned r = 0; r < rows; ++r )
+ for ( unsigned c = 0; c < columns; ++c ) {
+ Value* v1 = at( c, r );
+ Value* v2 = other.at( c, r );
+ if ( ( v1 && !v2 ) || ( !v1 && v2 ) )
+ return false;
+ if ( !( v1 && v2 && *v1 == *v2 ) )
+ return false;
+ }
+ return true;
+ }
+
+};
+
+
+// helper class for Value
+class KSpread::ValueData
+{
+ public:
+
+ Value::Type type:4;
+ Value::Format format:4;
+
+ // reference count, at least one when object exists
+ unsigned int count:24;
+
+ union
+ {
+ bool b;
+ long i;
+ double f;
+ TQString* ps;
+ ValueArray* pa;
+ };
+
+ // create empty data
+ ValueData(): type( Value::Empty ),
+ format (Value::fmt_None), count( 1 ), ps( 0 ) { };
+
+ // destroys data
+ ~ValueData(){ if( this == s_null ) s_null = 0;
+ if( type == Value::Array ) delete pa;
+ if( type == Value::String ) delete ps;
+ if( type == Value::Error ) delete ps;
+ }
+
+ // static empty data to be shared
+ static ValueData* null()
+ { if( !s_null) s_null = new ValueData; else s_null->ref(); return s_null; }
+
+ // increase reference count
+ void ref() { count++; }
+
+ // decrease reference count
+ void unref()
+ { --count; if( !count ) delete this; }
+
+ // true if it's null (which is shared)
+ bool isNull(){ return this == s_null; }
+
+ /** set most probable formatting based on the type */
+ void setFormatByType ();
+
+ private:
+
+ static ValueData* s_null;
+};
+
+void KSpread::ValueData::setFormatByType ()
+{
+ switch (type) {
+ case Value::Empty:
+ format = Value::fmt_None;
+ break;
+ case Value::Boolean:
+ format = Value::fmt_Boolean;
+ break;
+ case Value::Integer:
+ format = Value::fmt_Number;
+ break;
+ case Value::Float:
+ format = Value::fmt_Number;
+ break;
+ case Value::String:
+ format = Value::fmt_String;
+ break;
+ case Value::Array:
+ format = Value::fmt_None;
+ break;
+ case Value::CellRange:
+ format = Value::fmt_None;
+ break;
+ case Value::Error:
+ format = Value::fmt_String;
+ break;
+ };
+}
+
+// to be shared between all empty value
+ValueData* ValueData::s_null = 0;
+
+// static things
+Value ks_value_empty;
+Value ks_error_div0;
+Value ks_error_na;
+Value ks_error_name;
+Value ks_error_null;
+Value ks_error_num;
+Value ks_error_ref;
+Value ks_error_value;
+
+// create an empty value
+Value::Value()
+{
+ d = ValueData::null();
+}
+
+// destructor
+Value::~Value()
+{
+ d->unref();
+}
+
+// create value of certain type
+Value::Value( Value::Type _type )
+{
+ d = new ValueData;
+ d->type = _type;
+ d->setFormatByType ();
+}
+
+// copy constructor
+Value::Value( const Value& _value )
+{
+ d = ValueData::null();
+ assign( _value );
+}
+
+// assignment operator
+Value& Value::operator=( const Value& _value )
+{
+ return assign( _value );
+}
+
+// comparison operator - returns true only if strictly identical, unlike equal()/compare()
+bool Value::operator==( const Value& v ) const
+{
+ const ValueData* n = v.d;
+ if ( (uint)d->type != n->type )
+ return false;
+ switch( d->type )
+ {
+ case Empty: return true;
+ case Boolean: return n->b == d->b;
+ case Integer: return n->i == d->i;
+ case Float: return compare( n->f, d->f ) == 0;
+ case String: return *n->ps == *d->ps;
+ case Array: return *n->pa == *d->pa;
+ case Error: return *n->ps == *d->ps;
+ default: break;
+ }
+ kdWarning() << "Unhandled type in Value::operator==: " << d->type << endl;
+ return false;
+}
+
+// create a boolean value
+Value::Value( bool b )
+{
+ d = ValueData::null();
+ setValue( b );
+}
+
+// create an integer value
+Value::Value( long i )
+{
+ d = ValueData::null();
+ setValue ( i );
+}
+
+// create an integer value
+Value::Value( int i )
+{
+ d = ValueData::null();
+ setValue ( i );
+}
+
+// create a floating-point value
+Value::Value( double f )
+{
+ d = ValueData::null();
+ setValue( f );
+}
+
+// create a string value
+Value::Value( const TQString& s )
+{
+ d = ValueData::null();
+ setValue( s );
+}
+
+// create a string value
+Value::Value (const char *s)
+{
+ d = ValueData::null();
+ setValue (TQString (s));
+}
+
+// create a floating-point value from date/time
+Value::Value( const TQDateTime& dt )
+{
+ d = ValueData::null();
+ setValue( dt );
+}
+
+// create a floating-point value from time
+Value::Value( const TQTime& dt )
+{
+ d = ValueData::null();
+ setValue( dt );
+}
+
+// create a floating-point value from date
+Value::Value( const TQDate& dt )
+{
+ d = ValueData::null();
+ setValue( dt );
+}
+
+// create an array value
+Value::Value( unsigned columns, unsigned rows )
+{
+ d = new ValueData;
+ d->type = Array;
+ d->format = fmt_None;
+ d->pa = new ValueArray;
+ d->pa->init( columns, rows );
+}
+
+// assign value from other
+// shallow copy: only copy the data pointer
+Value& Value::assign( const Value& _value )
+{
+ d->unref();
+ d = _value.d;
+ d->ref();
+ return *this;
+}
+
+// return type of the value
+Value::Type Value::type() const
+{
+ return d ? d->type : Empty;
+}
+
+// set the value to boolean
+void Value::setValue( bool b )
+{
+ detach();
+ d->type = Boolean;
+ d->b = b;
+ d->format = fmt_Boolean;
+}
+
+// get the value as boolean
+bool Value::asBoolean() const
+{
+ bool result = false;
+
+ if( type() == Value::Boolean )
+ result = d->b;
+
+ return result;
+}
+
+// set the value to integer
+void Value::setValue( long i )
+{
+ detach();
+ d->type = Integer;
+ d->i = i;
+ d->format = fmt_Number;
+}
+
+// set the value to integer
+void Value::setValue( int i )
+{
+ detach();
+ d->type = Integer;
+ d->i = static_cast<long>( i );
+ d->format = fmt_Number;
+}
+
+// get the value as integer
+long Value::asInteger() const
+{
+ long result = 0;
+
+ if( type() == Value::Integer )
+ result = d->i;
+
+ if( type() == Value::Float )
+ result = static_cast<long>(d->f);
+
+ return result;
+}
+
+void Value::setValue( const Value& v )
+{
+ assign( v );
+}
+
+// set the value as floating-point
+void Value::setValue( double f )
+{
+ detach();
+ d->type = Float;
+ d->f = f;
+ d->format = fmt_Number;
+}
+
+// get the value as floating-point
+double Value::asFloat() const
+{
+ double result = 0.0;
+
+ if( type() == Value::Float )
+ result = d->f;
+
+ if( type() == Value::Integer )
+ result = static_cast<double>(d->i);
+
+ return result;
+}
+
+// set the value as string
+void Value::setValue( const TQString& s )
+{
+ detach();
+ d->type = String;
+ d->ps = new TQString( s );
+ d->format = fmt_String;
+}
+
+// get the value as string
+TQString Value::asString() const
+{
+ TQString result;
+
+ if( type() == Value::String )
+ if( d->ps )
+ result = TQString( *d->ps );
+
+ return result;
+}
+
+// set error message
+void Value::setError( const TQString& msg )
+{
+ detach();
+ d->type = Error;
+ d->ps = new TQString( msg );
+}
+
+// get error message
+TQString Value::errorMessage() const
+{
+ TQString result;
+
+ if( type() == Value::Error )
+ if( d->ps )
+ result = TQString( *d->ps );
+
+ return result;
+}
+
+// set the value as date/time
+// NOTE: date/time is stored as serial number
+// Day 61 means 1st of March, 1900
+void Value::setValue( const TQDateTime& dt )
+{
+ // reference is 31 Dec, 1899 midnight
+ TQDate refDate( 1899, 12, 31 );
+ TQTime refTime( 0, 0 );
+
+ int i = refDate.daysTo( dt.date() ) + 1;
+ i += refTime.secsTo( dt.time() ) / 86400;
+
+ setValue( i );
+ d->format = fmt_DateTime;
+}
+
+void Value::setValue( const TQTime& time )
+{
+ // reference time is midnight
+ TQTime refTime( 0, 0 );
+ int i = refTime.msecsTo( time ) /* / 86400000 */;
+
+ setValue( i );
+ d->format = fmt_Time;
+}
+
+void Value::setValue( const TQDate& date )
+{
+ // reference date is 31 Dec, 1899
+ TQDate refDate = TQDate( 1899, 12, 31 );
+ int i = refDate.daysTo( date ) + 1;
+
+ setValue( i );
+ d->format = fmt_Date;
+}
+
+// get the value as date/time
+TQDateTime Value::asDateTime() const
+{
+ return TQDateTime( asDate(), asTime() );
+}
+
+// get the value as date
+TQDate Value::asDate() const
+{
+ TQDate dt( 1899, 12, 30 );
+
+ int i = asInteger();
+ dt = dt.addDays( i );
+
+ return dt;
+}
+
+// get the value as time
+TQTime Value::asTime() const
+{
+ TQTime dt;
+
+ int i = asInteger();
+ dt = dt.addMSecs(i) /*( f * 86400 * 1000 )*/;
+
+ return dt;
+}
+
+Value::Format Value::format() const
+{
+ return d ? d->format : fmt_None;
+}
+
+void Value::setFormat (Value::Format fmt)
+{
+ detach();
+ d->format = fmt;
+}
+
+Value Value::element( unsigned column, unsigned row ) const
+{
+ if( (uint)d->type != Array ) return *this;
+ if( !d->pa ) return *this;
+ Value* v = d->pa->at (column % columns(), row % rows());
+ return v ? Value( *v ) : empty();
+}
+
+void Value::setElement( unsigned column, unsigned row, const Value& v )
+{
+ if( (uint)d->type != Array ) return;
+ if( !d->pa ) return;
+ detach();
+ d->pa->set( column, row, new Value( v ) );
+}
+
+unsigned Value::columns() const
+{
+ if( (uint)d->type != Array ) return 1;
+ if( !d->pa ) return 1;
+ return d->pa->columns;
+}
+
+unsigned Value::rows() const
+{
+ if( (uint)d->type != Array ) return 1;
+ if( !d->pa ) return 1;
+ return d->pa->rows;
+}
+
+// reference to empty value
+const Value& Value::empty()
+{
+ return ks_value_empty;
+}
+
+// reference to #DIV/0! error
+const Value& Value::errorDIV0()
+{
+ if( !ks_error_div0.isError() )
+ ks_error_div0.setError( "#DIV/0!" );
+ return ks_error_div0;
+}
+
+// reference to #N/A error
+const Value& Value::errorNA()
+{
+ if( !ks_error_na.isError() )
+ ks_error_na.setError( "#N/A" );
+ return ks_error_na;
+}
+
+// reference to #NAME? error
+const Value& Value::errorNAME()
+{
+ if( !ks_error_name.isError() )
+ ks_error_name.setError( "#NAME?" );
+ return ks_error_name;
+}
+
+// reference to #NUM! error
+const Value& Value::errorNUM()
+{
+ if( !ks_error_num.isError() )
+ ks_error_num.setError( "#NUM!" );
+ return ks_error_num;
+}
+
+// reference to #NULL! error
+const Value& Value::errorNULL()
+{
+ if( !ks_error_null.isError() )
+ ks_error_null.setError( "#NULL!" );
+ return ks_error_null;
+}
+
+// reference to #REF! error
+const Value& Value::errorREF()
+{
+ if( !ks_error_ref.isError() )
+ ks_error_ref.setError( "#REF!" );
+ return ks_error_ref;
+}
+
+// reference to #VALUE! error
+const Value& Value::errorVALUE()
+{
+ if( !ks_error_value.isError() )
+ ks_error_value.setError( "#VALUE!" );
+ return ks_error_value;
+}
+
+// detach, create deep copy of ValueData
+void Value::detach()
+{
+ if( d->isNull() || ( d->count > 1 ) )
+ {
+ ValueData* n;
+ n = new ValueData;
+
+ n->type = d->type;
+ switch( n->type )
+ {
+ case Empty: break;
+ case Boolean: n->b = d->b; break;
+ case Integer: n->i = d->i; break;
+ case Float: n->f = d->f; break;
+ case String: n->ps = new TQString( *d->ps ); break;
+ case Array: n->pa = new ValueArray; *n->pa = (*d->pa); break;
+ case Error: n->ps = new TQString( *d->ps ); break;
+ default: break;
+ }
+
+ d->unref();
+ d = n;
+ }
+}
+
+int Value::compare( double v1, double v2 )
+{
+ double v3 = v1 - v2;
+ if( v3 > DBL_EPSILON ) return 1;
+ if( v3 < -DBL_EPSILON ) return -1;
+ return 0;
+}
+
+bool Value::isZero( double v )
+{
+ return fabs( v ) < DBL_EPSILON;
+}
+
+bool Value::isZero() const
+{
+ if( !isNumber() ) return false;
+ return isZero( asFloat() );
+}
+
+bool Value::allowComparison( const Value& v ) const
+{
+ Value::Type t1 = d->type;
+ Value::Type t2 = v.type();
+
+ if( ( t1 == Empty ) && ( t2 == Empty ) ) return true;
+ if( ( t1 == Empty ) && ( t2 == String ) ) return true;
+
+ if( ( t1 == Boolean ) && ( t2 == Boolean ) ) return true;
+ if( ( t1 == Boolean ) && ( t2 == Integer ) ) return true;
+ if( ( t1 == Boolean ) && ( t2 == Float ) ) return true;
+ if( ( t1 == Boolean ) && ( t2 == String ) ) return true;
+
+ if( ( t1 == Integer ) && ( t2 == Boolean ) ) return true;
+ if( ( t1 == Integer ) && ( t2 == Integer ) ) return true;
+ if( ( t1 == Integer ) && ( t2 == Float ) ) return true;
+ if( ( t1 == Integer ) && ( t2 == String ) ) return true;
+
+ if( ( t1 == Float ) && ( t2 == Boolean ) ) return true;
+ if( ( t1 == Float ) && ( t2 == Integer ) ) return true;
+ if( ( t1 == Float ) && ( t2 == Float ) ) return true;
+ if( ( t1 == Float ) && ( t2 == String ) ) return true;
+
+ if( ( t1 == String ) && ( t2 == Empty ) ) return true;
+ if( ( t1 == String ) && ( t2 == Boolean ) ) return true;
+ if( ( t1 == String ) && ( t2 == Integer ) ) return true;
+ if( ( t1 == String ) && ( t2 == Float ) ) return true;
+ if( ( t1 == String ) && ( t2 == String ) ) return true;
+
+ // errors can be compared too ...
+ if ((t1 == Error) && (t2 == Error)) return true;
+
+ return false;
+}
+
+// compare values. looks strange in order to be compatible with Excel
+int Value::compare( const Value& v ) const
+{
+ Value::Type t1 = d->type;
+ Value::Type t2 = v.type();
+
+ // errors always less than everything else
+ if( ( t1 == Error ) && ( t2 != Error ) )
+ return -1;
+ if( ( t2 == Error ) && ( t1 != Error ) )
+ return 1;
+
+ // comparing errors only yields 0 if they are the same
+ if( ( t1 == Error ) && ( t2 == Error ) )
+ return errorMessage() != v.errorMessage();
+
+ // empty == empty
+ if( ( t1 == Empty ) && ( t2 == Empty ) )
+ return 0;
+
+ // empty value is always less than string
+ // (except when the string is empty)
+ if( ( t1 == Empty ) && ( t2 == String ) )
+ return( v.asString().isEmpty() ) ? 0 : -1;
+
+ // boolean vs boolean
+ if( ( t1 == Boolean ) && ( t2 == Boolean ) )
+ {
+ bool p = asBoolean();
+ bool q = v.asBoolean();
+ if( p ) return q ? 0 : 1;
+ else return q ? -1 : 0;
+ }
+
+ // boolean is always greater than integer
+ if( ( t1 == Boolean ) && ( t2 == Integer ) )
+ return 1;
+
+ // boolean is always greater than float
+ if( ( t1 == Boolean ) && ( t2 == Float ) )
+ return 1;
+
+ // boolean is always greater than string
+ if( ( t1 == Boolean ) && ( t2 == String ) )
+ return 1;
+
+ // integer is always less than boolean
+ if( ( t1 == Integer ) && ( t2 == Boolean ) )
+ return -1;
+
+ // integer vs integer
+ if( ( t1 == Integer ) && ( t2 == Integer ) )
+ {
+ long p = asInteger();
+ long q = v.asInteger();
+ return ( p == q ) ? 0 : ( p < q ) ? -1 : 1;
+ }
+
+ // integer vs float
+ if( ( t1 == Integer ) && ( t2 == Float ) )
+ return compare( asFloat(), v.asFloat() );
+
+ // integer is always less than string
+ if( ( t1 == Integer ) && ( t2 == String ) )
+ return -1;
+
+ // float is always less than boolean
+ if( ( t1 == Float ) && ( t2 == Boolean ) )
+ return -1;
+
+ // float vs integer
+ if( ( t1 == Float ) && ( t2 == Integer ) )
+ return compare( asFloat(), v.asFloat() );
+
+ // float vs float
+ if( ( t1 == Float ) && ( t2 == Float ) )
+ return compare( asFloat(), v.asFloat() );
+
+ // float is always less than string
+ if( ( t1 == Float ) && ( t2 == String ) )
+ return -1;
+
+ // string is always greater than empty value
+ // (except when the string is empty)
+ if( ( t1 == String ) && ( t2 == Empty ) )
+ return( asString().isEmpty() ) ? 0 : 1;
+
+ // string is always less than boolean
+ if( ( t1 == String ) && ( t2 == Boolean ) )
+ return -1;
+
+ // string is always greater than integer
+ if( ( t1 == String ) && ( t2 == Integer ) )
+ return 1;
+
+ // string is always greater than float
+ if( ( t1 == String ) && ( t2 == Float ) )
+ return 1;
+
+ // The-Real-String comparison
+ if( ( t1 == String ) && ( t2 == String ) )
+ return asString().compare( v.asString() );
+
+ // Undefined, actually allowComparison would return false
+ return 0;
+}
+
+bool Value::equal( const Value& v ) const
+{
+ if (!allowComparison (v)) return false;
+ return compare( v ) == 0;
+}
+
+bool Value::less( const Value& v ) const
+{
+ if (!allowComparison (v)) return false;
+ return compare( v ) < 0;
+}
+
+bool Value::greater( const Value& v ) const
+{
+ if (!allowComparison (v)) return false;
+ return compare( v ) > 0;
+}
+
+TQTextStream& operator<<( TQTextStream& ts, Value::Type type )
+{
+ switch( type )
+ {
+ case Value::Empty: ts << "Empty"; break;
+ case Value::Boolean: ts << "Boolean"; break;
+ case Value::Integer: ts << "Integer"; break;
+ case Value::Float: ts << "Float"; break;
+ case Value::String: ts << "String"; break;
+ case Value::Array: ts << "Array"; break;
+ case Value::Error: ts << "Error"; break;
+ default: ts << "Unknown!"; break;
+ };
+ return ts;
+}
+
+TQTextStream& operator<<( TQTextStream& ts, Value value )
+{
+ ts << value.type();
+ switch( value.type() )
+ {
+ case Value::Empty: break;
+
+ case Value::Boolean:
+ ts << ": ";
+ if (value.asBoolean()) ts << "TRUE";
+ else ts << "FALSE"; break;
+
+ case Value::Integer:
+ ts << ": " << value.asInteger(); break;
+
+ case Value::Float:
+ ts << ": " << value.asFloat(); break;
+
+ case Value::String:
+ ts << ": " << value.asString(); break;
+
+ case Value::Error:
+ ts << "(" << value.errorMessage() << ")"; break;
+
+ default: break;
+ }
+ return ts;
+}