kspread

functions.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003,2004 Ariya Hidayat <ariya@kde.org>
3 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "formula.h"
22#include "functions.h"
23#include "valuecalc.h"
24
25#include <tqdict.h>
26#include <tqdom.h>
27#include <tqfile.h>
28#include <tqvaluevector.h>
29
30#include <kdebug.h>
31#include <tdelocale.h>
32#include <kstandarddirs.h>
33#include <kstaticdeleter.h>
34
35#include "kspread_factory.h"
36
37namespace KSpread
38{
39
40class Function::Private
41{
42public:
43 TQString name;
44 FunctionPtr ptr;
45 int paramMin, paramMax;
46 bool acceptArray;
47 bool ne; // need FunctionExtra* when called ?
48};
49
50class FunctionRepository::Private
51{
52public:
53 TQDict<Function> functions;
54 TQDict<FunctionDescription> funcs;
55};
56
57} // namespace KSpread
58
59
60using namespace KSpread;
61
62Function::Function( const TQString& name, FunctionPtr ptr )
63{
64 d = new Private;
65 d->name = name;
66 d->ptr = ptr;
67 d->acceptArray = false;
68 d->paramMin = 1;
69 d->paramMax = 1;
70 d->ne = false;
71}
72
73Function::~Function()
74{
75 delete d;
76}
77
78TQString Function::name() const
79{
80 return d->name;
81}
82
83void Function::setParamCount (int min, int max)
84{
85 d->paramMin = min;
86 d->paramMax = (max == 0) ? min : max;
87}
88
89bool Function::paramCountOkay (int count)
90{
91 // less than needed
92 if (count < d->paramMin) return false;
93 // no upper limit
94 if (d->paramMax == -1) return true;
95 // more than needed
96 if (count > d->paramMax) return false;
97 // okay otherwise
98 return true;
99}
100
101void Function::setAcceptArray (bool accept) {
102 d->acceptArray = accept;
103}
104
105bool Function::needsExtra () {
106 return d->ne;
107}
108void Function::setNeedsExtra (bool extra) {
109 d->ne = extra;
110}
111
112Value Function::exec (valVector args, ValueCalc *calc, FuncExtra *extra)
113{
114 // check number of parameters
115 if (!paramCountOkay (args.count()))
116 return Value::errorVALUE();
117
118 // do we need to perform array expansion ?
119 bool mustExpandArray = false;
120 if (!d->acceptArray)
121 for (unsigned int i = 0; i < args.count(); ++i) {
122 if (args[i].isArray())
123 mustExpandArray = true;
124 }
125
126 if( !d->ptr ) return Value::errorVALUE();
127
128 // perform the actual array expansion if need be
129
130 if (mustExpandArray) {
131 // compute number of rows/cols of the result
132 int rows = 0;
133 int cols = 0;
134 for (unsigned int i = 0; i < args.count(); ++i) {
135 int x = (args[i].type() == Value::Array) ? args[i].rows() : 1;
136 if (x > rows) rows = x;
137 x = (args[i].type() == Value::Array) ? args[i].columns() : 1;
138 if (x > cols) cols = x;
139 }
140 // allocate the resulting array
141 Value res (cols, rows);
142 // perform the actual computation for each element of the array
143 for (int row = 0; row < rows; ++row)
144 for (int col = 0; col < cols; ++col) {
145 // fill in the parameter vector
146 valVector vals (args.count());
147 for (unsigned int i = 0; i < args.count(); ++i) {
148 int r = args[i].rows();
149 int c = args[i].columns();
150 vals[i] = args[i].isArray() ?
151 args[i].element (col % c, row % r): args[i];
152 }
153
154 // execute the function on each element
155 res.setElement (col, row, exec (vals, calc, extra));
156 }
157 return res;
158 }
159 else
160 // call the function
161 return (*d->ptr) (args, calc, extra);
162}
163
164
165// these are defined in kspread_function_*.cpp
166void RegisterConversionFunctions();
167void RegisterDatabaseFunctions();
168void RegisterDateTimeFunctions();
169void RegisterEngineeringFunctions();
170void RegisterFinancialFunctions();
171void RegisterInformationFunctions();
172void RegisterLogicFunctions();
173void RegisterMathFunctions();
174void RegisterReferenceFunctions();
175void RegisterStatisticalFunctions();
176void RegisterTextFunctions();
177void RegisterTrigFunctions();
178
179
180static KStaticDeleter<FunctionRepository> fr_sd;
181FunctionRepository* FunctionRepository::s_self = 0;
182
183FunctionRepository* FunctionRepository::self()
184{
185 if( !s_self )
186 {
187 kdDebug() << "Creating function repository" << endl;
188
189 fr_sd.setObject( s_self, new FunctionRepository() );
190
191 kdDebug() << "Registering functions" << endl;
192
193 // register all existing functions
194 RegisterConversionFunctions();
195 RegisterDatabaseFunctions();
196 RegisterDateTimeFunctions();
197 RegisterEngineeringFunctions();
198 RegisterFinancialFunctions();
199 RegisterInformationFunctions();
200 RegisterLogicFunctions();
201 RegisterMathFunctions();
202 RegisterReferenceFunctions();
203 RegisterStatisticalFunctions();
204 RegisterTextFunctions();
205 RegisterTrigFunctions();
206
207 kdDebug() << "Functions registered, loading descriptions" << endl;
208
209 // find all XML description files
210 TQStringList files = Factory::global()->dirs()->findAllResources
211 ("extensions", "*.xml", true);
212
213 // load desc/help from XML file
214 for( TQStringList::Iterator it = files.begin(); it != files.end(); ++it )
215 s_self->loadFile (*it);
216
217 kdDebug() << "All ok, repository ready" << endl;
218
219 }
220 return s_self;
221}
222
223FunctionRepository::FunctionRepository()
224{
225 d = new Private;
226
227 d->functions.setAutoDelete( true );
228 d->funcs.setAutoDelete( true );
229}
230
231FunctionRepository::~FunctionRepository()
232{
233 delete d;
234 s_self = 0;
235}
236
237void FunctionRepository::add( Function* function )
238{
239 if( !function ) return;
240 d->functions.insert( function->name().upper(), function );
241}
242
243Function *FunctionRepository::function (const TQString& name)
244{
245 return d->functions.find (name.upper());
246}
247
248FunctionDescription *FunctionRepository::functionInfo (const TQString& name)
249{
250 return d->funcs.find (name.upper());
251}
252
253// returns names of function in certain group
254TQStringList FunctionRepository::functionNames( const TQString& group )
255{
256 TQStringList lst;
257
258 TQDictIterator<FunctionDescription> it (d->funcs);
259 for(; it.current(); ++it) {
260 if (group.isNull() || (it.current()->group() == group))
261 lst.append (it.current()->name());
262 }
263
264 lst.sort();
265 return lst;
266}
267
268void FunctionRepository::loadFile (const TQString& filename)
269{
270 TQFile file (filename);
271 if (!file.open (IO_ReadOnly))
272 return;
273
274 TQDomDocument doc;
275 doc.setContent( &file );
276 file.close();
277
278 TQString group = "";
279
280 TQDomNode n = doc.documentElement().firstChild();
281 for (; !n.isNull(); n = n.nextSibling())
282 {
283 if (!n.isElement())
284 continue;
285 TQDomElement e = n.toElement();
286 if (e.tagName() == "Group")
287 {
288 group = i18n (e.namedItem ("GroupName").toElement().text().utf8());
289 m_groups.append( group );
290 m_groups.sort();
291
292 TQDomNode n2 = e.firstChild();
293 for (; !n2.isNull(); n2 = n2.nextSibling())
294 {
295 if (!n2.isElement())
296 continue;
297 TQDomElement e2 = n2.toElement();
298 if (e2.tagName() == "Function")
299 {
300 FunctionDescription* desc = new FunctionDescription( e2 );
301 desc->setGroup (group);
302 if (d->functions.find (desc->name()))
303 d->funcs.insert (desc->name(), desc);
304 }
305 }
306 group = "";
307 }
308 }
309}
310
311// ------------------------------------------------------------
312
313static ParameterType toType( const TQString& type )
314{
315 if ( type == "Boolean" )
316 return KSpread_Boolean;
317 if ( type == "Int" )
318 return KSpread_Int;
319 if ( type == "String" )
320 return KSpread_String;
321 if ( type == "Any" )
322 return KSpread_Any;
323
324 return KSpread_Float;
325}
326
327static TQString toString (ParameterType type, bool range = false)
328{
329 if ( !range )
330 {
331 switch(type) {
332 case KSpread_String:
333 return i18n("Text");
334 case KSpread_Int:
335 return i18n("Whole number (like 1, 132, 2344)");
336 case KSpread_Boolean:
337 return i18n("A truth value (TRUE or FALSE)" );
338 case KSpread_Float:
339 return i18n("A floating point value (like 1.3, 0.343, 253 )" );
340 case KSpread_Any:
341 return i18n("Any kind of value");
342 }
343 }
344 else
345 {
346 switch(type) {
347 case KSpread_String:
348 return i18n("A range of strings");
349 case KSpread_Int:
350 return i18n("A range of whole numbers (like 1, 132, 2344)");
351 case KSpread_Boolean:
352 return i18n("A range of truth values (TRUE or FALSE)" );
353 case KSpread_Float:
354 return i18n("A range of floating point values (like 1.3, 0.343, 253 )" );
355 case KSpread_Any:
356 return i18n("A range of any kind of values");
357 }
358 }
359
360 return TQString();
361}
362
363FunctionParameter::FunctionParameter()
364{
365 m_type = KSpread_Float;
366 m_range = false;
367}
368
369FunctionParameter::FunctionParameter (const FunctionParameter& param)
370{
371 m_help = param.m_help;
372 m_type = param.m_type;
373 m_range = param.m_range;
374}
375
376FunctionParameter::FunctionParameter (const TQDomElement& element)
377{
378 m_type = KSpread_Float;
379 m_range = false;
380
381 TQDomNode n = element.firstChild();
382 for( ; !n.isNull(); n = n.nextSibling() )
383 if ( n.isElement() )
384 {
385 TQDomElement e = n.toElement();
386 if ( e.tagName() == "Comment" )
387 m_help = i18n( e.text().utf8() );
388 else if ( e.tagName() == "Type" )
389 {
390 m_type = toType( e.text() );
391 if ( e.hasAttribute( "range" ))
392 {
393 if (e.attribute("range").lower() == "true")
394 m_range = true;
395 }
396 }
397 }
398}
399
400FunctionDescription::FunctionDescription()
401{
402 m_type = KSpread_Float;
403}
404
405FunctionDescription::FunctionDescription (const TQDomElement& element)
406{
407 TQDomNode n = element.firstChild();
408 for( ; !n.isNull(); n = n.nextSibling() )
409 {
410 if (!n.isElement())
411 continue;
412 TQDomElement e = n.toElement();
413 if ( e.tagName() == "Name" )
414 m_name = e.text();
415 else if ( e.tagName() == "Type" )
416 m_type = toType( e.text() );
417 else if ( e.tagName() == "Parameter" )
418 m_params.append (FunctionParameter (e));
419 else if ( e.tagName() == "Help" )
420 {
421 TQDomNode n2 = e.firstChild();
422 for( ; !n2.isNull(); n2 = n2.nextSibling() )
423 {
424 if (!n2.isElement())
425 continue;
426 TQDomElement e2 = n2.toElement();
427 if ( e2.tagName() == "Text" )
428 m_help.append ( i18n( e2.text().utf8() ) );
429 else if ( e2.tagName() == "Syntax" )
430 m_syntax.append( i18n( e2.text().utf8() ) );
431 else if ( e2.tagName() == "Example" )
432 m_examples.append( i18n( e2.text().utf8() ) );
433 else if ( e2.tagName() == "Related" )
434 m_related.append( i18n( e2.text().utf8() ) );
435 }
436 }
437 }
438}
439
440FunctionDescription::FunctionDescription( const FunctionDescription& desc )
441{
442 m_examples = desc.m_examples;
443 m_related = desc.m_related;
444 m_syntax = desc.m_syntax;
445 m_help = desc.m_help;
446 m_name = desc.m_name;
447 m_type = desc.m_type;
448}
449
450TQString FunctionDescription::toTQML() const
451{
452 TQString text( "<qt><h1>" );
453 text += name();
454 text += "</h1>";
455
456 if( !m_help.isEmpty() )
457 {
458 text += i18n("<p>");
459 TQStringList::ConstIterator it = m_help.begin();
460 for( ; it != m_help.end(); ++it )
461 {
462 text += *it;
463 text += "<p>";
464 }
465 text += "</p>";
466 }
467
468 text += i18n("<p><b>Return type: </b>");
469 text += toString( type() );
470 text += "</p>";
471
472 if ( !m_syntax.isEmpty() )
473 {
474 text += i18n("<h2>Syntax</h2><ul>");
475 TQStringList::ConstIterator it = m_syntax.begin();
476 for( ; it != m_syntax.end(); ++it )
477 {
478 text += "<li>";
479 text += *it;
480 }
481 text += "</ul>";
482 }
483
484 if ( !m_params.isEmpty() )
485 {
486 text += i18n("<h2>Parameters</h2><ul>");
487 TQValueList<FunctionParameter>::ConstIterator it = m_params.begin();
488 for( ; it != m_params.end(); ++it )
489 {
490 text += i18n("<li><b>Comment:</b> ");
491 text += (*it).helpText();
492 text += i18n("<br><b>Type:</b> ");
493 text += toString( (*it).type(), (*it).hasRange() );
494 }
495 text += "</ul>";
496 }
497
498 if ( !m_examples.isEmpty() )
499 {
500 text += i18n("<h2>Examples</h2><ul>");
501 TQStringList::ConstIterator it = m_examples.begin();
502 for( ; it != m_examples.end(); ++it )
503 {
504 text += "<li>";
505 text += *it;
506 }
507 text += "</ul>";
508 }
509
510 if ( !m_related.isEmpty() )
511 {
512 text += i18n("<h2>Related Functions</h2><ul>");
513 TQStringList::ConstIterator it = m_related.begin();
514 for( ; it != m_related.end(); ++it )
515 {
516 text += "<li>";
517 text += "<a href=\"" + *it + "\">";
518 text += *it;
519 text += "</a>";
520 }
521 text += "</ul>";
522 }
523
524 text += "</qt>";
525 return text;
526}
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
Provides a wrapper for cell value.
static const Value & errorVALUE()
Returns constant reference to #VALUE! error.
The KSpread namespace.
Definition commands.h:39