xref: /haiku/src/kits/locale/MutableLocaleRoster.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
27 namespace BPrivate {
28 
29 
30 namespace {
31 
32 
33 static MutableLocaleRoster* sLocaleRoster;
34 
35 static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT;
36 
37 
38 }	// anonymous namespace
39 
40 
41 static void
42 InitializeLocaleRoster()
43 {
44 	sLocaleRoster = new (std::nothrow) MutableLocaleRoster();
45 }
46 
47 
48 MutableLocaleRoster::MutableLocaleRoster()
49 {
50 }
51 
52 
53 MutableLocaleRoster::~MutableLocaleRoster()
54 {
55 }
56 
57 
58 /*static*/ MutableLocaleRoster*
59 MutableLocaleRoster::Default()
60 {
61 	if (sLocaleRoster == NULL)
62 		pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster);
63 
64 	return sLocaleRoster;
65 }
66 
67 
68 status_t
69 MutableLocaleRoster::SetDefaultFormattingConventions(
70 	const BFormattingConventions& newFormattingConventions)
71 {
72 	return fData->SetDefaultFormattingConventions(newFormattingConventions);
73 }
74 
75 
76 status_t
77 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone)
78 {
79 	return fData->SetDefaultTimeZone(newZone);
80 }
81 
82 
83 status_t
84 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages)
85 {
86 	return fData->SetPreferredLanguages(languages);
87 }
88 
89 
90 status_t
91 MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred)
92 {
93 	return fData->SetFilesystemTranslationPreferred(preferred);
94 }
95 
96 
97 status_t
98 MutableLocaleRoster::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  */
137 BCatalogData*
138 MutableLocaleRoster::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  */
178 BCatalogData*
179 MutableLocaleRoster::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  */
247 status_t
248 MutableLocaleRoster::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