kspread

valueparser.cpp
1/* This file is part of the KDE project
2 Copyright 2004 Tomas Mecir <mecirt@gmail.com>
3 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
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, or (at your option) any later version.
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 "valueparser.h"
22
23#include "kspread_cell.h"
24#include "kspread_format.h"
25#include "kspread_locale.h"
26#include "kspread_value.h"
27
28using namespace KSpread;
29
30ValueParser::ValueParser( TDELocale* locale ) : parserLocale( locale )
31{
32}
33
34TDELocale* ValueParser::locale()
35{
36 return parserLocale;
37}
38
39void ValueParser::parse (const TQString& str, Cell *cell)
40{
41 FormatType format = cell->formatType();
42
43 // If the text is empty, we don't have a value
44 // If the user stated explicitly that he wanted text
45 // (using the format or using a quote),
46 // then we don't parse as a value, but as string.
47 if ( str.isEmpty() || format == Text_format || str.at(0)=='\'' )
48 {
49 cell->setValue (str);
50 return;
51 }
52
53 TQString strStripped = str.stripWhiteSpace();
54
55 // Try parsing as various datatypes, to find the type of the cell
56
57 // First as number
58 if (tryParseNumber (strStripped, cell))
59 return;
60
61 // Then as bool
62 if (tryParseBool (strStripped, cell))
63 return;
64
65
66 // Test for money number
67 bool ok;
68 double money = parserLocale->readMoney (strStripped, &ok);
69 if (ok)
70 {
71 cell->format()->setPrecision(2);
72 Value val (money);
73 val.setFormat (Value::fmt_Money);
74 cell->setValue (val);
75 return;
76 }
77
78 if (tryParseDate (strStripped, cell))
79 return;
80
81 if (tryParseTime (strStripped, cell))
82 return;
83
84 // Nothing particular found, then this is simply a string
85 cell->setValue (Value (str));
86}
87
88Value ValueParser::parse (const TQString &str)
89{
90 Value val;
91
92 // If the text is empty, we don't have a value
93 // If the user stated explicitly that he wanted text
94 // (using the format or using a quote),
95 // then we don't parse as a value, but as string.
96 if ( str.isEmpty() || str.at(0)=='\'' )
97 {
98 val.setValue (str);
99 return val;
100 }
101
102 bool ok;
103
104 TQString strStripped = str.stripWhiteSpace();
105 // Try parsing as various datatypes, to find the type of the string
106
107 // First as number
108 val = tryParseNumber (strStripped, &ok);
109 if (ok)
110 return val;
111
112 // Then as bool
113 // Note - I swapped the order of these two to try parsing as a number
114 // first because that will probably be the most common case
115 val = tryParseBool (strStripped, &ok);
116 if (ok)
117 return val;
118
119
120 // Test for money number
121 double money = parserLocale->readMoney (strStripped, &ok);
122 if (ok)
123 {
124 val.setValue (money);
125 val.setFormat (Value::fmt_Money);
126 return val;
127 }
128
129 val = tryParseDate (strStripped, &ok);
130 if (ok)
131 return val;
132
133 val = tryParseTime (strStripped, &ok);
134 if (ok)
135 return val;
136
137 // Nothing particular found, then this is simply a string
138 val.setValue (str);
139 return val;
140}
141
142bool ValueParser::tryParseBool (const TQString& str, Cell *cell)
143{
144 bool ok;
145 Value val = tryParseBool (str, &ok);
146 if (ok)
147 cell->setValue (val);
148 return ok;
149}
150
151bool ValueParser::tryParseNumber (const TQString& str, Cell *cell)
152{
153 bool ok;
154 Value val = tryParseNumber (str, &ok);
155 if (ok)
156 cell->setValue (val);
157 return ok;
158}
159
160bool ValueParser::tryParseDate (const TQString& str, Cell *cell)
161{
162 bool ok;
163 Value value = tryParseDate (str, &ok);
164 if (ok)
165 cell->setValue (value);
166 return ok;
167}
168
169bool ValueParser::tryParseTime (const TQString& str, Cell *cell)
170{
171 bool ok;
172 Value value = tryParseTime (str, &ok);
173 if (ok)
174 cell->setValue (value);
175 return ok;
176}
177
178
179Value ValueParser::tryParseBool (const TQString& str, bool *ok)
180{
181 Value val;
182 if (ok) *ok = false;
183
184 const TQString& lowerStr = str.lower();
185
186 if ((lowerStr == "true") ||
187 (lowerStr == parserLocale->translate("true").lower()))
188 {
189 val.setValue (true);
190 if (ok) *ok = true;
191 }
192 else if ((lowerStr == "false") ||
193 (lowerStr == parserLocale->translate("false").lower()))
194 {
195 val.setValue (false);
196 if (ok) *ok = true;
197 fmtType = Number_format; //TODO: really?
198 }
199 return val;
200}
201
202double ValueParser::readNumber(const TQString &_str, bool * ok, bool * isInt)
203{
204 TQString str = _str.stripWhiteSpace();
205 bool neg = str.find(parserLocale->negativeSign()) == 0;
206 if (neg)
207 str.remove( 0, parserLocale->negativeSign().length() );
208
209 /* will hold the scientific notation portion of the number.
210 Example, with 2.34E+23, exponentialPart == "E+23"
211 */
212 TQString exponentialPart;
213 int EPos;
214
215 EPos = str.find('E', 0, false);
216
217 if (EPos != -1)
218 {
219 exponentialPart = str.mid(EPos);
220 str = str.left(EPos);
221 }
222
223 int pos = str.find(parserLocale->decimalSymbol());
224 TQString major;
225 TQString minor;
226 if ( pos == -1 )
227 {
228 major = str;
229 if (isInt) *isInt = true;
230 }
231 else
232 {
233 major = str.left(pos);
234 minor = str.mid(pos + parserLocale->decimalSymbol().length());
235 if (isInt) *isInt = false;
236 }
237
238 // Remove thousand separators
239 int thlen = parserLocale->thousandsSeparator().length();
240 int lastpos = 0;
241 while ( ( pos = major.find( parserLocale->thousandsSeparator() ) ) > 0 )
242 {
243 // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N
244 int fromEnd = major.length() - pos;
245 if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error
246 || pos - lastpos > 3 // More than 3 digits between two separators -> error
247 || pos == 0 // Can't start with a separator
248 || (lastpos>0 && pos-lastpos!=3)) // Must have exactly 3 digits between two separators
249 {
250 if (ok) *ok = false;
251 return 0.0;
252 }
253
254 lastpos = pos;
255 major.remove( pos, thlen );
256 }
257 if (lastpos>0 && major.length()-lastpos!=3) // Must have exactly 3 digits after the last separator
258 {
259 if (ok) *ok = false;
260 return 0.0;
261 }
262
263 TQString tot;
264 if (neg) tot = '-';
265
266 tot += major + '.' + minor + exponentialPart;
267
268 return tot.toDouble(ok);
269}
270
271Value ValueParser::tryParseNumber (const TQString& str, bool *ok)
272{
273 Value value;
274
275 bool percent = false;
276 TQString str2;
277 if( str.at(str.length()-1)=='%')
278 {
279 str2 = str.left (str.length()-1).stripWhiteSpace();
280 percent = true;
281 }
282 else
283 str2 = str;
284
285
286 // First try to understand the number using the parserLocale
287 bool isInt;
288 double val = readNumber (str2, ok, &isInt);
289 // If not, try with the '.' as decimal separator
290 if (!(*ok))
291 {
292 val = str2.toDouble(ok);
293 if (str.contains('.'))
294 isInt = false;
295 else
296 isInt = true;
297 }
298
299 if (*ok)
300 {
301 if (percent)
302 {
303 //kdDebug(36001) << "ValueParser::tryParseNumber '" << str <<
304 // "' successfully parsed as percentage: " << val << "%" << endl;
305 value.setValue (val / 100.0);
306 value.setFormat (Value::fmt_Percent);
307 fmtType = Percentage_format;
308 }
309 else
310 {
311 //kdDebug(36001) << "ValueParser::tryParseNumber '" << str <<
312 // "' successfully parsed as number: " << val << endl;
313 if (isInt)
314 value.setValue (static_cast<long> (val));
315 else
316 value.setValue (val);
317
318 if ( str2.contains('E') || str2.contains('e') )
319 fmtType = Scientific_format;
320 else
321 {
322 if (val > 1e+10)
323 fmtType = Scientific_format;
324 else
325 fmtType = Number_format;
326 }
327 }
328 }
329
330 return value;
331}
332
333Value ValueParser::tryParseDate (const TQString& str, bool *ok)
334{
335 bool valid = false;
336 TQDate tmpDate = parserLocale->readDate (str, &valid);
337 if (!valid)
338 {
339 // Try without the year
340 // The tricky part is that we need to remove any separator around the year
341 // For instance %Y-%m-%d becomes %m-%d and %d/%m/%Y becomes %d/%m
342 // If the year is in the middle, say %m-%Y/%d, we'll remove the sep.
343 // before it (%m/%d).
344 TQString fmt = parserLocale->dateFormatShort();
345 int yearPos = fmt.find ("%Y", 0, false);
346 if ( yearPos > -1 )
347 {
348 if ( yearPos == 0 )
349 {
350 fmt.remove( 0, 2 );
351 while ( fmt[0] != '%' )
352 fmt.remove( 0, 1 );
353 } else
354 {
355 fmt.remove( yearPos, 2 );
356 for ( ; yearPos > 0 && fmt[yearPos-1] != '%'; --yearPos )
357 fmt.remove( yearPos, 1 );
358 }
359 //kdDebug(36001) << "Cell::tryParseDate short format w/o date: " << fmt << endl;
360 tmpDate = parserLocale->readDate( str, fmt, &valid );
361 }
362 }
363 if (valid)
364 {
365 // Note: if shortdate format only specifies 2 digits year, then 3/4/1955
366 // will be treated as in year 3055, while 3/4/55 as year 2055
367 // (because 55 < 69, see TDELocale) and thus there's no way to enter for
368 // year 1995
369
370 // The following fixes the problem, 3/4/1955 will always be 1955
371
372 TQString fmt = parserLocale->dateFormatShort();
373 if( ( fmt.contains( "%y" ) == 1 ) && ( tmpDate.year() > 2999 ) )
374 tmpDate = tmpDate.addYears( -1900 );
375
376 // this is another HACK !
377 // with two digit years, 0-69 is treated as year 2000-2069 (see TDELocale)
378 // however, in Excel only 0-29 is year 2000-2029, 30 or later is 1930
379 // onwards
380
381 // the following provides workaround for TDELocale so we're compatible
382 // with Excel
383 // (e.g 3/4/45 is Mar 4, 1945 not Mar 4, 2045)
384 if( ( tmpDate.year() >= 2030 ) && ( tmpDate.year() <= 2069 ) )
385 {
386 TQString yearFourDigits = TQString::number( tmpDate.year() );
387 TQString yearTwoDigits = TQString::number( tmpDate.year() % 100 );
388
389 // if year is 2045, check to see if "2045" isn't there --> actual
390 // input is "45"
391 if( ( str.contains( yearTwoDigits ) >= 1 ) &&
392 ( str.contains( yearFourDigits ) == 0 ) )
393 tmpDate = tmpDate.addYears( -100 );
394 }
395
396 //test if it's a short date or text date.
397 if (parserLocale->formatDate (tmpDate, false) == str)
398 fmtType = TextDate_format;
399 else
400 fmtType = ShortDate_format;
401 }
402 if (!valid)
403 {
404 //try to use the standard TQt date parsing, using ISO 8601 format
405 tmpDate = TQDate::fromString(str,TQt::ISODate);
406 if (tmpDate.isValid())
407 {
408 valid = true;
409 }
410 }
411
412 if (ok)
413 *ok = valid;
414
415 return Value (tmpDate);
416}
417
418Value ValueParser::tryParseTime (const TQString& str, bool *ok)
419{
420 if (ok)
421 *ok = false;
422
423 bool valid = false;
424 bool duration = false;
425 Value val;
426
427 TQDateTime tmpTime = readTime (str, true, &valid, duration);
428 if (!tmpTime.isValid())
429 tmpTime = readTime (str, false, &valid, duration);
430
431 if (!valid)
432 {
433 TQTime tm;
434 if (parserLocale->use12Clock())
435 {
436 TQString stringPm = parserLocale->translate("pm");
437 TQString stringAm = parserLocale->translate("am");
438 int pos=0;
439 if((pos=str.find(stringPm))!=-1)
440 {
441 TQString tmp=str.mid(0,str.length()-stringPm.length());
442 tmp=tmp.simplifyWhiteSpace();
443 tm = parserLocale->readTime(tmp+" "+stringPm, &valid);
444 if (!valid)
445 tm = parserLocale->readTime(tmp+":00 "+stringPm, &valid);
446 }
447 else if((pos=str.find(stringAm))!=-1)
448 {
449 TQString tmp = str.mid(0,str.length()-stringAm.length());
450 tmp = tmp.simplifyWhiteSpace();
451 tm = parserLocale->readTime (tmp + " " + stringAm, &valid);
452 if (!valid)
453 tm = parserLocale->readTime (tmp + ":00 " + stringAm, &valid);
454 }
455 if (valid)
456 tmpTime.setTime(tm);
457 }
458 }
459 if (valid)
460 {
461 fmtType = Time_format;
462 if ( duration )
463 {
464 val.setValue (tmpTime);
465 fmtType = Time_format7;
466 }
467 else
468 val.setValue (tmpTime.time());
469 }
470
471 if (ok)
472 *ok = valid;
473
474 return val;
475}
476
477TQDateTime ValueParser::readTime (const TQString & intstr, bool withSeconds,
478 bool *ok, bool & duration)
479{
480 duration = false;
481 TQString str = intstr.simplifyWhiteSpace().lower();
482 TQString format = parserLocale->timeFormat().simplifyWhiteSpace();
483 if ( !withSeconds )
484 {
485 int n = format.find("%S");
486 format = format.left( n - 1 );
487 }
488
489 int days = -1;
490 int hour = -1, minute = -1;
491 int second = withSeconds ? -1 : 0; // don't require seconds
492 bool g_12h = false;
493 bool pm = false;
494 uint strpos = 0;
495 uint formatpos = 0;
496
497 TQDate refDate( 1899, 12, 31 );
498
499 uint l = format.length();
500 uint sl = str.length();
501
502 while (l > formatpos || sl > strpos)
503 {
504 if ( !(l > formatpos && sl > strpos) )
505 goto error;
506
507 TQChar c( format.at( formatpos++ ) );
508
509 if (c != '%')
510 {
511 if (c.isSpace())
512 ++strpos;
513 else if (c != str.at(strpos++))
514 goto error;
515 continue;
516 }
517
518 // remove space at the begining
519 if (sl > strpos && str.at( strpos).isSpace() )
520 ++strpos;
521
522 c = format.at( formatpos++ );
523 switch (c)
524 {
525 case 'p':
526 {
527 TQString s;
528 s = parserLocale->translate("pm").lower();
529 int len = s.length();
530 if (str.mid(strpos, len) == s)
531 {
532 pm = true;
533 strpos += len;
534 }
535 else
536 {
537 s = parserLocale->translate("am").lower();
538 len = s.length();
539 if (str.mid(strpos, len) == s)
540 {
541 pm = false;
542 strpos += len;
543 }
544 else
545 goto error;
546 }
547 }
548 break;
549
550 case 'k':
551 case 'H':
552 g_12h = false;
553 hour = readInt(str, strpos);
554 if (hour < 0)
555 goto error;
556 if (hour > 23)
557 {
558 days = (int)(hour / 24);
559 hour %= 24;
560 }
561
562 break;
563
564 case 'l':
565 case 'I':
566 g_12h = true;
567 hour = readInt(str, strpos);
568 if (hour < 1 || hour > 12)
569 goto error;
570
571 break;
572
573 case 'M':
574 minute = readInt(str, strpos);
575 if (minute < 0 || minute > 59)
576 goto error;
577
578 break;
579
580 case 'S':
581 second = readInt(str, strpos);
582 if (second < 0 || second > 59)
583 goto error;
584
585 break;
586 }
587 }
588
589 if (g_12h)
590 {
591 hour %= 12;
592 if (pm) hour += 12;
593 }
594
595 if (days > 0)
596 {
597 refDate.addDays( days );
598 duration = true;
599 }
600
601 if (ok)
602 *ok = true;
603 return TQDateTime( refDate, TQTime( hour, minute, second ) );
604
605 error:
606 if (ok)
607 *ok = false;
608 // return invalid date if it didn't work
609 return TQDateTime( refDate, TQTime( -1, -1, -1 ) );
610}
611
618int ValueParser::readInt (const TQString &str, uint &pos)
619{
620 if (!str.at(pos).isDigit())
621 return -1;
622 int result = 0;
623 for ( ; str.length() > pos && str.at(pos).isDigit(); pos++ )
624 {
625 result *= 10;
626 result += str.at(pos).digitValue();
627 }
628
629 return result;
630}
631
For every cell in the spread sheet there is a Cell object.
FormatType formatType() const
Return the format of this cell.
void setValue(const Value &value)
Sets the value for this cell.
Format * format() const
Returns the format object of this cell.
TQDateTime readTime(const TQString &intstr, bool withSeconds, bool *ok, bool &duration)
converts a string to a date/time value
void parse(const TQString &str, Cell *cell)
try to parse the text in a given cell and set value accordingly
ValueParser(TDELocale *locale)
constructor
double readNumber(const TQString &_str, bool *ok, bool *isInt)
a helper function to read numbers and distinguish integers and FPs
int readInt(const TQString &str, uint &pos)
a helper function to read integers
Provides a wrapper for cell value.
void setFormat(Format fmt)
Sets format information for this value.
void setValue(const Value &v)
Sets this value to another value.
The KSpread namespace.
Definition commands.h:39