1/* 2 * Copyright 2003-2012, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel D��rfler, axeld@pinc-software.de 7 * Oliver Tappe, zooey@hirschkaefer.de 8 */ 9 10 11#include <MutableLocaleRoster.h> 12 13#include <pthread.h> 14 15#include <Application.h> 16#include <Autolock.h> 17#include <Catalog.h> 18#include <CatalogData.h> 19#include <DefaultCatalog.h> 20#include <Debug.h> 21#include <Entry.h> 22#include <FormattingConventions.h> 23#include <Language.h> 24#include <LocaleRosterData.h> 25#include <String.h> 26 27 28namespace BPrivate { 29 30 31namespace { 32 33 34static MutableLocaleRoster* sLocaleRoster; 35 36static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT; 37 38 39} // anonymous namespace 40 41 42static void 43InitializeLocaleRoster() 44{ 45 sLocaleRoster = new (std::nothrow) MutableLocaleRoster(); 46} 47 48 49MutableLocaleRoster::MutableLocaleRoster() 50{ 51} 52 53 54MutableLocaleRoster::~MutableLocaleRoster() 55{ 56} 57 58 59/*static*/ MutableLocaleRoster* 60MutableLocaleRoster::Default() 61{ 62 if (sLocaleRoster == NULL) 63 pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster); 64 65 return sLocaleRoster; 66} 67 68 69status_t 70MutableLocaleRoster::SetDefaultFormattingConventions( 71 const BFormattingConventions& newFormattingConventions) 72{ 73 return fData->SetDefaultFormattingConventions(newFormattingConventions); 74} 75 76 77status_t 78MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone) 79{ 80 return fData->SetDefaultTimeZone(newZone); 81} 82 83 84status_t 85MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages) 86{ 87 return fData->SetPreferredLanguages(languages); 88} 89 90 91status_t 92MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred) 93{ 94 return fData->SetFilesystemTranslationPreferred(preferred); 95} 96 97 98status_t 99MutableLocaleRoster::LoadSystemCatalog(BCatalog* catalog) const 100{ 101 if (!catalog) 102 return B_BAD_VALUE; 103 104 // figure out libbe-image (shared object) by name 105 image_info info; 106 int32 cookie = 0; 107 bool found = false; 108 109 while (get_next_image_info(0, &cookie, &info) == B_OK) { 110 if (info.data < (void*)&be_app 111 && (char*)info.data + info.data_size > (void*)&be_app) { 112 found = true; 113 break; 114 } 115 } 116 117 if (!found) 118 return B_ERROR; 119 120 // load the catalog for libbe into the given catalog 121 entry_ref ref; 122 status_t status = BEntry(info.name).GetRef(&ref); 123 if (status != B_OK) 124 return status; 125 126 return catalog->SetTo(ref); 127} 128 129 130/* 131 * creates a new (empty) catalog of the given type (the request is dispatched 132 * to the appropriate add-on). 133 * If the add-on doesn't support catalog-creation or if the creation fails, 134 * NULL is returned, otherwise a pointer to the freshly created catalog. 135 * Any created catalog will be initialized with the given signature and 136 * language-name. 137 */ 138BCatalogData* 139MutableLocaleRoster::CreateCatalog(const char* type, const char* signature, 140 const char* language) 141{ 142 if (!type || !signature || !language) 143 return NULL; 144 145 BAutolock lock(fData->fLock); 146 if (!lock.IsLocked()) 147 return NULL; 148 149 int32 count = fData->fCatalogAddOnInfos.CountItems(); 150 for (int32 i = 0; i < count; ++i) { 151 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 152 fData->fCatalogAddOnInfos.ItemAt(i); 153 if (info->fName.ICompare(type) != 0 || !info->fCreateFunc) 154 continue; 155 156 BCatalogData* catalog = info->fCreateFunc(signature, language); 157 if (catalog != NULL) { 158 info->fLoadedCatalogs.AddItem(catalog); 159 return catalog; 160 } 161 } 162 163 return NULL; 164} 165 166 167/* 168 * Loads a catalog for the given signature, language and fingerprint. 169 * The request to load this catalog is dispatched to all add-ons in turn, 170 * until an add-on reports success. 171 * If a catalog depends on another language (as 'english-british' depends 172 * on 'english') the dependant catalogs are automatically loaded, too. 173 * So it is perfectly possible that this method returns a catalog-chain 174 * instead of a single catalog. 175 * NULL is returned if no matching catalog could be found. 176 */ 177BCatalogData* 178MutableLocaleRoster::LoadCatalog(const entry_ref& catalogOwner, 179 const char* language, int32 fingerprint) const 180{ 181 BAutolock lock(fData->fLock); 182 if (!lock.IsLocked()) 183 return NULL; 184 185 int32 count = fData->fCatalogAddOnInfos.CountItems(); 186 for (int32 i = 0; i < count; ++i) { 187 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 188 fData->fCatalogAddOnInfos.ItemAt(i); 189 190 if (!info->fInstantiateFunc) 191 continue; 192 BMessage languages; 193 if (language != NULL) { 194 // try to load catalogs for the given language: 195 languages.AddString("language", language); 196 } else { 197 // try to load catalogs for one of the preferred languages: 198 GetPreferredLanguages(&languages); 199 } 200 201 BCatalogData* catalog = NULL; 202 const char* lang; 203 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; 204 ++l) { 205 catalog = info->fInstantiateFunc(catalogOwner, lang, fingerprint); 206 if (catalog != NULL) 207 info->fLoadedCatalogs.AddItem(catalog); 208 // Chain-load catalogs for languages that depend on 209 // other languages. 210 // The current implementation uses the filename in order to 211 // detect dependencies (parenthood) between languages (it 212 // traverses from "english_british_oxford" to "english_british" 213 // to "english"): 214 int32 pos; 215 BString langName(lang); 216 BCatalogData* currentCatalog = catalog; 217 BCatalogData* nextCatalog = NULL; 218 while ((pos = langName.FindLast('_')) >= 0) { 219 // language is based on parent, so we load that, too: 220 // (even if the parent catalog was not found) 221 langName.Truncate(pos); 222 nextCatalog = info->fInstantiateFunc(catalogOwner, 223 langName.String(), fingerprint); 224 if (nextCatalog != NULL) { 225 info->fLoadedCatalogs.AddItem(nextCatalog); 226 if (currentCatalog != NULL) 227 currentCatalog->SetNext(nextCatalog); 228 else 229 catalog = nextCatalog; 230 currentCatalog = nextCatalog; 231 } 232 } 233 if (catalog != NULL) 234 return catalog; 235 } 236 } 237 238 return NULL; 239} 240 241 242/* 243 * Loads a catalog for the given signature and language. 244 * 245 * Only the default catalog type is searched, and only the standard system 246 * directories. 247 * 248 * If a catalog depends on another language (as 'english-british' depends 249 * on 'english') the dependant catalogs are automatically loaded, too. 250 * So it is perfectly possible that this method returns a catalog-chain 251 * instead of a single catalog. 252 * NULL is returned if no matching catalog could be found. 253 */ 254BCatalogData* 255MutableLocaleRoster::LoadCatalog(const char* signature, 256 const char* language) const 257{ 258 BAutolock lock(fData->fLock); 259 if (!lock.IsLocked()) 260 return NULL; 261 262 BMessage languages; 263 if (language != NULL) { 264 // try to load catalogs for the given language: 265 languages.AddString("language", language); 266 } else { 267 // try to load catalogs for one of the preferred languages: 268 GetPreferredLanguages(&languages); 269 } 270 271 272 int32 count = fData->fCatalogAddOnInfos.CountItems(); 273 CatalogAddOnInfo* defaultCatalogInfo = NULL; 274 for (int32 i = 0; i < count; ++i) { 275 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 276 fData->fCatalogAddOnInfos.ItemAt(i); 277 if (info->fInstantiateFunc 278 == BPrivate::DefaultCatalog::Instantiate) { 279 defaultCatalogInfo = info; 280 break; 281 } 282 } 283 284 if (defaultCatalogInfo == NULL) 285 return NULL; 286 287 BPrivate::DefaultCatalog* catalog = NULL; 288 const char* lang; 289 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; ++l) { 290 catalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, signature, 291 lang); 292 293 if (catalog != NULL) { 294 if (catalog->InitCheck() != B_OK 295 || catalog->ReadFromStandardLocations() != B_OK) { 296 delete catalog; 297 catalog = NULL; 298 } else { 299 defaultCatalogInfo->fLoadedCatalogs.AddItem(catalog); 300 } 301 } 302 303 // Chain-load catalogs for languages that depend on 304 // other languages. 305 // The current implementation uses the filename in order to 306 // detect dependencies (parenthood) between languages (it 307 // traverses from "english_british_oxford" to "english_british" 308 // to "english"): 309 int32 pos; 310 BString langName(lang); 311 BCatalogData* currentCatalog = catalog; 312 BPrivate::DefaultCatalog* nextCatalog = NULL; 313 while ((pos = langName.FindLast('_')) >= 0) { 314 // language is based on parent, so we load that, too: 315 // (even if the parent catalog was not found) 316 langName.Truncate(pos); 317 nextCatalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, 318 signature, langName.String()); 319 320 if (nextCatalog == NULL) 321 continue; 322 323 if (nextCatalog->InitCheck() != B_OK 324 || nextCatalog->ReadFromStandardLocations() != B_OK) { 325 delete nextCatalog; 326 continue; 327 } 328 329 defaultCatalogInfo->fLoadedCatalogs.AddItem(nextCatalog); 330 331 if (currentCatalog != NULL) 332 currentCatalog->SetNext(nextCatalog); 333 else 334 catalog = nextCatalog; 335 currentCatalog = nextCatalog; 336 } 337 if (catalog != NULL) 338 return catalog; 339 } 340 341 return NULL; 342} 343 344 345/* 346 * unloads the given catalog (or rather: catalog-chain). 347 * Every single catalog of the chain will be deleted automatically. 348 */ 349status_t 350MutableLocaleRoster::UnloadCatalog(BCatalogData* catalog) 351{ 352 if (!catalog) 353 return B_BAD_VALUE; 354 355 BAutolock lock(fData->fLock); 356 if (!lock.IsLocked()) 357 return B_ERROR; 358 359 status_t res = B_ERROR; 360 BCatalogData* nextCatalog; 361 362 while (catalog != NULL) { 363 nextCatalog = catalog->Next(); 364 int32 count = fData->fCatalogAddOnInfos.CountItems(); 365 for (int32 i = 0; i < count; ++i) { 366 CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>( 367 fData->fCatalogAddOnInfos.ItemAt(i)); 368 if (info->fLoadedCatalogs.HasItem(catalog)) { 369 info->fLoadedCatalogs.RemoveItem(catalog); 370 delete catalog; 371 res = B_OK; 372 break; 373 } 374 } 375 catalog = nextCatalog; 376 } 377 return res; 378} 379 380 381} // namespace BPrivate 382