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 <Debug.h>
20#include <Entry.h>
21#include <FormattingConventions.h>
22#include <Language.h>
23#include <LocaleRosterData.h>
24#include <String.h>
25
26
27namespace BPrivate {
28
29
30namespace {
31
32
33static MutableLocaleRoster* sLocaleRoster;
34
35static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT;
36
37
38}	// anonymous namespace
39
40
41static void
42InitializeLocaleRoster()
43{
44	sLocaleRoster = new (std::nothrow) MutableLocaleRoster();
45}
46
47
48MutableLocaleRoster::MutableLocaleRoster()
49{
50}
51
52
53MutableLocaleRoster::~MutableLocaleRoster()
54{
55}
56
57
58/*static*/ MutableLocaleRoster*
59MutableLocaleRoster::Default()
60{
61	if (sLocaleRoster == NULL)
62		pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster);
63
64	return sLocaleRoster;
65}
66
67
68status_t
69MutableLocaleRoster::SetDefaultFormattingConventions(
70	const BFormattingConventions& newFormattingConventions)
71{
72	return fData->SetDefaultFormattingConventions(newFormattingConventions);
73}
74
75
76status_t
77MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone)
78{
79	return fData->SetDefaultTimeZone(newZone);
80}
81
82
83status_t
84MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages)
85{
86	return fData->SetPreferredLanguages(languages);
87}
88
89
90status_t
91MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred)
92{
93	return fData->SetFilesystemTranslationPreferred(preferred);
94}
95
96
97status_t
98MutableLocaleRoster::LoadSystemCatalog(BCatalog* catalog) const
99{
100	if (!catalog)
101		return B_BAD_VALUE;
102
103	// figure out libbe-image (shared object) by name
104	image_info info;
105	int32 cookie = 0;
106	bool found = false;
107
108	while (get_next_image_info(0, &cookie, &info) == B_OK) {
109		if (info.data < (void*)&be_app
110			&& (char*)info.data + info.data_size > (void*)&be_app) {
111			found = true;
112			break;
113		}
114	}
115
116	if (!found)
117		return B_ERROR;
118
119	// load the catalog for libbe into the given catalog
120	entry_ref ref;
121	status_t status = BEntry(info.name).GetRef(&ref);
122	if (status != B_OK)
123		return status;
124
125	return catalog->SetTo(ref);
126}
127
128
129/*
130 * creates a new (empty) catalog of the given type (the request is dispatched
131 * to the appropriate add-on).
132 * If the add-on doesn't support catalog-creation or if the creation fails,
133 * NULL is returned, otherwise a pointer to the freshly created catalog.
134 * Any created catalog will be initialized with the given signature and
135 * language-name.
136 */
137BCatalogData*
138MutableLocaleRoster::CreateCatalog(const char* type, const char* signature,
139	const char* language)
140{
141	if (!type || !signature || !language)
142		return NULL;
143
144	BAutolock lock(fData->fLock);
145	if (!lock.IsLocked())
146		return NULL;
147
148	int32 count = fData->fCatalogAddOnInfos.CountItems();
149	for (int32 i = 0; i < count; ++i) {
150		CatalogAddOnInfo* info = (CatalogAddOnInfo*)
151			fData->fCatalogAddOnInfos.ItemAt(i);
152		if (info->fName.ICompare(type)!=0 || !info->MakeSureItsLoaded()
153			|| !info->fCreateFunc)
154			continue;
155
156		BCatalogData* catalog = info->fCreateFunc(signature, language);
157		if (catalog) {
158			info->fLoadedCatalogs.AddItem(catalog);
159			info->UnloadIfPossible();
160			return catalog;
161		}
162	}
163
164	return NULL;
165}
166
167
168/*
169 * Loads a catalog for the given signature, language and fingerprint.
170 * The request to load this catalog is dispatched to all add-ons in turn,
171 * until an add-on reports success.
172 * If a catalog depends on another language (as 'english-british' depends
173 * on 'english') the dependant catalogs are automatically loaded, too.
174 * So it is perfectly possible that this method returns a catalog-chain
175 * instead of a single catalog.
176 * NULL is returned if no matching catalog could be found.
177 */
178BCatalogData*
179MutableLocaleRoster::LoadCatalog(const entry_ref& catalogOwner,
180	const char* language, int32 fingerprint) const
181{
182	BAutolock lock(fData->fLock);
183	if (!lock.IsLocked())
184		return NULL;
185
186	int32 count = fData->fCatalogAddOnInfos.CountItems();
187	for (int32 i = 0; i < count; ++i) {
188		CatalogAddOnInfo* info = (CatalogAddOnInfo*)
189			fData->fCatalogAddOnInfos.ItemAt(i);
190
191		if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc)
192			continue;
193		BMessage languages;
194		if (language)
195			// try to load catalogs for the given language:
196			languages.AddString("language", language);
197		else
198			// try to load catalogs for one of the preferred languages:
199			GetPreferredLanguages(&languages);
200
201		BCatalogData* catalog = NULL;
202		const char* lang;
203		for (int32 l=0; languages.FindString("language", l, &lang)==B_OK; ++l) {
204			catalog = info->fInstantiateFunc(catalogOwner, lang, fingerprint);
205			if (catalog)
206				info->fLoadedCatalogs.AddItem(catalog);
207			// Chain-load catalogs for languages that depend on
208			// other languages.
209			// The current implementation uses the filename in order to
210			// detect dependencies (parenthood) between languages (it
211			// traverses from "english_british_oxford" to "english_british"
212			// to "english"):
213			int32 pos;
214			BString langName(lang);
215			BCatalogData* currCatalog = catalog;
216			BCatalogData* nextCatalog = NULL;
217			while ((pos = langName.FindLast('_')) >= 0) {
218				// language is based on parent, so we load that, too:
219				// (even if the parent catalog was not found)
220				langName.Truncate(pos);
221				nextCatalog = info->fInstantiateFunc(catalogOwner,
222					langName.String(), fingerprint);
223				if (nextCatalog) {
224					info->fLoadedCatalogs.AddItem(nextCatalog);
225					if(currCatalog)
226						currCatalog->SetNext(nextCatalog);
227					else
228						catalog = nextCatalog;
229					currCatalog = nextCatalog;
230				}
231			}
232			if (catalog != NULL)
233				return catalog;
234		}
235		info->UnloadIfPossible();
236	}
237
238	return NULL;
239}
240
241
242/*
243 * unloads the given catalog (or rather: catalog-chain).
244 * Every single catalog of the chain will be deleted automatically.
245 * Add-ons that have no more current catalogs are unloaded, too.
246 */
247status_t
248MutableLocaleRoster::UnloadCatalog(BCatalogData* catalog)
249{
250	if (!catalog)
251		return B_BAD_VALUE;
252
253	BAutolock lock(fData->fLock);
254	if (!lock.IsLocked())
255		return B_ERROR;
256
257	status_t res = B_ERROR;
258	BCatalogData* nextCatalog;
259
260	while (catalog != NULL) {
261		nextCatalog = catalog->Next();
262		int32 count = fData->fCatalogAddOnInfos.CountItems();
263		for (int32 i = 0; i < count; ++i) {
264			CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>(
265				fData->fCatalogAddOnInfos.ItemAt(i));
266			if (info->fLoadedCatalogs.HasItem(catalog)) {
267				info->fLoadedCatalogs.RemoveItem(catalog);
268				delete catalog;
269				info->UnloadIfPossible();
270				res = B_OK;
271				break;
272			}
273		}
274		catalog = nextCatalog;
275	}
276	return res;
277}
278
279
280}	// namespace BPrivate
281