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