kspread
functions.cc00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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;
00048 };
00049
00050 class FunctionRepository::Private
00051 {
00052 public:
00053 QDict<Function> functions;
00054 QDict<FunctionDescription> funcs;
00055 };
00056
00057 }
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
00092 if (count < d->paramMin) return false;
00093
00094 if (d->paramMax == -1) return true;
00095
00096 if (count > d->paramMax) return false;
00097
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
00115 if (!paramCountOkay (args.count()))
00116 return Value::errorVALUE();
00117
00118
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
00129
00130 if (mustExpandArray) {
00131
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
00141 Value res (cols, rows);
00142
00143 for (int row = 0; row < rows; ++row)
00144 for (int col = 0; col < cols; ++col) {
00145
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
00155 res.setElement (col, row, exec (vals, calc, extra));
00156 }
00157 return res;
00158 }
00159 else
00160
00161 return (*d->ptr) (args, calc, extra);
00162 }
00163
00164
00165
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
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
00210 QStringList files = Factory::global()->dirs()->findAllResources
00211 ("extensions", "*.xml", TRUE);
00212
00213
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
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 }
|