kspread

kspread_functions_text.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998-2002 The KSpread Team
3 www.koffice.org/kspread
4 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22
23// built-in text functions
24// please keep it in alphabetical order
25
26#include <tqregexp.h>
27#include <kdebug.h>
28#include <tdelocale.h>
29#include <math.h>
30
31#include "functions.h"
32#include "valuecalc.h"
33#include "valueconverter.h"
34
35using namespace KSpread;
36
37// Functions DOLLAR and FIXED convert data to double, hence they will not
38// support arbitrary precision, when it will be introduced.
39
40// prototypes
41Value func_char (valVector args, ValueCalc *calc, FuncExtra *);
42Value func_clean (valVector args, ValueCalc *calc, FuncExtra *);
43Value func_code (valVector args, ValueCalc *calc, FuncExtra *);
44Value func_compare (valVector args, ValueCalc *calc, FuncExtra *);
45Value func_concatenate (valVector args, ValueCalc *calc, FuncExtra *);
46Value func_dollar (valVector args, ValueCalc *calc, FuncExtra *);
47Value func_exact (valVector args, ValueCalc *calc, FuncExtra *);
48Value func_find (valVector args, ValueCalc *calc, FuncExtra *);
49Value func_fixed (valVector args, ValueCalc *calc, FuncExtra *);
50Value func_left (valVector args, ValueCalc *calc, FuncExtra *);
51Value func_len (valVector args, ValueCalc *calc, FuncExtra *);
52Value func_lower (valVector args, ValueCalc *calc, FuncExtra *);
53Value func_mid (valVector args, ValueCalc *calc, FuncExtra *);
54Value func_proper (valVector args, ValueCalc *calc, FuncExtra *);
55Value func_regexp (valVector args, ValueCalc *calc, FuncExtra *);
56Value func_regexpre (valVector args, ValueCalc *calc, FuncExtra *);
57Value func_replace (valVector args, ValueCalc *calc, FuncExtra *);
58Value func_rept (valVector args, ValueCalc *calc, FuncExtra *);
59Value func_rot (valVector args, ValueCalc *calc, FuncExtra *);
60Value func_right (valVector args, ValueCalc *calc, FuncExtra *);
61Value func_search (valVector args, ValueCalc *calc, FuncExtra *);
62Value func_sleek (valVector args, ValueCalc *calc, FuncExtra *);
63Value func_substitute (valVector args, ValueCalc *calc, FuncExtra *);
64Value func_t (valVector args, ValueCalc *calc, FuncExtra *);
65Value func_text (valVector args, ValueCalc *calc, FuncExtra *);
66Value func_toggle (valVector args, ValueCalc *calc, FuncExtra *);
67Value func_trim (valVector args, ValueCalc *calc, FuncExtra *);
68Value func_upper (valVector args, ValueCalc *calc, FuncExtra *);
69Value func_value (valVector args, ValueCalc *calc, FuncExtra *);
70
71// registers all text functions
72void RegisterTextFunctions()
73{
74 FunctionRepository* repo = FunctionRepository::self();
75 Function *f;
76
77 // one-parameter functions
78 f = new Function ("CHAR", func_char);
79 repo->add (f);
80 f = new Function ("CLEAN", func_clean);
81 repo->add (f);
82 f = new Function ("CODE", func_code);
83 repo->add (f);
84 f = new Function ("LEN", func_len);
85 repo->add (f);
86 f = new Function ("LOWER", func_lower);
87 repo->add (f);
88 f = new Function ("PROPER", func_proper);
89 repo->add (f);
90 f = new Function ("ROT", func_rot);
91 repo->add (f);
92 f = new Function ("SLEEK", func_sleek);
93 repo->add (f);
94 f = new Function ("T", func_t);
95 repo->add (f);
96 f = new Function ("TOGGLE", func_toggle);
97 repo->add (f);
98 f = new Function ("TRIM", func_trim);
99 repo->add (f);
100 f = new Function ("UPPER", func_upper);
101 repo->add (f);
102 f = new Function ("VALUE", func_value);
103 repo->add (f);
104
105 // other functions
106 f = new Function ("COMPARE", func_compare);
107 f->setParamCount (3);
108 repo->add (f);
109 f = new Function ("CONCATENATE", func_concatenate);
110 f->setParamCount (1, -1);
111 f->setAcceptArray ();
112 repo->add (f);
113 f = new Function ("DOLLAR", func_dollar);
114 f->setParamCount (1, 2);
115 repo->add (f);
116 f = new Function ("EXACT", func_exact);
117 f->setParamCount (2);
118 repo->add (f);
119 f = new Function ("FIND", func_find);
120 f->setParamCount (2, 3);
121 repo->add (f);
122 f = new Function ("FIXED", func_fixed);
123 f->setParamCount (1, 3);
124 repo->add (f);
125 f = new Function ("LEFT", func_left);
126 f->setParamCount (2);
127 repo->add (f);
128 f = new Function ("MID", func_mid);
129 f->setParamCount (2, 3);
130 repo->add (f);
131 f = new Function ("REGEXP", func_regexp);
132 f->setParamCount (2, 4);
133 repo->add (f);
134 f = new Function ("REGEXPRE", func_regexpre);
135 f->setParamCount (3);
136 repo->add (f);
137 f = new Function ("REPLACE", func_replace);
138 f->setParamCount (4);
139 repo->add (f);
140 f = new Function ("REPT", func_rept);
141 f->setParamCount (2);
142 repo->add (f);
143 f = new Function ("RIGHT", func_right);
144 f->setParamCount (2);
145 repo->add (f);
146 f = new Function ("SEARCH", func_search);
147 f->setParamCount (2, 3);
148 repo->add (f);
149 f = new Function ("SUBSTITUTE", func_substitute);
150 f->setParamCount (3, 4);
151 repo->add (f);
152 f = new Function ("TEXT", func_text);
153 f->setParamCount (1, 2);
154 repo->add (f);
155}
156
157
158// Function: CHAR
159Value func_char (valVector args, ValueCalc *calc, FuncExtra *)
160{
161 int val = calc->conv()->asInteger (args[0]).asInteger ();
162 return Value (TQString (TQChar (val)));
163}
164
165// Function: CLEAN
166Value func_clean (valVector args, ValueCalc *calc, FuncExtra *)
167{
168 TQString str (calc->conv()->asString (args[0]).asString());
169 TQString result;
170 TQChar c;
171 int i;
172 int l = str.length();
173
174 for (i = 0; i < l; ++i)
175 {
176 c = str[i];
177 if (c.isPrint())
178 result += c;
179 }
180
181 return Value (result);
182}
183
184// Function: CODE
185Value func_code (valVector args, ValueCalc *calc, FuncExtra *)
186{
187 TQString str (calc->conv()->asString (args[0]).asString());
188 if (str.length() <= 0)
189 return Value::errorVALUE();
190
191 return Value (str[0].unicode());
192}
193
194// Function: COMPARE
195Value func_compare (valVector args, ValueCalc *calc, FuncExtra *)
196{
197 int result = 0;
198 bool exact = calc->conv()->asBoolean (args[2]).asBoolean();
199
200 TQString s1 = calc->conv()->asString (args[0]).asString();
201 TQString s2 = calc->conv()->asString (args[1]).asString();
202
203 if (!exact)
204 result = s1.lower().localeAwareCompare(s2.lower());
205 else
206 result = s1.localeAwareCompare(s2);
207
208 if (result < 0)
209 result = -1;
210 else if (result > 0)
211 result = 1;
212
213 return Value (result);
214}
215
216void func_concatenate_helper (Value val, ValueCalc *calc,
217 TQString& tmp)
218{
219 if (val.isArray()) {
220 for (unsigned int row = 0; row < val.rows(); ++row)
221 for (unsigned int col = 0; col < val.columns(); ++col)
222 func_concatenate_helper (val.element (col, row), calc, tmp);
223 } else
224 tmp += calc->conv()->asString (val).asString();
225}
226
227// Function: CONCATENATE
228Value func_concatenate (valVector args, ValueCalc *calc, FuncExtra *)
229{
230 TQString tmp;
231 for (unsigned int i = 0; i < args.count(); ++i)
232 func_concatenate_helper (args[i], calc, tmp);
233
234 return Value (tmp);
235}
236
237// Function: DOLLAR
238Value func_dollar (valVector args, ValueCalc *calc, FuncExtra *)
239{
240 // ValueConverter doesn't support money directly, hence we need to
241 // use the locale. This code has the same effect as the output
242 // of ValueFormatter for money format.
243
244 // This function converts data to double/int, hence it won't support
245 // larger precision.
246
247 double value = calc->conv()->asFloat (args[0]).asFloat();
248 int precision = 2;
249 if (args.count() == 2)
250 precision = calc->conv()->asInteger (args[1]).asInteger();
251
252 // do round, because formatMoney doesn't
253 value = floor (value * pow (10.0, precision) + 0.5) / pow (10.0, precision);
254
255 TDELocale *locale = calc->conv()->locale();
256 TQString s = locale->formatMoney (value, locale->currencySymbol(), precision);
257
258 return Value (s);
259}
260
261// Function: EXACT
262Value func_exact (valVector args, ValueCalc *calc, FuncExtra *)
263{
264 TQString s1 = calc->conv()->asString (args[0]).asString();
265 TQString s2 = calc->conv()->asString (args[1]).asString();
266 bool exact = (s1 == s2);
267 return Value (exact);
268}
269
270// Function: FIND
271Value func_find (valVector args, ValueCalc *calc, FuncExtra *)
272{
273 TQString find_text, within_text;
274 int start_num = 1;
275
276 find_text = calc->conv()->asString (args[0]).asString();
277 within_text = calc->conv()->asString (args[1]).asString();
278 if (args.count() == 3)
279 start_num = calc->conv()->asInteger (args[2]).asInteger();
280
281 // conforms to Excel behaviour
282 if (start_num <= 0) return Value::errorVALUE();
283 if (start_num > (int)within_text.length()) return Value::errorVALUE();
284
285 int pos = within_text.find (find_text, start_num - 1);
286 if( pos < 0 ) return Value::errorNA();
287
288 return Value (pos + 1);
289}
290
291// Function: FIXED
292Value func_fixed (valVector args, ValueCalc *calc, FuncExtra *)
293{
294 // uses double, hence won't support big precision
295
296 int decimals = 2;
297 bool no_commas = false;
298
299 double number = calc->conv()->asFloat (args[0]).asFloat();
300 if (args.count() > 1)
301 decimals = calc->conv()->asInteger (args[1]).asInteger();
302 if (args.count() == 3)
303 no_commas = calc->conv()->asBoolean (args[2]).asBoolean();
304
305 TQString result;
306 TDELocale *locale = calc->conv()->locale();
307
308 // unfortunately, we can't just use TDELocale::formatNumber because
309 // * if decimals < 0, number is rounded
310 // * if no_commas is true, thousand separators shouldn't show up
311
312 if( decimals < 0 )
313 {
314 decimals = -decimals;
315 number = floor( number/pow(10.0,decimals)+0.5 ) * pow(10.0,decimals);
316 decimals = 0;
317 }
318
319 bool neg = number < 0;
320 result = TQString::number( neg ? -number:number, 'f', decimals );
321
322 int pos = result.find('.');
323 if (pos == -1) pos = result.length();
324 else result.replace(pos, 1, locale->decimalSymbol());
325 if( !no_commas )
326 while (0 < (pos -= 3))
327 result.insert(pos, locale->thousandsSeparator());
328
329 result.prepend( neg ? locale->negativeSign():
330 locale->positiveSign() );
331
332 return Value (result);
333}
334
335// Function: LEFT
336Value func_left (valVector args, ValueCalc *calc, FuncExtra *)
337{
338 TQString str = calc->conv()->asString (args[0]).asString();
339 int nb = 1;
340 if (args.count() == 2)
341 nb = calc->conv()->asInteger (args[1]).asInteger();
342
343 return Value (str.left (nb));
344}
345
346// Function: LEN
347Value func_len (valVector args, ValueCalc *calc, FuncExtra *)
348{
349 int nb = calc->conv()->asString (args[0]).asString().length();
350 return Value (nb);
351}
352
353// Function: LOWER
354Value func_lower (valVector args, ValueCalc *calc, FuncExtra *)
355{
356 return Value (calc->conv()->asString (args[0]).asString().lower());
357}
358
359// Function: MID
360Value func_mid (valVector args, ValueCalc *calc, FuncExtra *)
361{
362 TQString str = calc->conv()->asString (args[0]).asString();
363 int pos = calc->conv()->asInteger (args[1]).asInteger();
364 uint len = 0xffffffff;
365 if (args.count() == 3)
366 len = (uint) calc->conv()->asInteger (args[2]).asInteger();
367
368 // Excel compatible
369 pos--;
370
371 return Value (str.mid (pos, len));
372}
373
374// Function: PROPER
375Value func_proper (valVector args, ValueCalc *calc, FuncExtra *)
376{
377 TQString str = calc->conv()->asString (args[0]).asString().lower();
378
379 TQChar f;
380 bool first = true;
381
382 for (unsigned int i = 0; i < str.length(); ++i)
383 {
384 if (first)
385 {
386 f = str[i];
387 if (f.isNumber())
388 continue;
389
390 f = f.upper();
391
392 str[i] = f;
393 first = false;
394
395 continue;
396 }
397
398 if (str[i] == ' ' || str[i] == '-')
399 first = true;
400 }
401
402 return Value (str);
403}
404
405// Function: REGEXP
406Value func_regexp (valVector args, ValueCalc *calc, FuncExtra *)
407{
408 // ensure that we got a valid regular expression
409 TQRegExp exp (calc->conv()->asString (args[1]).asString());
410 if (!exp.isValid ())
411 return Value::errorVALUE();
412
413 TQString s = calc->conv()->asString (args[0]).asString();
414 TQString defText;
415 if (args.count() > 2)
416 defText = calc->conv()->asString (args[2]).asString();
417 int bkref = 0;
418 if (args.count() == 4)
419 bkref = calc->conv()->asInteger (args[3]).asInteger();
420 if (bkref < 0) // strange back-reference
421 return Value::errorVALUE();
422
423 TQString returnValue;
424
425 int pos = exp.search (s);
426 if (pos == -1)
427 returnValue = defText;
428 else
429 returnValue = exp.cap (bkref);
430
431 return Value (returnValue);
432}
433
434// Function: REGEXPRE
435Value func_regexpre (valVector args, ValueCalc *calc, FuncExtra *)
436{
437 // ensure that we got a valid regular expression
438 TQRegExp exp (calc->conv()->asString (args[1]).asString());
439 if (!exp.isValid ())
440 return Value::errorVALUE();
441
442 TQString s = calc->conv()->asString (args[0]).asString();
443 TQString str = calc->conv()->asString (args[2]).asString();
444
445 int pos = 0;
446 while ((pos = exp.search (s, pos)) != -1)
447 {
448 int i = exp.matchedLength();
449 s = s.replace (pos, i, str);
450 pos += str.length();
451 }
452
453 return Value (s);
454}
455
456// Function: REPLACE
457Value func_replace (valVector args, ValueCalc *calc, FuncExtra *)
458{
459 TQString text = calc->conv()->asString (args[0]).asString();
460 int pos = calc->conv()->asInteger (args[1]).asInteger();
461 int len = calc->conv()->asInteger (args[2]).asInteger();
462 TQString new_text = calc->conv()->asString (args[3]).asString();
463
464 if (pos < 0) pos = 0;
465
466 TQString result = text.replace (pos-1, len, new_text);
467 return Value (result);
468}
469
470// Function: REPT
471Value func_rept (valVector args, ValueCalc *calc, FuncExtra *)
472{
473 TQString s = calc->conv()->asString (args[0]).asString();
474 int nb = calc->conv()->asInteger (args[1]).asInteger();
475
476 TQString result;
477 for (int i = 0; i < nb; i++) result += s;
478 return Value (result);
479}
480
481// Function: RIGHT
482Value func_right (valVector args, ValueCalc *calc, FuncExtra *)
483{
484 TQString str = calc->conv()->asString (args[0]).asString();
485 int nb = 1;
486 if (args.count() == 2)
487 nb = calc->conv()->asInteger (args[1]).asInteger();
488
489 return Value (str.right (nb));
490}
491
492// Function: ROT
493Value func_rot (valVector args, ValueCalc *calc, FuncExtra *)
494{
495 TQString text = calc->conv()->asString (args[0]).asString();
496
497 for( unsigned i=0; i<text.length(); i++ )
498 {
499 unsigned c = text[i].upper().unicode();
500 if( ( c >= 'A' ) && ( c <= 'M' ) )
501 text[i] = TQChar( text[i].unicode() + 13);
502 if( ( c >= 'N' ) && ( c <= 'Z' ) )
503 text[i] = TQChar( text[i].unicode() - 13);
504 }
505
506 return Value (text);
507}
508
509// Function: SEARCH
510Value func_search (valVector args, ValueCalc *calc, FuncExtra *)
511{
512 TQString find_text = calc->conv()->asString (args[0]).asString();
513 TQString within_text = calc->conv()->asString (args[1]).asString();
514 int start_num = 1;
515 if (args.count() == 3)
516 start_num = calc->conv()->asInteger (args[2]).asInteger();
517
518 // conforms to Excel behaviour
519 if (start_num <= 0) return Value::errorVALUE();
520 if (start_num > (int)within_text.length()) return Value::errorVALUE();
521
522 // use globbing feature of TQRegExp
523 TQRegExp regex( find_text, false, true );
524 int pos = within_text.find( regex, start_num-1 );
525 if( pos < 0 ) return Value::errorNA();
526
527 return Value (pos + 1);
528}
529
530// Function: SLEEK
531Value func_sleek (valVector args, ValueCalc *calc, FuncExtra *)
532{
533 TQString str = calc->conv()->asString (args[0]).asString();
534 TQString result;
535 TQChar c;
536 int i;
537 int l = str.length();
538
539 for (i = 0; i < l; ++i)
540 {
541 c = str[i];
542 if (!c.isSpace())
543 result += c;
544 }
545
546 return Value (result);
547}
548
549// Function: SUBSTITUTE
550Value func_substitute (valVector args, ValueCalc *calc, FuncExtra *)
551{
552 int num = 1;
553 bool all = true;
554
555 if (args.count() == 4)
556 {
557 num = calc->conv()->asInteger (args[3]).asInteger();
558 all = false;
559 }
560
561 TQString text = calc->conv()->asString (args[0]).asString();
562 TQString old_text = calc->conv()->asString (args[1]).asString();
563 TQString new_text = calc->conv()->asString (args[2]).asString();
564
565 if( num <= 0 ) return Value::errorVALUE();
566 if (old_text.length() == 0) return Value (text);
567
568 TQString result = text;
569
570 int p = result.find (old_text);
571 while ((p != -1) && (num > 0))
572 {
573 result.replace( p, old_text.length(), new_text );
574 // find another location, starting straight after the replaced text
575 p = result.find (old_text, p + new_text.length());
576 if( !all ) num--;
577 }
578
579 return Value (result);
580}
581
582// Function: T
583Value func_t (valVector args, ValueCalc *calc, FuncExtra *)
584{
585 return calc->conv()->asString (args[0]);
586}
587
588// Function: TEXT
589Value func_text (valVector args, ValueCalc *calc, FuncExtra *)
590{
591 //Currently the same as the T function ...
592 //Second parameter is format_text. It is currently ignored.
593 return calc->conv()->asString (args[0]);
594}
595
596// Function: TOGGLE
597Value func_toggle (valVector args, ValueCalc *calc, FuncExtra *)
598{
599 TQString str = calc->conv()->asString (args[0]).asString();
600 int i;
601 int l = str.length();
602
603 for (i = 0; i < l; ++i)
604 {
605 TQChar c = str[i];
606 TQChar lc = c.lower();
607 TQChar uc = c.upper();
608
609 if (c == lc) // it is in lowercase
610 str[i] = c.upper();
611 else if (c == uc) // it is in uppercase
612 str[i] = c.lower();
613 }
614
615 return Value (str);
616}
617
618// Function: TRIM
619Value func_trim (valVector args, ValueCalc *calc, FuncExtra *)
620{
621 return Value (
622 calc->conv()->asString (args[0]).asString().simplifyWhiteSpace());
623}
624
625// Function: UPPER
626Value func_upper (valVector args, ValueCalc *calc, FuncExtra *)
627{
628 return Value (calc->conv()->asString (args[0]).asString().upper());
629}
630
631// Function: VALUE
632Value func_value (valVector args, ValueCalc *calc, FuncExtra *)
633{
634 // same as the N function
635 return calc->conv()->asFloat (args[0]);
636}
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
Provides a wrapper for cell value.
unsigned columns() const
If this value is an array, return the number of columns.
Value element(unsigned column, unsigned row) const
Return an element in the array value.
unsigned rows() const
If this value is an array, return the number of rows.
bool isArray() const
Returns true if the type of this value is array.
double asFloat() const
Returns the floating-point value of this value.
long asInteger() const
Returns the integer value of this value.
TQString asString() const
Returns the string value of this value.
static const Value & errorVALUE()
Returns constant reference to #VALUE! error.
bool asBoolean() const
Returns the boolean value of this value.
static const Value & errorNA()
Returns constant reference to #N/A error.
The KSpread namespace.
Definition commands.h:39