File indexing completed on 2024-04-14 05:43:12

0001 /***************************************************************************
0002                          currencies.cpp  -  list of currencies
0003                              -------------------
0004     begin                : sam déc  1 23:40:19 CET 2001
0005     copyright            : (C) 2001-2022 by Éric Bischoff
0006     email                : bischoff@kde.org
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "currencies.h"
0019 
0020 #include <QFile>
0021 #include <QDate>
0022 #include <QStandardPaths>
0023 #include <QDomDocument>
0024 #include <QDebug>
0025 
0026 #include <KIO/TransferJob>
0027 #include <KLocalizedString>
0028 
0029 static const char
0030     *urlECB = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml",
0031 //  *urlNY_FRB = "http://www.newyorkfed.org/markets/fxrates/FXtoXML.cfm?FEXdate=%04d-%02d-%02d&FEXtime=1200",
0032     *urlTG = "http://rss.timegenie.com/forex2.xml";
0033 
0034 // Constructor
0035 Currencies::Currencies()
0036     : QObject(),
0037           variableRates()
0038 {
0039     currency = 0;
0040 }
0041 
0042 // Destructor
0043 Currencies::~Currencies()
0044 {
0045     if (currency) delete [] currency;
0046 }
0047 
0048 // Read currencies list
0049 bool Currencies::readCurrencies( const char *filename )
0050 {
0051     QDomDocument document( "currencies" );
0052     QFile file( QStandardPaths::locate(QStandardPaths::AppDataLocation, filename) );
0053 
0054     if ( !file.open( QIODevice::ReadOnly ) ) return false;
0055     if ( !document.setContent( &file ) ) { file.close(); return false; }
0056     file.close();
0057 
0058     QDomNodeList currenciesList = document.elementsByTagName( "currency" );
0059     int num;
0060     QString name;
0061 
0062     numCurrencies = currenciesList.count();
0063     if (currency) delete [] currency;
0064     currency = new currencyStruc[numCurrencies];
0065     for (num = 0; num < numCurrencies; ++num)
0066     {
0067         QDomElement elt = currenciesList.item(num).toElement();
0068 
0069         if ( !elt.hasAttribute( "symbol" ) )
0070             return false;
0071         currency[num].symbol =  elt.attribute( "symbol" );
0072 
0073         if ( !elt.hasAttribute( "code" ) )
0074             return false;
0075         currency[num].code =  elt.attribute( "code" );
0076         if ( currency[num].code == "USD" )
0077             dollarCurrency = num;
0078         else if ( currency[num].code == "EUR" )
0079             euroCurrency = num;
0080 
0081         if ( !elt.hasAttribute( "official-rules-precision" ) )
0082             return false;
0083         currency[num].officialRulesPrecision = elt.attribute( "official-rules-precision" ).toDouble();
0084 
0085         if ( !elt.hasAttribute( "smallest-coin-precision" ) )
0086             return false;
0087         currency[num].smallestCoinPrecision = elt.attribute( "smallest-coin-precision" ).toDouble();
0088 
0089         if ( !elt.hasAttribute( "name" ) )
0090             return false;
0091         name = elt.attribute( "name" );
0092         currency[num].name = i18n( name.toUtf8().data() );
0093 
0094         if ( elt.hasAttribute( "new-york-name" ) )
0095             currency[num].newYorkName = elt.attribute( "new-york-name" );
0096         else
0097             currency[num].newYorkName = "N/A";
0098 
0099         if ( elt.hasAttribute( "fixed-rate" ) )
0100             currency[num].fixedRate = elt.attribute( "fixed-rate" ).toDouble();
0101         else
0102             currency[num].fixedRate = 0.0;
0103 
0104         currency[num].rate = 1.0;
0105         currency[num].position = -1;
0106     }
0107     return true;
0108 }
0109 
0110 // Clear rates
0111 void Currencies::clearRates()
0112 {
0113     int num;
0114 
0115     for (num = 0; num < numCurrencies; num++)
0116     {
0117         if ( !currency[num].fixedRate )
0118         {
0119             currency[num].rate = 1.0;
0120             currency[num].position = -1;
0121         }
0122     }
0123 }
0124 
0125 // Add fixed rates
0126 void Currencies::addFixedRates( int rounding, bool someMoreToCome )
0127 {
0128     int num;
0129     double currencyPrecision;
0130     QString blank("");
0131 
0132     for (num = 0; num < numCurrencies; num++)
0133         if ( currency[num].fixedRate )
0134     {
0135         switch (rounding)
0136         {
0137             case OFFICIAL_RULES:
0138                 currencyPrecision = currency[num].officialRulesPrecision;
0139                 break;
0140             case SMALLEST_COIN:
0141                 currencyPrecision = currency[num].smallestCoinPrecision;
0142                 break;
0143             default:
0144                 currencyPrecision = 1.0;
0145         }
0146         currency[num].rate =
0147             currency[num].fixedRate / currencyPrecision;
0148         currency[num].position = -2;
0149     }
0150     endDownload( someMoreToCome? 0: euroCurrency, blank );
0151 }
0152 
0153 // Add variable rates from European Central Bank
0154 void Currencies::addECBRates( int rounding )
0155 {
0156     KIO::TransferJob *job;
0157 
0158     roundingMethod = rounding;
0159 
0160     job = KIO::get( QUrl( urlECB ), KIO::Reload, KIO::HideProgressInfo );
0161     connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
0162          this, SLOT(httpDataECB(KIO::Job*,QByteArray))
0163         );
0164     connect( job, SIGNAL(result(KJob*)),
0165              this, SLOT(httpResultECB(KJob*))
0166            );
0167 }
0168 
0169 // Add variable rates from New York Federal Reserve Bank
0170 //void Currencies::addNY_FRBRates( int rounding )
0171 //{
0172 //  char url[128];
0173 //  QDate yesterday;
0174 //  KIO::TransferJob *job;
0175 //
0176 //  roundingMethod = rounding;
0177 //
0178 //  yesterday = QDate::currentDate().addDays(-1);
0179 //  // This is suboptimal: we should guess the date of latest working day at 12:00 in New York local time
0180 //  // Or much better: use a URL that does not depend on that date...
0181 //  sprintf(url, urlNY_FRB, yesterday.year(), yesterday.month(), yesterday.day());
0182 //  job = KIO::get( QUrl( url ), KIO::Reload, KIO::HideProgressInfo );
0183 //  connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
0184 //       this, SLOT(httpDataNY_FRB(KIO::Job*,QByteArray))
0185 //      );
0186 //  connect( job, SIGNAL(result(KJob*)),
0187 //           this, SLOT(httpResultNY_FRB(KJob*))
0188 //         );
0189 //}
0190 
0191 // Add variable rates from Time Genie foreign exchange
0192 void Currencies::addTGRates( int rounding )
0193 {
0194     KIO::TransferJob *job;
0195 
0196     roundingMethod = rounding;
0197 
0198     job = KIO::get( QUrl( urlTG ), KIO::Reload, KIO::HideProgressInfo );
0199     connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
0200          this, SLOT(httpDataTG(KIO::Job*,QByteArray))
0201         );
0202     connect( job, SIGNAL(result(KJob*)),
0203              this, SLOT(httpResultTG(KJob*))
0204            );
0205 }
0206 
0207 // Exchange rates received from European Central Bank
0208 void Currencies::httpDataECB(KIO::Job *job, const QByteArray &array)
0209 {
0210     (void) job; // Unused parameter
0211 
0212     if ( array.size() )
0213     {
0214         variableRates += QString(array);
0215     }
0216     else
0217     {
0218         QDomDocument document( "rates" );
0219 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0220         const QDomDocument::ParseResult parseResult = document.setContent(variableRates, QDomDocument::ParseOption::UseNamespaceProcessing);
0221         if (!parseResult) {
0222             qDebug() << QStringLiteral("Can't read variableRates\nError: %1 in Line %2, Column %3")
0223                                    .arg(parseResult.errorMessage).arg(parseResult.errorLine).arg(parseResult.errorColumn);
0224         }
0225 #else
0226         document.setContent( variableRates, true );
0227 #endif
0228         QDomNodeList ratesList = document.elementsByTagName( "Cube" );
0229         int num;
0230         double currencyPrecision;
0231 
0232         for (int i = 0; i < ratesList.count(); i++)
0233         {
0234             QDomElement elt = ratesList.item(i).toElement();
0235             if ( elt.hasAttribute( "time" ) )
0236                 date = elt.attribute( "time" );
0237             else if ( elt.hasAttribute( "currency" ) && elt.hasAttribute( "rate" ) )
0238             {
0239                 QString code(elt.attribute( "currency" ));
0240                 for (num = 0; num < numCurrencies; ++num)
0241                     if ( code == currency[num].code )
0242                         break;
0243                 if ( num < numCurrencies )
0244                 {
0245                     if ( !currency[num].fixedRate )
0246                     {
0247                         switch (roundingMethod)
0248                         {
0249                             case OFFICIAL_RULES:
0250                                 currencyPrecision = currency[num].officialRulesPrecision;
0251                                 break;
0252                             case SMALLEST_COIN:
0253                                 currencyPrecision = currency[num].smallestCoinPrecision;
0254                                 break;
0255                             default:
0256                                 currencyPrecision = 1.0;
0257                         }
0258                         currency[num].rate =
0259                             elt.attribute( "rate" ).toDouble() / currencyPrecision;
0260                         currency[num].position = -2;
0261                     }
0262                 }
0263             }
0264         }
0265         variableRates = "";
0266     }
0267 }
0268 
0269 // End of exchange rates from European Central Bank
0270 void Currencies::httpResultECB(KJob *job)
0271 {
0272     if ( job->error() )
0273     {
0274         clearRates();
0275         endDownload( euroCurrency, "ERROR" );
0276     }
0277     else
0278         endDownload( euroCurrency, date );
0279 }
0280 
0281 // Exchange rates received from New York Federal Reserve Bank
0282 //void Currencies::httpDataNY_FRB(KIO::Job *job, const QByteArray &array)
0283 //{
0284 //  static const char *frbny = "http://www.newyorkfed.org/xml/schemas/FX/utility";
0285 //
0286 //  (void) job; // Unused parameter
0287 //
0288 //  if ( array.size() )
0289 //  {
0290 //      variableRates += QString(array);
0291 //  }
0292 //  else
0293 //  {
0294 //      QDomDocument document( "rates" );
0295 //
0296 //      document.setContent( variableRates, true );
0297 //
0298 //      QDomNodeList ratesList = document.elementsByTagNameNS( frbny, "Series" );
0299 //      int num;
0300 //      double currencyPrecision;
0301 //
0302 //      for (int i = 0; i < ratesList.count(); i++)
0303 //      {
0304 //          QDomElement elt = ratesList.item(i).toElement();
0305 //          QDomNodeList dateElements = elt.elementsByTagNameNS( frbny, "TIME_PERIOD" ),
0306 //                   currencyElements = elt.elementsByTagNameNS( frbny, "CURR" ),
0307 //                   valueElements = elt.elementsByTagNameNS( frbny, "OBS_VALUE" );
0308 //          if (dateElements.count() == 1 && currencyElements.count() == 1 && valueElements.count() == 1)
0309 //          {
0310 //              QDomElement dateElement = dateElements.item(0).toElement(),
0311 //                      currencyElement = currencyElements.item(0).toElement(),
0312 //                      valueElement = valueElements.item(0).toElement();
0313 //              QString code(
0314 //                  elt.attribute( "UNIT" ) == "USD" ?
0315 //                  currencyElement.text():
0316 //                  elt.attribute( "UNIT" )
0317 //                          );
0318 //              for (num = 0; num < numCurrencies; num++)
0319 //                  if ( code == currency[num].code )
0320 //                      break;
0321 //              if ( num < numCurrencies )
0322 //              {
0323 //                  if ( !currency[num].fixedRate )
0324 //                  {
0325 //                      switch (roundingMethod)
0326 //                      {
0327 //                          case OFFICIAL_RULES:
0328 //                              currencyPrecision = currency[num].officialRulesPrecision;
0329 //                              break;
0330 //                          case SMALLEST_COIN:
0331 //                              currencyPrecision = currency[num].smallestCoinPrecision;
0332 //                              break;
0333 //                          default:
0334 //                              currencyPrecision = 1.0;
0335 //                      }
0336 //                      if ( elt.attribute( "UNIT" ) != "USD" )
0337 //                          currency[num].rate =
0338 //                              valueElement.text().toDouble() / currencyPrecision;
0339 //                      else currency[num].rate =
0340 //                          ( 1.0 / valueElement.text().toDouble() ) / currencyPrecision;
0341 //                      currency[num].position = -2;
0342 //                  }
0343 //                  if ( date.isNull() )
0344 //                      date = dateElement.text();
0345 //              }
0346 //          }
0347 //      }
0348 //      variableRates = "";
0349 //  }
0350 //}
0351 
0352 // End of exchange rates from New York Federal Reserve Bank
0353 //void Currencies::httpResultNY_FRB(KJob *job)
0354 //{
0355 //  if ( job->error() )
0356 //  {
0357 //      clearRates();
0358 //      endDownload( dollarCurrency, "ERROR" );
0359 //  }
0360 //  else
0361 //      endDownload( dollarCurrency, date );
0362 //}
0363 
0364 // Exchange rates received from Time Genie foreign exchange
0365 void Currencies::httpDataTG(KIO::Job *job, const QByteArray &array)
0366 {
0367     (void) job; // Unused parameter
0368 
0369     if ( array.size() )
0370     {
0371         variableRates += QString(array);
0372     }
0373     else
0374     {
0375         QDomDocument document( "forex" );
0376 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0377         const QDomDocument::ParseResult parseResult = document.setContent(variableRates, QDomDocument::ParseOption::UseNamespaceProcessing);
0378         if (!parseResult) {
0379             qDebug() << QStringLiteral("Can't read variableRates\nError: %1 in Line %2, Column %3")
0380                                    .arg(parseResult.errorMessage).arg(parseResult.errorLine).arg(parseResult.errorColumn);
0381         }
0382 #else
0383         document.setContent( variableRates, true );
0384 #endif
0385 
0386         QDomNodeList ratesList = document.elementsByTagName( "currency" );
0387         int num;
0388         double currencyPrecision;
0389 
0390         for (int i = 0; i < ratesList.count(); i++)
0391         {
0392             QDomElement elt = ratesList.item(i).toElement();
0393             if ( elt.hasAttribute( "code" ) && elt.hasAttribute( "rate" ) )
0394             {
0395                 QString code(elt.attribute( "code" ));
0396                 for (num = 0; num < numCurrencies; ++num)
0397                     if ( code == currency[num].code )
0398                         break;
0399                 if ( num < numCurrencies )
0400                 {
0401                     if ( !currency[num].fixedRate )
0402                     {
0403                         switch (roundingMethod)
0404                         {
0405                             case OFFICIAL_RULES:
0406                                 currencyPrecision = currency[num].officialRulesPrecision;
0407                                 break;
0408                             case SMALLEST_COIN:
0409                                 currencyPrecision = currency[num].smallestCoinPrecision;
0410                                 break;
0411                             default:
0412                                 currencyPrecision = 1.0;
0413                         }
0414                         currency[num].rate =
0415                             elt.attribute( "rate" ).toDouble() / currencyPrecision;
0416                         currency[num].position = -2;
0417                     }
0418                 }
0419             }
0420         }
0421         variableRates = "";
0422         date = QDate::currentDate().toString("yyyy-MM-dd");   // TG does not provide time information, use current date
0423     }
0424 }
0425 
0426 // End of exchange rates from Time Genie foreign exchange
0427 void Currencies::httpResultTG(KJob *job)
0428 {
0429     if ( job->error() )
0430     {
0431         clearRates();
0432         endDownload( euroCurrency, "ERROR" );
0433     }
0434     else
0435         endDownload( euroCurrency, date );
0436 
0437 }
0438 
0439 #include "moc_currencies.cpp"