/* This file is part of the KDE project Copyright (C) 2003,2004 Ariya Hidayat Copyright (C) 2005 Tomas Mecir 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 #include #include #include #include #include #include #include #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 functions; TQDict 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 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 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( "

" ); text += name(); text += "

"; if( !m_help.isEmpty() ) { text += i18n("

"); TQStringList::ConstIterator it = m_help.begin(); for( ; it != m_help.end(); ++it ) { text += *it; text += "

"; } text += "

"; } text += i18n("

Return type: "); text += toString( type() ); text += "

"; if ( !m_syntax.isEmpty() ) { text += i18n("

Syntax

    "); TQStringList::ConstIterator it = m_syntax.begin(); for( ; it != m_syntax.end(); ++it ) { text += "
  • "; text += *it; } text += "
"; } if ( !m_params.isEmpty() ) { text += i18n("

Parameters

    "); TQValueList::ConstIterator it = m_params.begin(); for( ; it != m_params.end(); ++it ) { text += i18n("
  • Comment: "); text += (*it).helpText(); text += i18n("
    Type: "); text += toString( (*it).type(), (*it).hasRange() ); } text += "
"; } if ( !m_examples.isEmpty() ) { text += i18n("

Examples

    "); TQStringList::ConstIterator it = m_examples.begin(); for( ; it != m_examples.end(); ++it ) { text += "
  • "; text += *it; } text += "
"; } if ( !m_related.isEmpty() ) { text += i18n("

Related Functions

    "); TQStringList::ConstIterator it = m_related.begin(); for( ; it != m_related.end(); ++it ) { text += "
  • "; text += ""; text += *it; text += ""; } text += "
"; } text += "
"; return text; }