xref: /haiku/src/kits/locale/MutableLocaleRoster.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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->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  */
177 BCatalogData*
178 MutableLocaleRoster::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  */
254 BCatalogData*
255 MutableLocaleRoster::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  */
349 status_t
350 MutableLocaleRoster::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