summaryrefslogtreecommitdiffstats
path: root/kspread/functions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kspread/functions.cpp')
-rw-r--r--kspread/functions.cpp526
1 files changed, 526 insertions, 0 deletions
diff --git a/kspread/functions.cpp b/kspread/functions.cpp
new file mode 100644
index 000000000..fa485171a
--- /dev/null
+++ b/kspread/functions.cpp
@@ -0,0 +1,526 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
+ Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
+
+ 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;
+}