kspread

functions.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
00003    Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "formula.h"
00022 #include "functions.h"
00023 #include "valuecalc.h"
00024 
00025 #include <qdict.h>
00026 #include <qdom.h>
00027 #include <qfile.h>
00028 #include <qvaluevector.h>
00029 
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kstandarddirs.h>
00033 #include <kstaticdeleter.h>
00034 
00035 #include "kspread_factory.h"
00036 
00037 namespace KSpread
00038 {
00039 
00040 class Function::Private
00041 {
00042 public:
00043   QString name;
00044   FunctionPtr ptr;
00045   int paramMin, paramMax;
00046   bool acceptArray;
00047   bool ne;   // need FunctionExtra* when called ?
00048 };
00049 
00050 class FunctionRepository::Private
00051 {
00052 public:
00053   QDict<Function> functions;
00054   QDict<FunctionDescription> funcs;
00055 };
00056 
00057 } // namespace KSpread
00058 
00059 
00060 using namespace KSpread;
00061 
00062 Function::Function( const QString& name, FunctionPtr ptr )
00063 {
00064   d = new Private;
00065   d->name = name;
00066   d->ptr = ptr;
00067   d->acceptArray = false;
00068   d->paramMin = 1;
00069   d->paramMax = 1;
00070   d->ne = false;
00071 }
00072 
00073 Function::~Function()
00074 {
00075   delete d;
00076 }
00077 
00078 QString Function::name() const
00079 {
00080   return d->name;
00081 }
00082 
00083 void Function::setParamCount (int min, int max)
00084 {
00085   d->paramMin = min;
00086   d->paramMax = (max == 0) ? min : max;
00087 }
00088 
00089 bool Function::paramCountOkay (int count)
00090 {
00091   // less than needed
00092   if (count < d->paramMin) return false;
00093   // no upper limit
00094   if (d->paramMax == -1) return true;
00095   // more than needed
00096   if (count > d->paramMax) return false;
00097   // okay otherwise
00098   return true;
00099 }
00100 
00101 void Function::setAcceptArray (bool accept) {
00102   d->acceptArray = accept;
00103 }
00104 
00105 bool Function::needsExtra () {
00106   return d->ne;
00107 }
00108 void Function::setNeedsExtra (bool extra) {
00109   d->ne = extra;
00110 }
00111 
00112 Value Function::exec (valVector args, ValueCalc *calc, FuncExtra *extra)
00113 {
00114   // check number of parameters
00115   if (!paramCountOkay (args.count()))
00116     return Value::errorVALUE();
00117 
00118   // do we need to perform array expansion ?
00119   bool mustExpandArray = false;
00120   if (!d->acceptArray)
00121     for (unsigned int i = 0; i < args.count(); ++i) {
00122       if (args[i].isArray())
00123         mustExpandArray = true;
00124     }
00125 
00126   if( !d->ptr ) return Value::errorVALUE();
00127   
00128   // perform the actual array expansion if need be
00129   
00130   if (mustExpandArray) {
00131     // compute number of rows/cols of the result
00132     int rows = 0;
00133     int cols = 0;
00134     for (unsigned int i = 0; i < args.count(); ++i) {
00135       int x = (args[i].type() == Value::Array) ? args[i].rows() : 1;
00136       if (x > rows) rows = x;
00137       x = (args[i].type() == Value::Array) ? args[i].columns() : 1;
00138       if (x > cols) cols = x;
00139     }
00140     // allocate the resulting array
00141     Value res (cols, rows);
00142     // perform the actual computation for each element of the array
00143     for (int row = 0; row < rows; ++row)
00144       for (int col = 0; col < cols; ++col) {
00145         // fill in the parameter vector
00146         valVector vals (args.count());
00147         for (unsigned int i = 0; i < args.count(); ++i) {
00148           int r = args[i].rows();
00149           int c = args[i].columns();
00150           vals[i] = args[i].isArray() ?
00151               args[i].element (col % c, row % r): args[i];
00152         }
00153         
00154         // execute the function on each element
00155         res.setElement (col, row, exec (vals, calc, extra));
00156       }
00157     return res;
00158   }
00159   else
00160     // call the function
00161     return (*d->ptr) (args, calc, extra);
00162 }
00163 
00164 
00165 // these are defined in kspread_function_*.cc
00166 void RegisterConversionFunctions();
00167 void RegisterDatabaseFunctions();
00168 void RegisterDateTimeFunctions();
00169 void RegisterEngineeringFunctions();
00170 void RegisterFinancialFunctions();
00171 void RegisterInformationFunctions();
00172 void RegisterLogicFunctions();
00173 void RegisterMathFunctions();
00174 void RegisterReferenceFunctions();
00175 void RegisterStatisticalFunctions();
00176 void RegisterTextFunctions();
00177 void RegisterTrigFunctions();
00178 
00179 
00180 static KStaticDeleter<FunctionRepository> fr_sd;
00181 FunctionRepository* FunctionRepository::s_self = 0;
00182 
00183 FunctionRepository* FunctionRepository::self()
00184 {
00185   if( !s_self )
00186   {
00187     kdDebug() << "Creating function repository" << endl;
00188   
00189     fr_sd.setObject( s_self, new FunctionRepository() );
00190   
00191     kdDebug() << "Registering functions" << endl;
00192     
00193     // register all existing functions
00194     RegisterConversionFunctions();
00195     RegisterDatabaseFunctions();
00196     RegisterDateTimeFunctions();
00197     RegisterEngineeringFunctions();
00198     RegisterFinancialFunctions();
00199     RegisterInformationFunctions();
00200     RegisterLogicFunctions();
00201     RegisterMathFunctions();
00202     RegisterReferenceFunctions();
00203     RegisterStatisticalFunctions();
00204     RegisterTextFunctions();
00205     RegisterTrigFunctions();
00206   
00207     kdDebug() << "Functions registered, loading descriptions" << endl;
00208   
00209     // find all XML description files
00210     QStringList files = Factory::global()->dirs()->findAllResources
00211         ("extensions", "*.xml", TRUE);
00212     
00213     // load desc/help from XML file
00214     for( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00215       s_self->loadFile (*it);
00216   
00217     kdDebug() << "All ok, repository ready" << endl;
00218 
00219   }    
00220   return s_self;
00221 }
00222 
00223 FunctionRepository::FunctionRepository()
00224 {
00225   d = new Private;
00226   
00227   d->functions.setAutoDelete( true );
00228   d->funcs.setAutoDelete( true );
00229 }
00230 
00231 FunctionRepository::~FunctionRepository()
00232 {
00233   delete d;
00234   s_self = 0;
00235 }
00236 
00237 void FunctionRepository::add( Function* function )
00238 {
00239   if( !function ) return;
00240   d->functions.insert( function->name().upper(), function );
00241 }
00242 
00243 Function *FunctionRepository::function (const QString& name)
00244 {
00245   return d->functions.find (name.upper());
00246 }
00247 
00248 FunctionDescription *FunctionRepository::functionInfo (const QString& name)
00249 {
00250   return d->funcs.find (name.upper());
00251 }
00252 
00253 // returns names of function in certain group
00254 QStringList FunctionRepository::functionNames( const QString& group )
00255 {
00256   QStringList lst;
00257 
00258   QDictIterator<FunctionDescription> it (d->funcs);
00259   for(; it.current(); ++it) {
00260     if (group.isNull() || (it.current()->group() == group))
00261       lst.append (it.current()->name());
00262   }
00263   
00264   lst.sort();
00265   return lst;
00266 }
00267 
00268 void FunctionRepository::loadFile (const QString& filename)
00269 {
00270   QFile file (filename);
00271   if (!file.open (IO_ReadOnly))
00272     return;
00273 
00274   QDomDocument doc;
00275   doc.setContent( &file );
00276   file.close();
00277 
00278   QString group = "";
00279 
00280   QDomNode n = doc.documentElement().firstChild();
00281   for (; !n.isNull(); n = n.nextSibling())
00282   {
00283     if (!n.isElement())
00284       continue;
00285     QDomElement e = n.toElement();
00286     if (e.tagName() == "Group")
00287     {
00288       group = i18n (e.namedItem ("GroupName").toElement().text().utf8());
00289       m_groups.append( group );
00290       m_groups.sort();
00291     
00292       QDomNode n2 = e.firstChild();
00293       for (; !n2.isNull(); n2 = n2.nextSibling())
00294       {
00295         if (!n2.isElement())
00296           continue;
00297         QDomElement e2 = n2.toElement();
00298         if (e2.tagName() == "Function")
00299         {
00300           FunctionDescription* desc = new FunctionDescription( e2 );
00301           desc->setGroup (group);
00302           if (d->functions.find (desc->name()))
00303             d->funcs.insert (desc->name(), desc);
00304         }
00305       }
00306       group = "";
00307     }
00308   }
00309 }
00310 
00311 // ------------------------------------------------------------
00312 
00313 static ParameterType toType( const QString& type )
00314 {
00315   if ( type == "Boolean" )
00316     return KSpread_Boolean;
00317   if ( type == "Int" )
00318     return KSpread_Int;
00319   if ( type == "String" )
00320     return KSpread_String;
00321   if ( type == "Any" )
00322     return KSpread_Any;
00323 
00324   return KSpread_Float;
00325 }
00326 
00327 static QString toString (ParameterType type, bool range = FALSE)
00328 {
00329   if ( !range )
00330   {
00331     switch(type) {
00332       case KSpread_String:
00333         return i18n("Text");
00334       case KSpread_Int:
00335         return i18n("Whole number (like 1, 132, 2344)");
00336       case KSpread_Boolean:
00337         return i18n("A truth value (TRUE or FALSE)" );
00338       case KSpread_Float:
00339         return i18n("A floating point value (like 1.3, 0.343, 253 )" );
00340       case KSpread_Any:
00341         return i18n("Any kind of value");
00342     }
00343   }
00344   else
00345   {
00346     switch(type) {
00347       case KSpread_String:
00348         return i18n("A range of strings");
00349       case KSpread_Int:
00350         return i18n("A range of whole numbers (like 1, 132, 2344)");
00351       case KSpread_Boolean:
00352         return i18n("A range of truth values (TRUE or FALSE)" );
00353       case KSpread_Float:
00354         return i18n("A range of floating point values (like 1.3, 0.343, 253 )" );
00355       case KSpread_Any:
00356         return i18n("A range of any kind of values");
00357     }
00358   }
00359 
00360   return QString::null;
00361 }
00362 
00363 FunctionParameter::FunctionParameter()
00364 {
00365   m_type = KSpread_Float;
00366   m_range = FALSE;
00367 }
00368 
00369 FunctionParameter::FunctionParameter (const FunctionParameter& param)
00370 {
00371   m_help = param.m_help;
00372   m_type = param.m_type;
00373   m_range = param.m_range;
00374 }
00375 
00376 FunctionParameter::FunctionParameter (const QDomElement& element)
00377 {
00378   m_type  = KSpread_Float;
00379   m_range = FALSE;
00380 
00381   QDomNode n = element.firstChild();
00382   for( ; !n.isNull(); n = n.nextSibling() )
00383     if ( n.isElement() )
00384     {
00385       QDomElement e = n.toElement();
00386       if ( e.tagName() == "Comment" )
00387         m_help = i18n( e.text().utf8() );
00388       else if ( e.tagName() == "Type" )
00389       {
00390         m_type = toType( e.text() );
00391         if ( e.hasAttribute( "range" ))
00392         {
00393           if (e.attribute("range").lower() == "true")
00394             m_range = TRUE;
00395         }
00396       }
00397     }
00398 }
00399 
00400 FunctionDescription::FunctionDescription()
00401 {
00402   m_type = KSpread_Float;
00403 }
00404 
00405 FunctionDescription::FunctionDescription (const QDomElement& element)
00406 {
00407   QDomNode n = element.firstChild();
00408   for( ; !n.isNull(); n = n.nextSibling() )
00409   {
00410     if (!n.isElement())
00411       continue;
00412     QDomElement e = n.toElement();
00413     if ( e.tagName() == "Name" )
00414       m_name = e.text();
00415     else if ( e.tagName() == "Type" )
00416       m_type = toType( e.text() );
00417     else if ( e.tagName() == "Parameter" )
00418       m_params.append (FunctionParameter (e));
00419     else if ( e.tagName() == "Help" )
00420     {
00421       QDomNode n2 = e.firstChild();
00422       for( ; !n2.isNull(); n2 = n2.nextSibling() )
00423       {
00424         if (!n2.isElement())
00425           continue;
00426         QDomElement e2 = n2.toElement();
00427         if ( e2.tagName() == "Text" )
00428           m_help.append ( i18n( e2.text().utf8() ) );
00429         else if ( e2.tagName() == "Syntax" )
00430           m_syntax.append( i18n( e2.text().utf8() ) );
00431         else if ( e2.tagName() == "Example" )
00432           m_examples.append( i18n( e2.text().utf8() ) );
00433         else if ( e2.tagName() == "Related" )
00434           m_related.append( i18n( e2.text().utf8() ) );
00435       }
00436     }
00437   }
00438 }
00439 
00440 FunctionDescription::FunctionDescription( const FunctionDescription& desc )
00441 {
00442   m_examples = desc.m_examples;
00443   m_related = desc.m_related;
00444   m_syntax = desc.m_syntax;
00445   m_help = desc.m_help;
00446   m_name = desc.m_name;
00447   m_type = desc.m_type;
00448 }
00449 
00450 QString FunctionDescription::toQML() const
00451 {
00452   QString text( "<qt><h1>" );
00453   text += name();
00454   text += "</h1>";
00455 
00456   if( !m_help.isEmpty() )
00457   {
00458     text += i18n("<p>");
00459     QStringList::ConstIterator it = m_help.begin();
00460     for( ; it != m_help.end(); ++it )
00461     {
00462       text += *it;
00463       text += "<p>";
00464     }
00465     text += "</p>";
00466   }
00467 
00468   text += i18n("<p><b>Return type: </b>");
00469   text += toString( type() );
00470   text += "</p>";
00471 
00472   if ( !m_syntax.isEmpty() )
00473   {
00474     text += i18n("<h2>Syntax</h2><ul>");
00475     QStringList::ConstIterator it = m_syntax.begin();
00476     for( ; it != m_syntax.end(); ++it )
00477     {
00478       text += "<li>";
00479       text += *it;
00480     }
00481     text += "</ul>";
00482   }
00483 
00484   if ( !m_params.isEmpty() )
00485   {
00486     text += i18n("<h2>Parameters</h2><ul>");
00487     QValueList<FunctionParameter>::ConstIterator it = m_params.begin();
00488     for( ; it != m_params.end(); ++it )
00489     {
00490       text += i18n("<li><b>Comment:</b> ");
00491       text += (*it).helpText();
00492       text += i18n("<br><b>Type:</b> ");
00493       text += toString( (*it).type(), (*it).hasRange() );
00494     }
00495     text += "</ul>";
00496   }
00497 
00498   if ( !m_examples.isEmpty() )
00499   {
00500     text += i18n("<h2>Examples</h2><ul>");
00501     QStringList::ConstIterator it = m_examples.begin();
00502     for( ; it != m_examples.end(); ++it )
00503     {
00504       text += "<li>";
00505       text += *it;
00506     }
00507     text += "</ul>";
00508   }
00509 
00510   if ( !m_related.isEmpty() )
00511   {
00512     text += i18n("<h2>Related Functions</h2><ul>");
00513     QStringList::ConstIterator it = m_related.begin();
00514     for( ; it != m_related.end(); ++it )
00515     {
00516       text += "<li>";
00517       text += "<a href=\"" + *it + "\">";
00518       text += *it;
00519       text += "</a>";
00520     }
00521     text += "</ul>";
00522   }
00523 
00524   text += "</qt>";
00525   return text;
00526 }
KDE Home | KDE Accessibility Home | Description of Access Keys