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.
527 lines
13 KiB
527 lines
13 KiB
/* 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 <klocale.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_*.cc
|
|
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;
|
|
}
|