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.
koffice/kspread/kspread_value.cc

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 <qstring.h>
#include <qtextstream.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;
QString* 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 QString& s )
{
d = ValueData::null();
setValue( s );
}
// create a string value
Value::Value (const char *s)
{
d = ValueData::null();
setValue (QString (s));
}
// create a floating-point value from date/time
Value::Value( const QDateTime& dt )
{
d = ValueData::null();
setValue( dt );
}
// create a floating-point value from time
Value::Value( const QTime& dt )
{
d = ValueData::null();
setValue( dt );
}
// create a floating-point value from date
Value::Value( const QDate& 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 QString& s )
{
detach();
d->type = String;
d->ps = new QString( s );
d->format = fmt_String;
}
// get the value as string
QString Value::asString() const
{
QString result;
if( type() == Value::String )
if( d->ps )
result = QString( *d->ps );
return result;
}
// set error message
void Value::setError( const QString& msg )
{
detach();
d->type = Error;
d->ps = new QString( msg );
}
// get error message
QString Value::errorMessage() const
{
QString result;
if( type() == Value::Error )
if( d->ps )
result = QString( *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 QDateTime& dt )
{
// reference is 31 Dec, 1899 midnight
QDate refDate( 1899, 12, 31 );
QTime 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 QTime& time )
{
// reference time is midnight
QTime refTime( 0, 0 );
int i = refTime.msecsTo( time ) /* / 86400000 */;
setValue( i );
d->format = fmt_Time;
}
void Value::setValue( const QDate& date )
{
// reference date is 31 Dec, 1899
QDate refDate = QDate( 1899, 12, 31 );
int i = refDate.daysTo( date ) + 1;
setValue( i );
d->format = fmt_Date;
}
// get the value as date/time
QDateTime Value::asDateTime() const
{
return QDateTime( asDate(), asTime() );
}
// get the value as date
QDate Value::asDate() const
{
QDate dt( 1899, 12, 30 );
int i = asInteger();
dt = dt.addDays( i );
return dt;
}
// get the value as time
QTime Value::asTime() const
{
QTime 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 QString( *d->ps ); break;
case Array: n->pa = new ValueArray; *n->pa = (*d->pa); break;
case Error: n->ps = new QString( *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;
}
QTextStream& operator<<( QTextStream& 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;
}
QTextStream& operator<<( QTextStream& 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;
}