kspread Library API Documentation

valueparser.cc

00001 /* This file is part of the KDE project
00002    Copyright 2004 Tomas Mecir <mecirt@gmail.com>
00003    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include "valueparser.h"
00022 
00023 #include "kspread_cell.h"
00024 #include "kspread_locale.h"
00025 
00026 using namespace KSpread;
00027 
00028 ValueParser::ValueParser( KLocale* locale ) : parserLocale( locale )
00029 {
00030 }
00031 
00032 KLocale* ValueParser::locale()
00033 {
00034   return parserLocale;
00035 }
00036 
00037 void ValueParser::parse (const QString& str, KSpreadCell *cell)
00038 {
00039   FormatType format = cell->formatType();
00040   
00041   // If the text is empty, we don't have a value
00042   // If the user stated explicitly that he wanted text
00043   // (using the format or using a quote),
00044   // then we don't parse as a value, but as string.
00045   if ( str.isEmpty() || format == Text_format || str.at(0)=='\'' )
00046   {
00047     cell->setValue (str);
00048     return;
00049   }
00050 
00051   QString strStripped = str.stripWhiteSpace();
00052   // Try parsing as various datatypes, to find the type of the cell
00053   // First as bool
00054   if (tryParseBool (strStripped, cell))
00055     return;
00056 
00057   // Then as a number
00058   if (tryParseNumber (strStripped, cell))
00059     return;
00060 
00061   // Test for money number
00062   bool ok;
00063   double money = parserLocale->readMoney (strStripped, &ok);
00064   if (ok)
00065   {
00066     cell->setPrecision(2);
00067     KSpreadValue val (money);
00068     val.setFormat (KSpreadValue::fmt_Money);
00069     cell->setValue (val);
00070     return;
00071   }
00072 
00073   if (tryParseDate (strStripped, cell))
00074     return;
00075 
00076   if (tryParseTime (strStripped, cell))
00077     return;
00078 
00079   // Nothing particular found, then this is simply a string
00080   cell->setValue (KSpreadValue (str));
00081 }
00082 
00083 KSpreadValue ValueParser::parse (const QString &str)
00084 {
00085   KSpreadValue val;
00086   
00087   // If the text is empty, we don't have a value
00088   // If the user stated explicitly that he wanted text
00089   // (using the format or using a quote),
00090   // then we don't parse as a value, but as string.
00091   if ( str.isEmpty() || str.at(0)=='\'' )
00092   {
00093     val.setValue (str);
00094     return val;
00095   }
00096 
00097   bool ok;
00098   
00099   QString strStripped = str.stripWhiteSpace();
00100   // Try parsing as various datatypes, to find the type of the string
00101   // First as bool
00102   val = tryParseBool (strStripped, &ok);
00103   if (ok)
00104     return val;
00105 
00106   // Then as a number
00107   val = tryParseNumber (strStripped, &ok);
00108   if (ok)
00109     return val;
00110 
00111   // Test for money number
00112   double money = parserLocale->readMoney (strStripped, &ok);
00113   if (ok)
00114   {
00115     val.setValue (money);
00116     val.setFormat (KSpreadValue::fmt_Money);
00117     return val;
00118   }
00119 
00120   val = tryParseDate (strStripped, &ok);
00121   if (ok)
00122     return val;
00123 
00124   val = tryParseTime (strStripped, &ok);
00125   if (ok)
00126     return val;
00127 
00128   // Nothing particular found, then this is simply a string
00129   val.setValue (str);
00130   return val;
00131 }
00132 
00133 bool ValueParser::tryParseBool (const QString& str, KSpreadCell *cell)
00134 {
00135   bool ok;
00136   KSpreadValue val = tryParseBool (str, &ok);
00137   if (ok)
00138     cell->setValue (val);
00139   return ok;
00140 }
00141 
00142 bool ValueParser::tryParseNumber (const QString& str, KSpreadCell *cell)
00143 {
00144   bool ok;
00145   KSpreadValue val = tryParseNumber (str, &ok);
00146   if (ok)
00147     cell->setValue (val);
00148   return ok;
00149 }
00150 
00151 bool ValueParser::tryParseDate (const QString& str, KSpreadCell *cell)
00152 {
00153   bool ok;
00154   KSpreadValue value = tryParseDate (str, &ok);
00155   if (ok)
00156     cell->setValue (value);
00157   return ok;
00158 }
00159 
00160 bool ValueParser::tryParseTime (const QString& str, KSpreadCell *cell)
00161 {
00162   bool ok;
00163   KSpreadValue value = tryParseTime (str, &ok);
00164   if (ok)
00165     cell->setValue (value);
00166   return ok;
00167 }
00168 
00169 
00170 KSpreadValue ValueParser::tryParseBool (const QString& str, bool *ok)
00171 {
00172   KSpreadValue val;
00173   if (ok) *ok = false;
00174   if ((str.lower() == "true") ||
00175       (str.lower() == parserLocale->translate("True").lower()))
00176   {
00177     val.setValue (true);
00178     if (ok) *ok = true;
00179   }
00180   else if ((str.lower() == "false") ||
00181       (str.lower() == parserLocale->translate("false").lower()))
00182   {
00183     val.setValue (false);
00184     if (ok) *ok = true;
00185     fmtType = Number_format;    //TODO: really?
00186   }
00187   return val;
00188 }
00189 
00190 KSpreadValue ValueParser::tryParseNumber (const QString& str, bool *ok)
00191 {
00192   KSpreadValue value;
00193 
00194   bool percent = false;
00195   QString str2;
00196   if( str.at(str.length()-1)=='%')
00197   {
00198     str2 = str.left (str.length()-1).stripWhiteSpace();
00199     percent = true;
00200   }
00201   else
00202     str2 = str;
00203 
00204   
00205   // First try to understand the number using the parserLocale
00206   double val = parserLocale->readNumber (str2, ok);
00207   // If not, try with the '.' as decimal separator
00208   if (!(*ok))
00209     val = str2.toDouble(ok);
00210 
00211   if (*ok)
00212   {
00213     if (percent)
00214     {
00215       kdDebug(36001) << "ValueParser::tryParseNumber '" << str <<
00216           "' successfully parsed as percentage: " << val << "%" << endl;
00217       value.setValue (val / 100.0);
00218       value.setFormat (KSpreadValue::fmt_Percent);
00219       fmtType = Percentage_format;
00220     }
00221     else
00222     {
00223       kdDebug(36001) << "ValueParser::tryParseNumber '" << str <<
00224           "' successfully parsed as number: " << val << endl;
00225       value.setValue (val);
00226       
00227       if ( str2.contains('E') || str2.contains('e') )
00228         fmtType = Scientific_format;
00229       else
00230       {
00231         if (val > 1e+10)
00232           fmtType = Scientific_format;
00233         else
00234           fmtType = Number_format;
00235       }
00236     }
00237   }
00238 
00239   return value;
00240 }
00241 
00242 KSpreadValue ValueParser::tryParseDate (const QString& str, bool *ok)
00243 {
00244   bool valid = false;
00245   QDate tmpDate = parserLocale->readDate (str, &valid);
00246   if (!valid)
00247   {
00248     // Try without the year
00249     // The tricky part is that we need to remove any separator around the year
00250     // For instance %Y-%m-%d becomes %m-%d and %d/%m/%Y becomes %d/%m
00251     // If the year is in the middle, say %m-%Y/%d, we'll remove the sep.
00252     // before it (%m/%d).
00253     QString fmt = parserLocale->dateFormatShort();
00254     int yearPos = fmt.find ("%Y", 0, false);
00255     if ( yearPos > -1 )
00256     {
00257       if ( yearPos == 0 )
00258       {
00259         fmt.remove( 0, 2 );
00260         while ( fmt[0] != '%' )
00261           fmt.remove( 0, 1 );
00262       } else
00263       {
00264         fmt.remove( yearPos, 2 );
00265         for ( ; yearPos > 0 && fmt[yearPos-1] != '%'; --yearPos )
00266           fmt.remove( yearPos, 1 );
00267       }
00268       //kdDebug(36001) << "KSpreadCell::tryParseDate short format w/o date: " << fmt << endl;
00269       tmpDate = parserLocale->readDate( str, fmt, &valid );
00270     }
00271   }
00272   if (valid)
00273   {
00274     // Note: if shortdate format only specifies 2 digits year, then 3/4/1955
00275     // will be treated as in year 3055, while 3/4/55 as year 2055
00276     // (because 55 < 69, see KLocale) and thus there's no way to enter for
00277     // year 1995
00278   
00279     // The following fixes the problem, 3/4/1955 will always be 1955
00280 
00281     QString fmt = parserLocale->dateFormatShort();
00282     if( ( fmt.contains( "%y" ) == 1 ) && ( tmpDate.year() > 2999 ) )
00283       tmpDate = tmpDate.addYears( -1900 );
00284 
00285     // this is another HACK !
00286     // with two digit years, 0-69 is treated as year 2000-2069 (see KLocale)
00287     // however, in Excel only 0-29 is year 2000-2029, 30 or later is 1930
00288     // onwards
00289 
00290     // the following provides workaround for KLocale so we're compatible
00291     // with Excel
00292     // (e.g 3/4/45 is Mar 4, 1945 not Mar 4, 2045)
00293     if( ( tmpDate.year() >= 2030 ) && ( tmpDate.year() <= 2069 ) )
00294     {
00295       QString yearFourDigits = QString::number( tmpDate.year() );
00296       QString yearTwoDigits = QString::number( tmpDate.year() % 100 );
00297 
00298       // if year is 2045, check to see if "2045" isn't there --> actual
00299       // input is "45"
00300       if( ( str.contains( yearTwoDigits ) >= 1 ) &&
00301           ( str.contains( yearFourDigits ) == 0 ) )
00302         tmpDate = tmpDate.addYears( -100 );
00303     }
00304     
00305     //test if it's a short date or text date.
00306     if (parserLocale->formatDate (tmpDate, false) == str)
00307       fmtType = TextDate_format;
00308     else
00309       fmtType = ShortDate_format;
00310   }
00311   if (ok)
00312     *ok = valid;
00313     
00314   return KSpreadValue (tmpDate);
00315 }
00316 
00317 KSpreadValue ValueParser::tryParseTime (const QString& str, bool *ok)
00318 {
00319   if (ok)
00320     *ok = false;
00321 
00322   bool valid    = false;
00323   bool duration = false;
00324   KSpreadValue val;
00325 
00326   QDateTime tmpTime = readTime (str, true, &valid, duration);
00327   if (!tmpTime.isValid())
00328     tmpTime = readTime (str, false, &valid, duration);
00329 
00330   if (!valid)
00331   {
00332     QTime tm;
00333     if (parserLocale->use12Clock())
00334     {
00335       QString stringPm = parserLocale->translate("pm");
00336       QString stringAm = parserLocale->translate("am");
00337       int pos=0;
00338       if((pos=str.find(stringPm))!=-1)
00339       {
00340           QString tmp=str.mid(0,str.length()-stringPm.length());
00341           tmp=tmp.simplifyWhiteSpace();
00342           tm = parserLocale->readTime(tmp+" "+stringPm, &valid);
00343           if (!valid)
00344               tm = parserLocale->readTime(tmp+":00 "+stringPm, &valid);
00345       }
00346       else if((pos=str.find(stringAm))!=-1)
00347       {
00348           QString tmp = str.mid(0,str.length()-stringAm.length());
00349           tmp = tmp.simplifyWhiteSpace();
00350           tm = parserLocale->readTime (tmp + " " + stringAm, &valid);
00351           if (!valid)
00352               tm = parserLocale->readTime (tmp + ":00 " + stringAm, &valid);
00353       }
00354     }
00355   }
00356   if (valid)
00357   {
00358     fmtType = Time_format;
00359     if ( duration )
00360     {
00361       val.setValue (tmpTime);
00362       fmtType = Time_format7;
00363     }
00364     else
00365       val.setValue (tmpTime.time());
00366   }
00367   
00368   if (ok)
00369     *ok = valid;
00370     
00371   return val;
00372 }
00373 
00374 QDateTime ValueParser::readTime (const QString & intstr, bool withSeconds,
00375     bool *ok, bool & duration)
00376 {
00377   duration = false;
00378   QString str = intstr.simplifyWhiteSpace().lower();
00379   QString format = parserLocale->timeFormat().simplifyWhiteSpace();
00380   if ( !withSeconds )
00381   {
00382     int n = format.find("%S");
00383     format = format.left( n - 1 );
00384   }
00385 
00386   int days = -1;
00387   int hour = -1, minute = -1;
00388   int second = withSeconds ? -1 : 0; // don't require seconds
00389   bool g_12h = false;
00390   bool pm = false;
00391   uint strpos = 0;
00392   uint formatpos = 0;
00393 
00394   QDate refDate( 1899, 12, 31 );
00395 
00396   uint l  = format.length();
00397   uint sl = str.length();
00398 
00399   while (l > formatpos || sl > strpos)
00400   {
00401     if ( !(l > formatpos && sl > strpos) )
00402       goto error;
00403 
00404     QChar c( format.at( formatpos++ ) );
00405 
00406     if (c != '%')
00407     {
00408       if (c.isSpace())
00409         ++strpos;
00410       else if (c != str.at(strpos++))
00411         goto error;
00412       continue;
00413     }
00414 
00415     // remove space at the begining
00416     if (sl > strpos && str.at( strpos).isSpace() )
00417       ++strpos;
00418 
00419     c = format.at( formatpos++ );
00420     switch (c)
00421     {
00422      case 'p':
00423       {
00424         QString s;
00425         s = parserLocale->translate("pm").lower();
00426         int len = s.length();
00427         if (str.mid(strpos, len) == s)
00428         {
00429           pm = true;
00430           strpos += len;
00431         }
00432         else
00433         {
00434           s = parserLocale->translate("am").lower();
00435           len = s.length();
00436           if (str.mid(strpos, len) == s)
00437           {
00438             pm = false;
00439             strpos += len;
00440           }
00441           else
00442             goto error;
00443         }
00444       }
00445       break;
00446 
00447      case 'k':
00448      case 'H':
00449       g_12h = false;
00450       hour = readInt(str, strpos);
00451       if (hour < 0)
00452         goto error;
00453       if (hour > 23)
00454       {
00455         days = (int)(hour / 24);
00456         hour %= 24;
00457       }
00458 
00459       break;
00460 
00461      case 'l':
00462      case 'I':
00463       g_12h = true;
00464       hour = readInt(str, strpos);
00465       if (hour < 1 || hour > 12)
00466         goto error;
00467 
00468       break;
00469 
00470      case 'M':
00471       minute = readInt(str, strpos);
00472       if (minute < 0 || minute > 59)
00473         goto error;
00474 
00475       break;
00476 
00477      case 'S':
00478       second = readInt(str, strpos);
00479       if (second < 0 || second > 59)
00480         goto error;
00481 
00482       break;
00483     }
00484   }
00485 
00486   if (g_12h)
00487   {
00488     hour %= 12;
00489     if (pm) hour += 12;
00490   }
00491 
00492   if (days > 0)
00493   {
00494     refDate.addDays( days );
00495     duration = true;
00496   }
00497 
00498   if (ok)
00499     *ok = true;
00500   return QDateTime( refDate, QTime( hour, minute, second ) );
00501 
00502  error:
00503   if (ok)
00504     *ok = false;
00505   // return invalid date if it didn't work
00506   return QDateTime( refDate, QTime( -1, -1, -1 ) ); 
00507 }
00508 
00515 int ValueParser::readInt (const QString &str, uint &pos)
00516 {
00517   if (!str.at(pos).isDigit())
00518     return -1;
00519   int result = 0;
00520   for ( ; str.length() > pos && str.at(pos).isDigit(); pos++ )
00521   {
00522     result *= 10;
00523     result += str.at(pos).digitValue();
00524   }
00525 
00526   return result;
00527 }
00528 
KDE Logo
This file is part of the documentation for kspread Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:43:38 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003