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