You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
971 lines
21 KiB
971 lines
21 KiB
/* 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;
|
|
}
|