diff options
Diffstat (limited to 'kspread/functions.cpp')
-rw-r--r-- | kspread/functions.cpp | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/kspread/functions.cpp b/kspread/functions.cpp new file mode 100644 index 00000000..fa485171 --- /dev/null +++ b/kspread/functions.cpp @@ -0,0 +1,526 @@ +/* This file is part of the KDE project + Copyright (C) 2003,2004 Ariya Hidayat <[email protected]> + Copyright (C) 2005 Tomas Mecir <[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 "formula.h" +#include "functions.h" +#include "valuecalc.h" + +#include <tqdict.h> +#include <tqdom.h> +#include <tqfile.h> +#include <tqvaluevector.h> + +#include <kdebug.h> +#include <tdelocale.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> + +#include "kspread_factory.h" + +namespace KSpread +{ + +class Function::Private +{ +public: + TQString name; + FunctionPtr ptr; + int paramMin, paramMax; + bool acceptArray; + bool ne; // need FunctionExtra* when called ? +}; + +class FunctionRepository::Private +{ +public: + TQDict<Function> functions; + TQDict<FunctionDescription> funcs; +}; + +} // namespace KSpread + + +using namespace KSpread; + +Function::Function( const TQString& name, FunctionPtr ptr ) +{ + d = new Private; + d->name = name; + d->ptr = ptr; + d->acceptArray = false; + d->paramMin = 1; + d->paramMax = 1; + d->ne = false; +} + +Function::~Function() +{ + delete d; +} + +TQString Function::name() const +{ + return d->name; +} + +void Function::setParamCount (int min, int max) +{ + d->paramMin = min; + d->paramMax = (max == 0) ? min : max; +} + +bool Function::paramCountOkay (int count) +{ + // less than needed + if (count < d->paramMin) return false; + // no upper limit + if (d->paramMax == -1) return true; + // more than needed + if (count > d->paramMax) return false; + // okay otherwise + return true; +} + +void Function::setAcceptArray (bool accept) { + d->acceptArray = accept; +} + +bool Function::needsExtra () { + return d->ne; +} +void Function::setNeedsExtra (bool extra) { + d->ne = extra; +} + +Value Function::exec (valVector args, ValueCalc *calc, FuncExtra *extra) +{ + // check number of parameters + if (!paramCountOkay (args.count())) + return Value::errorVALUE(); + + // do we need to perform array expansion ? + bool mustExpandArray = false; + if (!d->acceptArray) + for (unsigned int i = 0; i < args.count(); ++i) { + if (args[i].isArray()) + mustExpandArray = true; + } + + if( !d->ptr ) return Value::errorVALUE(); + + // perform the actual array expansion if need be + + if (mustExpandArray) { + // compute number of rows/cols of the result + int rows = 0; + int cols = 0; + for (unsigned int i = 0; i < args.count(); ++i) { + int x = (args[i].type() == Value::Array) ? args[i].rows() : 1; + if (x > rows) rows = x; + x = (args[i].type() == Value::Array) ? args[i].columns() : 1; + if (x > cols) cols = x; + } + // allocate the resulting array + Value res (cols, rows); + // perform the actual computation for each element of the array + for (int row = 0; row < rows; ++row) + for (int col = 0; col < cols; ++col) { + // fill in the parameter vector + valVector vals (args.count()); + for (unsigned int i = 0; i < args.count(); ++i) { + int r = args[i].rows(); + int c = args[i].columns(); + vals[i] = args[i].isArray() ? + args[i].element (col % c, row % r): args[i]; + } + + // execute the function on each element + res.setElement (col, row, exec (vals, calc, extra)); + } + return res; + } + else + // call the function + return (*d->ptr) (args, calc, extra); +} + + +// these are defined in kspread_function_*.cpp +void RegisterConversionFunctions(); +void RegisterDatabaseFunctions(); +void RegisterDateTimeFunctions(); +void RegisterEngineeringFunctions(); +void RegisterFinancialFunctions(); +void RegisterInformationFunctions(); +void RegisterLogicFunctions(); +void RegisterMathFunctions(); +void RegisterReferenceFunctions(); +void RegisterStatisticalFunctions(); +void RegisterTextFunctions(); +void RegisterTrigFunctions(); + + +static KStaticDeleter<FunctionRepository> fr_sd; +FunctionRepository* FunctionRepository::s_self = 0; + +FunctionRepository* FunctionRepository::self() +{ + if( !s_self ) + { + kdDebug() << "Creating function repository" << endl; + + fr_sd.setObject( s_self, new FunctionRepository() ); + + kdDebug() << "Registering functions" << endl; + + // register all existing functions + RegisterConversionFunctions(); + RegisterDatabaseFunctions(); + RegisterDateTimeFunctions(); + RegisterEngineeringFunctions(); + RegisterFinancialFunctions(); + RegisterInformationFunctions(); + RegisterLogicFunctions(); + RegisterMathFunctions(); + RegisterReferenceFunctions(); + RegisterStatisticalFunctions(); + RegisterTextFunctions(); + RegisterTrigFunctions(); + + kdDebug() << "Functions registered, loading descriptions" << endl; + + // find all XML description files + TQStringList files = Factory::global()->dirs()->findAllResources + ("extensions", "*.xml", TRUE); + + // load desc/help from XML file + for( TQStringList::Iterator it = files.begin(); it != files.end(); ++it ) + s_self->loadFile (*it); + + kdDebug() << "All ok, repository ready" << endl; + + } + return s_self; +} + +FunctionRepository::FunctionRepository() +{ + d = new Private; + + d->functions.setAutoDelete( true ); + d->funcs.setAutoDelete( true ); +} + +FunctionRepository::~FunctionRepository() +{ + delete d; + s_self = 0; +} + +void FunctionRepository::add( Function* function ) +{ + if( !function ) return; + d->functions.insert( function->name().upper(), function ); +} + +Function *FunctionRepository::function (const TQString& name) +{ + return d->functions.find (name.upper()); +} + +FunctionDescription *FunctionRepository::functionInfo (const TQString& name) +{ + return d->funcs.find (name.upper()); +} + +// returns names of function in certain group +TQStringList FunctionRepository::functionNames( const TQString& group ) +{ + TQStringList lst; + + TQDictIterator<FunctionDescription> it (d->funcs); + for(; it.current(); ++it) { + if (group.isNull() || (it.current()->group() == group)) + lst.append (it.current()->name()); + } + + lst.sort(); + return lst; +} + +void FunctionRepository::loadFile (const TQString& filename) +{ + TQFile file (filename); + if (!file.open (IO_ReadOnly)) + return; + + TQDomDocument doc; + doc.setContent( &file ); + file.close(); + + TQString group = ""; + + TQDomNode n = doc.documentElement().firstChild(); + for (; !n.isNull(); n = n.nextSibling()) + { + if (!n.isElement()) + continue; + TQDomElement e = n.toElement(); + if (e.tagName() == "Group") + { + group = i18n (e.namedItem ("GroupName").toElement().text().utf8()); + m_groups.append( group ); + m_groups.sort(); + + TQDomNode n2 = e.firstChild(); + for (; !n2.isNull(); n2 = n2.nextSibling()) + { + if (!n2.isElement()) + continue; + TQDomElement e2 = n2.toElement(); + if (e2.tagName() == "Function") + { + FunctionDescription* desc = new FunctionDescription( e2 ); + desc->setGroup (group); + if (d->functions.find (desc->name())) + d->funcs.insert (desc->name(), desc); + } + } + group = ""; + } + } +} + +// ------------------------------------------------------------ + +static ParameterType toType( const TQString& type ) +{ + if ( type == "Boolean" ) + return KSpread_Boolean; + if ( type == "Int" ) + return KSpread_Int; + if ( type == "String" ) + return KSpread_String; + if ( type == "Any" ) + return KSpread_Any; + + return KSpread_Float; +} + +static TQString toString (ParameterType type, bool range = FALSE) +{ + if ( !range ) + { + switch(type) { + case KSpread_String: + return i18n("Text"); + case KSpread_Int: + return i18n("Whole number (like 1, 132, 2344)"); + case KSpread_Boolean: + return i18n("A truth value (TRUE or FALSE)" ); + case KSpread_Float: + return i18n("A floating point value (like 1.3, 0.343, 253 )" ); + case KSpread_Any: + return i18n("Any kind of value"); + } + } + else + { + switch(type) { + case KSpread_String: + return i18n("A range of strings"); + case KSpread_Int: + return i18n("A range of whole numbers (like 1, 132, 2344)"); + case KSpread_Boolean: + return i18n("A range of truth values (TRUE or FALSE)" ); + case KSpread_Float: + return i18n("A range of floating point values (like 1.3, 0.343, 253 )" ); + case KSpread_Any: + return i18n("A range of any kind of values"); + } + } + + return TQString(); +} + +FunctionParameter::FunctionParameter() +{ + m_type = KSpread_Float; + m_range = FALSE; +} + +FunctionParameter::FunctionParameter (const FunctionParameter& param) +{ + m_help = param.m_help; + m_type = param.m_type; + m_range = param.m_range; +} + +FunctionParameter::FunctionParameter (const TQDomElement& element) +{ + m_type = KSpread_Float; + m_range = FALSE; + + TQDomNode n = element.firstChild(); + for( ; !n.isNull(); n = n.nextSibling() ) + if ( n.isElement() ) + { + TQDomElement e = n.toElement(); + if ( e.tagName() == "Comment" ) + m_help = i18n( e.text().utf8() ); + else if ( e.tagName() == "Type" ) + { + m_type = toType( e.text() ); + if ( e.hasAttribute( "range" )) + { + if (e.attribute("range").lower() == "true") + m_range = TRUE; + } + } + } +} + +FunctionDescription::FunctionDescription() +{ + m_type = KSpread_Float; +} + +FunctionDescription::FunctionDescription (const TQDomElement& element) +{ + TQDomNode n = element.firstChild(); + for( ; !n.isNull(); n = n.nextSibling() ) + { + if (!n.isElement()) + continue; + TQDomElement e = n.toElement(); + if ( e.tagName() == "Name" ) + m_name = e.text(); + else if ( e.tagName() == "Type" ) + m_type = toType( e.text() ); + else if ( e.tagName() == "Parameter" ) + m_params.append (FunctionParameter (e)); + else if ( e.tagName() == "Help" ) + { + TQDomNode n2 = e.firstChild(); + for( ; !n2.isNull(); n2 = n2.nextSibling() ) + { + if (!n2.isElement()) + continue; + TQDomElement e2 = n2.toElement(); + if ( e2.tagName() == "Text" ) + m_help.append ( i18n( e2.text().utf8() ) ); + else if ( e2.tagName() == "Syntax" ) + m_syntax.append( i18n( e2.text().utf8() ) ); + else if ( e2.tagName() == "Example" ) + m_examples.append( i18n( e2.text().utf8() ) ); + else if ( e2.tagName() == "Related" ) + m_related.append( i18n( e2.text().utf8() ) ); + } + } + } +} + +FunctionDescription::FunctionDescription( const FunctionDescription& desc ) +{ + m_examples = desc.m_examples; + m_related = desc.m_related; + m_syntax = desc.m_syntax; + m_help = desc.m_help; + m_name = desc.m_name; + m_type = desc.m_type; +} + +TQString FunctionDescription::toTQML() const +{ + TQString text( "<qt><h1>" ); + text += name(); + text += "</h1>"; + + if( !m_help.isEmpty() ) + { + text += i18n("<p>"); + TQStringList::ConstIterator it = m_help.begin(); + for( ; it != m_help.end(); ++it ) + { + text += *it; + text += "<p>"; + } + text += "</p>"; + } + + text += i18n("<p><b>Return type: </b>"); + text += toString( type() ); + text += "</p>"; + + if ( !m_syntax.isEmpty() ) + { + text += i18n("<h2>Syntax</h2><ul>"); + TQStringList::ConstIterator it = m_syntax.begin(); + for( ; it != m_syntax.end(); ++it ) + { + text += "<li>"; + text += *it; + } + text += "</ul>"; + } + + if ( !m_params.isEmpty() ) + { + text += i18n("<h2>Parameters</h2><ul>"); + TQValueList<FunctionParameter>::ConstIterator it = m_params.begin(); + for( ; it != m_params.end(); ++it ) + { + text += i18n("<li><b>Comment:</b> "); + text += (*it).helpText(); + text += i18n("<br><b>Type:</b> "); + text += toString( (*it).type(), (*it).hasRange() ); + } + text += "</ul>"; + } + + if ( !m_examples.isEmpty() ) + { + text += i18n("<h2>Examples</h2><ul>"); + TQStringList::ConstIterator it = m_examples.begin(); + for( ; it != m_examples.end(); ++it ) + { + text += "<li>"; + text += *it; + } + text += "</ul>"; + } + + if ( !m_related.isEmpty() ) + { + text += i18n("<h2>Related Functions</h2><ul>"); + TQStringList::ConstIterator it = m_related.begin(); + for( ; it != m_related.end(); ++it ) + { + text += "<li>"; + text += "<a href=\"" + *it + "\">"; + text += *it; + text += "</a>"; + } + text += "</ul>"; + } + + text += "</qt>"; + return text; +} |