holidayregion.cpp
00001 /* 00002 This file is part of the kholidays library. 00003 00004 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (c) 2004 Allen Winter <winter@kde.org> 00006 Copyright (c) 2008 David Jarvie <djarvie@kde.org> 00007 Copyright 2010 John Layt <john@layt.net> 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License as published by the Free Software Foundation; either 00012 version 2 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 GNU Library General Public License for more details. 00018 00019 You should have received a copy of the GNU Library General Public License 00020 along with this library; see the file COPYING.LIB. If not, write to the 00021 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00022 Boston, MA 02110-1301, USA. 00023 */ 00024 00025 #include "holidayregion.h" 00026 00027 #include <QtCore/QDateTime> 00028 #include <QtCore/QFile> 00029 #include <QtCore/QSharedData> 00030 #include <QtCore/QFileInfo> 00031 00032 #include <KStandardDirs> 00033 #include <KGlobal> 00034 #include <KLocale> 00035 #include <KDebug> 00036 00037 #include "holiday_p.h" 00038 #include "parsers/plan2/holidayparserdriverplan_p.h" 00039 00040 using namespace KHolidays; 00041 00042 class HolidayRegion::Private 00043 { 00044 public: 00045 Private( const QString ®ionCode ) : mDriver( 0 ), 00046 mRegionCode( regionCode ) 00047 { 00048 if ( !mRegionCode.isEmpty() ) { 00049 00050 if ( mRegionCode.length() == 2 ) { //Backwards compatible mode for old location code 00051 mLocation = mRegionCode; 00052 QStringList locationFiles = KGlobal::dirs()->findAllResources( "data", 00053 "libkholidays/plan2/holiday_" + mLocation + '*', 00054 KStandardDirs::NoDuplicates ); 00055 if ( locationFiles.count() > 0 ) { 00056 mRegionCode = locationFiles.at( 0 ). 00057 mid( locationFiles.at( 0 ).lastIndexOf( "holiday_" ) + 8 ); 00058 } 00059 } 00060 00061 mHolidayFile.setFile( 00062 KStandardDirs::locate( "data", "libkholidays/plan2/holiday_" + mRegionCode ) ); 00063 } 00064 00065 init(); 00066 } 00067 00068 Private( const QFileInfo ®ionFile ) : mDriver( 0 ), 00069 mHolidayFile( regionFile ) 00070 { 00071 init(); 00072 } 00073 00074 ~Private() 00075 { 00076 delete mDriver; 00077 } 00078 00079 void init() 00080 { 00081 if ( mHolidayFile.exists() ) { 00082 mDriver = new HolidayParserDriverPlan( mHolidayFile.absoluteFilePath() ); 00083 if ( mDriver ) { 00084 00085 if ( mLocation.isEmpty() ) { 00086 mLocation = mDriver->fileCountryCode().left( 2 ); 00087 } 00088 00089 if ( mRegionCode.isEmpty() ) { 00090 if ( mHolidayFile.fileName().startsWith( QLatin1String( "holiday_" ) ) ) { 00091 mRegionCode = mHolidayFile.fileName().mid( 8 ); 00092 } else { 00093 mRegionCode = mHolidayFile.fileName(); 00094 } 00095 } 00096 00097 } else { 00098 mRegionCode.clear(); 00099 mLocation.clear(); 00100 } 00101 } else { 00102 mRegionCode.clear(); 00103 mLocation.clear(); 00104 } 00105 } 00106 00107 HolidayParserDriver *mDriver; // The parser driver for the holiday file 00108 QString mRegionCode; // region code of holiday region 00109 QString mLocation; // old location code, use now deprecated 00110 QFileInfo mHolidayFile; // file containing holiday data, or null 00111 }; 00112 00113 HolidayRegion::HolidayRegion( const QString ®ionCode ) 00114 : d( new Private( regionCode ) ) 00115 { 00116 } 00117 00118 HolidayRegion::HolidayRegion( const QFileInfo ®ionFile ) 00119 : d( new Private( regionFile ) ) 00120 { 00121 } 00122 00123 HolidayRegion::~HolidayRegion() 00124 { 00125 delete d; 00126 } 00127 00128 QStringList HolidayRegion::locations() 00129 { 00130 const QStringList files = 00131 KGlobal::dirs()->findAllResources( "data", "libkholidays/plan2/holiday_*", 00132 KStandardDirs::NoDuplicates ); 00133 00134 QStringList locations; 00135 foreach ( const QString &filename, files ) { 00136 locations.append( filename.mid( filename.lastIndexOf( "holiday_" ) + 8, 2 ) ); 00137 } 00138 00139 locations.removeDuplicates(); 00140 qSort( locations ); 00141 return locations; 00142 } 00143 00144 QString HolidayRegion::location() const 00145 { 00146 return d->mLocation; 00147 } 00148 00149 QStringList HolidayRegion::regionCodes() 00150 { 00151 const QStringList files = 00152 KGlobal::dirs()->findAllResources( "data", "libkholidays/plan2/holiday_*", 00153 KStandardDirs::NoDuplicates ); 00154 00155 QStringList regionCodesList; 00156 foreach ( const QString &filename, files ) { 00157 regionCodesList.append( filename.mid( filename.lastIndexOf( "holiday_" ) + 8 ) ); 00158 } 00159 00160 qSort( regionCodesList ); 00161 return regionCodesList; 00162 } 00163 00164 QString HolidayRegion::regionCode() const 00165 { 00166 return d->mRegionCode; 00167 } 00168 00169 QString HolidayRegion::countryCode() const 00170 { 00171 return d->mDriver->fileCountryCode(); 00172 } 00173 00174 QString HolidayRegion::countryCode( const QString ®ionCode ) 00175 { 00176 HolidayRegion temp = HolidayRegion( regionCode ); 00177 if ( temp.isValid() ) { 00178 return temp.countryCode(); 00179 } else { 00180 return QString(); 00181 } 00182 } 00183 00184 QString HolidayRegion::languageCode() const 00185 { 00186 return d->mDriver->fileLanguageCode(); 00187 } 00188 00189 QString HolidayRegion::languageCode( const QString ®ionCode ) 00190 { 00191 HolidayRegion temp = HolidayRegion( regionCode ); 00192 if ( temp.isValid() ) { 00193 return temp.languageCode(); 00194 } else { 00195 return QString(); 00196 } 00197 } 00198 00199 QString HolidayRegion::name() const 00200 { 00201 QString tempName = d->mDriver->fileName(); 00202 00203 if ( tempName.isEmpty() ) { 00204 QStringList countryParts = countryCode().toLower().split( '-' ); 00205 QString country = countryParts.at( 0 ); 00206 QString regionName, typeName; 00207 00208 if ( country != "xx" ) { 00209 if ( countryParts.count() == 2 ) { 00210 // Temporary measure to get regions translated, only those files that already exist 00211 // In 4.6 hope to have isocodes project integration for translations via KLocale 00212 QString subdivision = countryParts.at( 1 ); 00213 if ( country == "ca" && subdivision == "qc" ) { 00214 regionName = i18nc( "Canadian region", "Quebec" ); 00215 } else if ( country == "de" && subdivision == "by" ) { 00216 regionName = i18nc( "German region", "Bavaria" ); 00217 } else if ( country == "es" && subdivision == "ct" ) { 00218 regionName = i18nc( "Spanish region", "Catalonia" ); 00219 } else if ( country == "gb" && subdivision == "eaw" ) { 00220 regionName = i18nc( "UK Region", "England and Wales" ); 00221 } else if ( country == "gb" && subdivision == "eng" ) { 00222 regionName = i18nc( "UK Region", "England" ); 00223 } else if ( country == "gb" && subdivision == "wls" ) { 00224 regionName = i18nc( "UK Region", "Wales" ); 00225 } else if ( country == "gb" && subdivision == "sct" ) { 00226 regionName = i18nc( "UK Region", "Scotland" ); 00227 } else if ( country == "gb" && subdivision == "nir" ) { 00228 regionName = i18nc( "UK Region", "Northern Ireland" ); 00229 } else if ( country == "it" && subdivision == "bz" ) { 00230 regionName = i18nc( "Italian Region", "South Tyrol" ); 00231 } else if ( country == "au" && subdivision == "nsw" ) { 00232 regionName = i18nc( "Australian Region", "New South Wales" ); 00233 } else if ( country == "au" && subdivision == "qld" ) { 00234 regionName = i18nc( "Australian Region", "Queensland" ); 00235 } else if ( country == "au" && subdivision == "vic" ) { 00236 regionName = i18nc( "Australian Region", "Victoria" ); 00237 } else if ( country == "au" && subdivision == "sa" ) { 00238 regionName = i18nc( "Australian Region", "South Australia" ); 00239 } else if ( country == "au" && subdivision == "nt" ) { 00240 regionName = i18nc( "Australian Region", "Northern Territory" ); 00241 } else if ( country == "au" && subdivision == "act" ) { 00242 regionName = i18nc( "Australian Region", "Australian Capital Territory" ); 00243 } else if ( country == "au" && subdivision == "wa" ) { 00244 regionName = i18nc( "Australian Region", "Western Australia" ); 00245 } else if ( country == "au" && subdivision == "tas" ) { 00246 regionName = i18nc( "Australian Region", "Tasmania" ); 00247 } else { 00248 regionName = KGlobal::locale()->countryCodeToName( country ); 00249 } 00250 } else { 00251 regionName = KGlobal::locale()->countryCodeToName( country ); 00252 } 00253 } 00254 00255 //Cheat on type for now,take direct from region code until API is introduced in SC 4.6 00256 QStringList regionParts = regionCode().toLower().split( '_' ); 00257 if ( regionParts.count() == 3 ) { 00258 QString type = regionParts.at( 2 ); 00259 // Will create lots more in 4.6 00260 // Religious types, just simple for now 00261 if ( type == "public" ) { 00262 typeName = i18nc( "Holiday type", "Public" ); 00263 } else if ( type == "religious" ) { 00264 typeName = i18nc( "Holiday type", "Religious" ); 00265 } else if ( type == "financial" ) { 00266 typeName = i18nc( "Holiday type", "Financial" ); 00267 } else if ( type == "cultural" ) { 00268 typeName = i18nc( "Holiday type", "Cultural" ); 00269 } else if ( type == "school" ) { 00270 typeName = i18nc( "Holiday type", "School" ); 00271 } else if ( type == "seasons" ) { 00272 typeName = i18nc( "Holiday type", "Seasons" ); 00273 } else if ( type == "name" ) { 00274 typeName = i18nc( "Holiday type", "Name Days" ); 00275 } else if ( type == "personal" ) { 00276 typeName = i18nc( "Holiday type", "Personal" ); 00277 } else if ( type == "catholic" ) { 00278 typeName = i18nc( "Holiday type", "Catholic" ); 00279 } else if ( type == "protestant" ) { 00280 typeName = i18nc( "Holiday type", "Protestant" ); 00281 } else if ( type == "orthodox" ) { 00282 typeName = i18nc( "Holiday type", "Orthodox" ); 00283 } else if ( type == "jewish" ) { 00284 typeName = i18nc( "Holiday type", "Jewish" ); 00285 } else if ( type == "islamic" ) { 00286 typeName = i18nc( "Holiday type", "Islamic" ); 00287 } 00288 } 00289 00290 if ( !regionName.isEmpty() ) { 00291 if ( !typeName.isEmpty() ) { 00292 //TODO translate when not frozen 00293 tempName = QString( "%1 - %2" ).arg( regionName ).arg( typeName ); 00294 } else { 00295 tempName = regionName; 00296 } 00297 } else if ( !typeName.isEmpty() ) { 00298 tempName = typeName; 00299 } else { 00300 tempName = i18nc( "Unknown holiday region", "Unknown" ); 00301 } 00302 } 00303 return tempName; 00304 } 00305 00306 QString HolidayRegion::name( const QString ®ionCode ) 00307 { 00308 HolidayRegion temp = HolidayRegion( regionCode ); 00309 if ( temp.isValid() ) { 00310 return temp.name(); 00311 } else { 00312 return QString(); 00313 } 00314 } 00315 00316 QString HolidayRegion::description() const 00317 { 00318 return d->mDriver->fileDescription(); 00319 } 00320 00321 QString HolidayRegion::description( const QString ®ionCode ) 00322 { 00323 HolidayRegion temp = HolidayRegion( regionCode ); 00324 if ( temp.isValid() ) { 00325 return temp.description(); 00326 } else { 00327 return QString(); 00328 } 00329 } 00330 00331 bool HolidayRegion::isValid() const 00332 { 00333 return d->mHolidayFile.exists() && d->mDriver; 00334 } 00335 00336 bool HolidayRegion::isValid( const QString ®ionCode ) 00337 { 00338 HolidayRegion temp = HolidayRegion( regionCode ); 00339 return temp.isValid(); 00340 } 00341 00342 Holiday::List HolidayRegion::holidays( const QDate &startDate, const QDate &endDate ) const 00343 { 00344 return holidays( startDate, endDate, Holiday::MultidayHolidaysAsMultipleEvents ); 00345 } 00346 00347 Holiday::List HolidayRegion::holidays( const QDate &startDate, const QDate &endDate, 00348 Holiday::MultidayMode multidayMode ) const 00349 { 00350 if ( isValid() ) { 00351 return d->mDriver->parseHolidays( startDate, endDate, multidayMode ); 00352 } else { 00353 return Holiday::List(); 00354 } 00355 } 00356 00357 Holiday::List HolidayRegion::holidays( const QDate &date ) const 00358 { 00359 return holidays( date, Holiday::MultidayHolidaysAsMultipleEvents ); 00360 } 00361 00362 Holiday::List HolidayRegion::holidays( const QDate &date, Holiday::MultidayMode multidayMode ) const 00363 { 00364 if ( isValid() ) { 00365 return d->mDriver->parseHolidays( date, multidayMode ); 00366 } else { 00367 return Holiday::List(); 00368 } 00369 } 00370 00371 Holiday::List HolidayRegion::holidays( int calendarYear, const QString &calendarType ) const 00372 { 00373 return holidays( calendarYear, calendarType, Holiday::MultidayHolidaysAsMultipleEvents ); 00374 } 00375 00376 Holiday::List HolidayRegion::holidays( int calendarYear, const QString &calendarType, 00377 Holiday::MultidayMode multidayMode ) const 00378 { 00379 if ( isValid() ) { 00380 return d->mDriver->parseHolidays( calendarYear, calendarType, multidayMode ); 00381 } else { 00382 return Holiday::List(); 00383 } 00384 } 00385 00386 bool HolidayRegion::isHoliday( const QDate &date ) const 00387 { 00388 Holiday::List holidayList = holidays( date, Holiday::MultidayHolidaysAsMultipleEvents ); 00389 if ( holidayList.count() > 0 ) { 00390 foreach ( const KHolidays::Holiday &holiday, holidayList ) { 00391 if ( holiday.dayType() == Holiday::NonWorkday ) { 00392 return true; 00393 } 00394 } 00395 } 00396 return false; 00397 } 00398 00399 QString HolidayRegion::defaultRegionCode( const QString &country, const QString &language ) 00400 { 00401 // Try to match against the users country and language, or failing that the language country. 00402 // Scan through all the regions finding the first match for each possible default 00403 // Holiday Region Country Code can be a country subdivision or the country itself, 00404 // e.g. US or US-CA for California, so we can try match on both but an exact match has priority 00405 // The Holiday Region file is in one language only, so give priority to any file in the 00406 // users language, e.g. bilingual countries with a separate file for each language 00407 // Locale language can have a country code embedded in it e.g. en_GB, which we can try use if 00408 // no country set, but a lot of countries use en_GB so it's a lower priority option 00409 00410 QString localeCountry, localeLanguage, localeLanguageCountry; 00411 00412 if ( country.isEmpty() ) { 00413 localeCountry = KGlobal::locale()->country().toLower(); 00414 } else { 00415 localeCountry = country.toLower(); 00416 } 00417 00418 if ( language.isEmpty() ) { 00419 localeLanguage = KGlobal::locale()->language().toLower(); 00420 } else { 00421 localeLanguage = language.toLower(); 00422 } 00423 00424 if ( localeLanguage.split( '_' ).count() > 1 ) { 00425 localeLanguageCountry = localeLanguage.split( '_' ).at( 1 ); 00426 } 00427 00428 QStringList regionList = KHolidays::HolidayRegion::regionCodes(); 00429 00430 QString countryAndLanguageMatch, countryOnlyMatch, subdivisionAndLanguageMatch, 00431 subdivisionOnlyMatch, languageCountryAndLanguageMatch, languageCountryOnlyMatch, 00432 languageSubdivisionAndLanguageMatch, languageSubdivisionOnlyMatch; 00433 00434 foreach ( const QString ®ionCode, regionList ) { 00435 QString regionCountry = KHolidays::HolidayRegion::countryCode( regionCode ).toLower(); 00436 QString regionSubdivisionCountry; 00437 if ( regionCountry.split( '-' ).count() > 1 ) { 00438 regionSubdivisionCountry = regionCountry.split( '-' ).at( 0 ); 00439 } 00440 QString regionLanguage = KHolidays::HolidayRegion::languageCode( regionCode ).toLower(); 00441 00442 if ( regionCountry == localeCountry && regionLanguage == localeLanguage ) { 00443 countryAndLanguageMatch = regionCode; 00444 break; // exact match so don't look further 00445 } else if ( regionCountry == localeCountry ) { 00446 if ( countryOnlyMatch.isEmpty() ) { 00447 countryOnlyMatch = regionCode; 00448 } 00449 } else if ( !regionSubdivisionCountry.isEmpty() && 00450 regionSubdivisionCountry == localeCountry && 00451 regionLanguage == localeLanguage ) { 00452 if ( subdivisionAndLanguageMatch.isEmpty() ) { 00453 subdivisionAndLanguageMatch = regionCode; 00454 } 00455 } else if ( !regionSubdivisionCountry.isEmpty() && regionSubdivisionCountry == localeCountry ) { 00456 if ( subdivisionOnlyMatch.isEmpty() ) { 00457 subdivisionOnlyMatch = regionCode; 00458 } 00459 } else if ( !localeLanguageCountry.isEmpty() && 00460 regionCountry == localeLanguageCountry && 00461 regionLanguage == localeLanguage ) { 00462 if ( languageCountryAndLanguageMatch.isEmpty() ) { 00463 languageCountryAndLanguageMatch = regionCode; 00464 } 00465 } else if ( !localeLanguageCountry.isEmpty() && regionCountry == localeLanguageCountry ) { 00466 if ( languageCountryOnlyMatch.isEmpty() ) { 00467 languageCountryOnlyMatch = regionCode; 00468 } 00469 } else if ( !regionSubdivisionCountry.isEmpty() && 00470 !localeLanguageCountry.isEmpty() && 00471 regionSubdivisionCountry == localeLanguageCountry && 00472 regionLanguage == localeLanguage ) { 00473 if ( languageSubdivisionAndLanguageMatch.isEmpty() ) { 00474 languageSubdivisionAndLanguageMatch = regionCode; 00475 } 00476 } else if ( !regionSubdivisionCountry.isEmpty() && 00477 !localeLanguageCountry.isEmpty() && 00478 regionSubdivisionCountry == localeLanguageCountry ) { 00479 if ( languageSubdivisionOnlyMatch.isEmpty() ) { 00480 languageSubdivisionOnlyMatch = regionCode; 00481 } 00482 } 00483 } 00484 00485 QString defaultRegionCode; 00486 00487 if ( !countryAndLanguageMatch.isEmpty() ) { 00488 defaultRegionCode = countryAndLanguageMatch; 00489 } else if ( !countryOnlyMatch.isEmpty() ) { 00490 defaultRegionCode = countryOnlyMatch; 00491 } else if ( !subdivisionAndLanguageMatch.isEmpty() ) { 00492 defaultRegionCode = subdivisionAndLanguageMatch; 00493 } else if ( !subdivisionOnlyMatch.isEmpty() ) { 00494 defaultRegionCode = subdivisionOnlyMatch; 00495 } else if ( !languageCountryAndLanguageMatch.isEmpty() ) { 00496 defaultRegionCode = languageCountryAndLanguageMatch; 00497 } else if ( !languageCountryOnlyMatch.isEmpty() ) { 00498 defaultRegionCode = languageCountryOnlyMatch; 00499 } else if ( !languageSubdivisionAndLanguageMatch.isEmpty() ) { 00500 defaultRegionCode = languageSubdivisionAndLanguageMatch; 00501 } else if ( !languageSubdivisionOnlyMatch.isEmpty() ) { 00502 defaultRegionCode = languageSubdivisionOnlyMatch; 00503 } 00504 00505 return defaultRegionCode; 00506 }