diff options
Diffstat (limited to 'kspread/kspread_value.cpp')
-rw-r--r-- | kspread/kspread_value.cpp | 970 |
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 <[email protected]> + + 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; +} |