KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

632 lines
15KB

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