xref: /haiku/src/kits/locale/MutableLocaleRoster.cpp (revision 68d37cfb3a755a7270d772b505ee15c8b18aa5e0)
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 
28 namespace BPrivate {
29 
30 
31 namespace {
32 
33 
34 static MutableLocaleRoster* sLocaleRoster;
35 
36 static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT;
37 
38 
39 }	// anonymous namespace
40 
41 
42 static void
43 InitializeLocaleRoster()
44 {
45 	sLocaleRoster = new (std::nothrow) MutableLocaleRoster();
46 }
47 
48 
49 MutableLocaleRoster::MutableLocaleRoster()
50 {
51 }
52 
53 
54 MutableLocaleRoster::~MutableLocaleRoster()
55 {
56 }
57 
58 
59 /*static*/ MutableLocaleRoster*
60 MutableLocaleRoster::Default()
61 {
62 	if (sLocaleRoster == NULL)
63 		pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster);
64 
65 	return sLocaleRoster;
66 }
67 
68 
69 status_t
70 MutableLocaleRoster::SetDefaultFormattingConventions(
71 	const BFormattingConventions& newFormattingConventions)
72 {
73 	return fData->SetDefaultFormattingConventions(newFormattingConventions);
74 }
75 
76 
77 status_t
78 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone)
79 {
80 	return fData->SetDefaultTimeZone(newZone);
81 }
82 
83 
84 status_t
85 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages)
86 {
87 	return fData->SetPreferredLanguages(languages);
88 }
89 
90 
91 status_t
92 MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred)
93 {
94 	return fData->SetFilesystemTranslationPreferred(preferred);
95 }
96 
97 
98 status_t
99 MutableLocaleRoster::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  */
138 BCatalogData*
139 MutableLocaleRoster::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->MakeSureItsLoaded()
154 			|| !info->fCreateFunc)
155 			continue;
156 
157 		BCatalogData* catalog = info->fCreateFunc(signature, language);
158 		if (catalog != NULL) {
159 			info->fLoadedCatalogs.AddItem(catalog);
160 			info->UnloadIfPossible();
161 			return catalog;
162 		}
163 	}
164 
165 	return NULL;
166 }
167 
168 
169 /*
170  * Loads a catalog for the given signature, language and fingerprint.
171  * The request to load this catalog is dispatched to all add-ons in turn,
172  * until an add-on reports success.
173  * If a catalog depends on another language (as 'english-british' depends
174  * on 'english') the dependant catalogs are automatically loaded, too.
175  * So it is perfectly possible that this method returns a catalog-chain
176  * instead of a single catalog.
177  * NULL is returned if no matching catalog could be found.
178  */
179 BCatalogData*
180 MutableLocaleRoster::LoadCatalog(const entry_ref& catalogOwner,
181 	const char* language, int32 fingerprint) const
182 {
183 	BAutolock lock(fData->fLock);
184 	if (!lock.IsLocked())
185 		return NULL;
186 
187 	int32 count = fData->fCatalogAddOnInfos.CountItems();
188 	for (int32 i = 0; i < count; ++i) {
189 		CatalogAddOnInfo* info = (CatalogAddOnInfo*)
190 			fData->fCatalogAddOnInfos.ItemAt(i);
191 
192 		if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc)
193 			continue;
194 		BMessage languages;
195 		if (language != NULL) {
196 			// try to load catalogs for the given language:
197 			languages.AddString("language", language);
198 		} else {
199 			// try to load catalogs for one of the preferred languages:
200 			GetPreferredLanguages(&languages);
201 		}
202 
203 		BCatalogData* catalog = NULL;
204 		const char* lang;
205 		for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK;
206 			++l) {
207 			catalog = info->fInstantiateFunc(catalogOwner, lang, fingerprint);
208 			if (catalog != NULL)
209 				info->fLoadedCatalogs.AddItem(catalog);
210 			// Chain-load catalogs for languages that depend on
211 			// other languages.
212 			// The current implementation uses the filename in order to
213 			// detect dependencies (parenthood) between languages (it
214 			// traverses from "english_british_oxford" to "english_british"
215 			// to "english"):
216 			int32 pos;
217 			BString langName(lang);
218 			BCatalogData* currentCatalog = catalog;
219 			BCatalogData* nextCatalog = NULL;
220 			while ((pos = langName.FindLast('_')) >= 0) {
221 				// language is based on parent, so we load that, too:
222 				// (even if the parent catalog was not found)
223 				langName.Truncate(pos);
224 				nextCatalog = info->fInstantiateFunc(catalogOwner,
225 					langName.String(), fingerprint);
226 				if (nextCatalog != NULL) {
227 					info->fLoadedCatalogs.AddItem(nextCatalog);
228 					if (currentCatalog != NULL)
229 						currentCatalog->SetNext(nextCatalog);
230 					else
231 						catalog = nextCatalog;
232 					currentCatalog = nextCatalog;
233 				}
234 			}
235 			if (catalog != NULL)
236 				return catalog;
237 		}
238 		info->UnloadIfPossible();
239 	}
240 
241 	return NULL;
242 }
243 
244 
245 /*
246  * Loads a catalog for the given signature and language.
247  *
248  * Only the default catalog type is searched, and only the standard system
249  * directories.
250  *
251  * If a catalog depends on another language (as 'english-british' depends
252  * on 'english') the dependant catalogs are automatically loaded, too.
253  * So it is perfectly possible that this method returns a catalog-chain
254  * instead of a single catalog.
255  * NULL is returned if no matching catalog could be found.
256  */
257 BCatalogData*
258 MutableLocaleRoster::LoadCatalog(const char* signature,
259 	const char* language) const
260 {
261 	BAutolock lock(fData->fLock);
262 	if (!lock.IsLocked())
263 		return NULL;
264 
265 	BMessage languages;
266 	if (language != NULL) {
267 		// try to load catalogs for the given language:
268 		languages.AddString("language", language);
269 	} else {
270 		// try to load catalogs for one of the preferred languages:
271 		GetPreferredLanguages(&languages);
272 	}
273 
274 
275 	int32 count = fData->fCatalogAddOnInfos.CountItems();
276 	CatalogAddOnInfo* defaultCatalogInfo = NULL;
277 	for (int32 i = 0; i < count; ++i) {
278 		CatalogAddOnInfo* info = (CatalogAddOnInfo*)
279 			fData->fCatalogAddOnInfos.ItemAt(i);
280 		if (info->MakeSureItsLoaded()
281 			&& info->fInstantiateFunc
282 				== BPrivate::DefaultCatalog::Instantiate) {
283 			defaultCatalogInfo = info;
284 			break;
285 		}
286 	}
287 
288 	if (defaultCatalogInfo == NULL)
289 		return NULL;
290 
291 	BPrivate::DefaultCatalog* catalog = NULL;
292 	const char* lang;
293 	for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; ++l) {
294 		catalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, signature,
295 			lang);
296 		if (catalog == NULL)
297 			continue;
298 
299 		if (catalog->InitCheck() != B_OK
300 			|| catalog->ReadFromStandardLocations() != B_OK) {
301 			delete catalog;
302 			continue;
303 		}
304 
305 		defaultCatalogInfo->fLoadedCatalogs.AddItem(catalog);
306 
307 		// Chain-load catalogs for languages that depend on
308 		// other languages.
309 		// The current implementation uses the filename in order to
310 		// detect dependencies (parenthood) between languages (it
311 		// traverses from "english_british_oxford" to "english_british"
312 		// to "english"):
313 		int32 pos;
314 		BString langName(lang);
315 		BCatalogData* currentCatalog = catalog;
316 		BPrivate::DefaultCatalog* nextCatalog = NULL;
317 		while ((pos = langName.FindLast('_')) >= 0) {
318 			// language is based on parent, so we load that, too:
319 			// (even if the parent catalog was not found)
320 			langName.Truncate(pos);
321 			nextCatalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL,
322 				signature, langName.String());
323 
324 			if (nextCatalog == NULL)
325 				continue;
326 
327 			if (nextCatalog->InitCheck() != B_OK
328 				|| nextCatalog->ReadFromStandardLocations() != B_OK) {
329 				delete nextCatalog;
330 				continue;
331 			}
332 
333 			defaultCatalogInfo->fLoadedCatalogs.AddItem(nextCatalog);
334 
335 			if (currentCatalog != NULL)
336 				currentCatalog->SetNext(nextCatalog);
337 			else
338 				catalog = nextCatalog;
339 			currentCatalog = nextCatalog;
340 		}
341 		if (catalog != NULL)
342 			return catalog;
343 	}
344 
345 	return NULL;
346 }
347 
348 
349 /*
350  * unloads the given catalog (or rather: catalog-chain).
351  * Every single catalog of the chain will be deleted automatically.
352  * Add-ons that have no more current catalogs are unloaded, too.
353  */
354 status_t
355 MutableLocaleRoster::UnloadCatalog(BCatalogData* catalog)
356 {
357 	if (!catalog)
358 		return B_BAD_VALUE;
359 
360 	BAutolock lock(fData->fLock);
361 	if (!lock.IsLocked())
362 		return B_ERROR;
363 
364 	status_t res = B_ERROR;
365 	BCatalogData* nextCatalog;
366 
367 	while (catalog != NULL) {
368 		nextCatalog = catalog->Next();
369 		int32 count = fData->fCatalogAddOnInfos.CountItems();
370 		for (int32 i = 0; i < count; ++i) {
371 			CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>(
372 				fData->fCatalogAddOnInfos.ItemAt(i));
373 			if (info->fLoadedCatalogs.HasItem(catalog)) {
374 				info->fLoadedCatalogs.RemoveItem(catalog);
375 				delete catalog;
376 				info->UnloadIfPossible();
377 				res = B_OK;
378 				break;
379 			}
380 		}
381 		catalog = nextCatalog;
382 	}
383 	return res;
384 }
385 
386 
387 }	// namespace BPrivate
388