1/*
2 * Copyright 2009, Adrien Destugues, pulkomandy@gmail.com. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <HashMapCatalog.h>
8
9#include <ByteOrder.h>
10
11#include <stdlib.h>
12
13
14namespace BPrivate {
15
16
17/*
18 * This is the standard implementation of a localization catalog, using a hash
19 * map. This class is abstract, you need to inherit it and provide methodes for
20 * reading and writing the catalog to a file. Classes doing that are
21 * HashMapCatalog and PlainTextCatalog.
22 * If you ever need to create a catalog not built around an hash map, inherit
23 * BCatalogData instead. Note that in this case you will not be able to use our
24 * development tools anymore.
25 */
26
27
28CatKey::CatKey(const char *str, const char *ctx, const char *cmt)
29	:
30	fString(str),
31	fContext(ctx),
32	fComment(cmt),
33	fFlags(0)
34{
35	fHashVal = HashFun(fString.String(),0);
36	fHashVal = HashFun(fContext.String(),fHashVal);
37	fHashVal = HashFun(fComment.String(),fHashVal);
38}
39
40
41CatKey::CatKey(uint32 id)
42	:
43	fHashVal(id),
44	fFlags(0)
45{
46}
47
48
49CatKey::CatKey()
50	:
51	fHashVal(0),
52	fFlags(0)
53{
54}
55
56
57bool
58CatKey::operator== (const CatKey& right) const
59{
60	// Two keys are equal if their hashval and key (string,context,comment)
61	// are equal (testing only the hash would not filter out collisions):
62	return fHashVal == right.fHashVal
63		&& fString == right.fString
64		&& fContext == right.fContext
65		&& fComment == right.fComment;
66}
67
68
69bool
70CatKey::operator!= (const CatKey& right) const
71{
72	// Two keys are equal if their hashval and key (string,context,comment)
73	// are equal (testing only the hash would not filter out collisions):
74	return fHashVal != right.fHashVal
75		|| fString != right.fString
76		|| fContext != right.fContext
77		|| fComment != right.fComment;
78}
79
80
81status_t
82CatKey::GetStringParts(BString* str, BString* ctx, BString* cmt) const
83{
84	if (str) *str = fString;
85	if (ctx) *ctx = fContext;
86	if (cmt) *cmt = fComment;
87
88	return B_OK;
89}
90
91
92uint32
93CatKey::HashFun(const char* s, int startValue) {
94	unsigned long h = startValue;
95	for ( ; *s; ++s)
96		h = 5 * h + *s;
97
98	// Add 1 to differenciate ("ab","cd","ef") from ("abcd","e","f")
99	h = 5 * h + 1;
100
101	return size_t(h);
102}
103
104
105// HashMapCatalog
106
107
108void
109HashMapCatalog::MakeEmpty()
110{
111	fCatMap.Clear();
112}
113
114
115int32
116HashMapCatalog::CountItems() const
117{
118	return fCatMap.Size();
119}
120
121
122const char *
123HashMapCatalog::GetString(const char *string, const char *context,
124	const char *comment)
125{
126	CatKey key(string, context, comment);
127	return GetString(key);
128}
129
130
131const char *
132HashMapCatalog::GetString(uint32 id)
133{
134	CatKey key(id);
135	return GetString(key);
136}
137
138
139const char *
140HashMapCatalog::GetString(const CatKey& key)
141{
142	BString value = fCatMap.Get(key);
143	if (value.Length() == 0)
144		return NULL;
145	else
146		return value.String();
147}
148
149
150static status_t
151parseQuotedChars(BString& stringToParse)
152{
153	char* in = stringToParse.LockBuffer(0);
154	if (in == NULL)
155		return B_ERROR;
156	char* out = in;
157	int newLength = 0;
158	bool quoted = false;
159
160	while (*in != 0) {
161		if (quoted) {
162			if (*in == 'a')
163				*out = '\a';
164			else if (*in == 'b')
165				*out = '\b';
166			else if (*in == 'f')
167				*out = '\f';
168			else if (*in == 'n')
169				*out = '\n';
170			else if (*in == 'r')
171				*out = '\r';
172			else if (*in == 't')
173				*out = '\t';
174			else if (*in == 'v')
175				*out = '\v';
176			else if (*in == '"')
177				*out = '"';
178			else if (*in == 'x') {
179				if (in[1] == '\0' || in[2] == '\0')
180					break;
181				// Parse the 2-digit hex integer that follows
182				char tmp[3];
183				tmp[0] = in[1];
184				tmp[1] = in[2];
185				tmp[2] = '\0';
186				unsigned int hexchar = strtoul(tmp, NULL, 16);
187				*out = hexchar;
188				// skip the number
189				in += 2;
190			} else {
191				// drop quote from unknown quoting-sequence:
192				*out = *in ;
193			}
194			quoted = false;
195			out++;
196			newLength++;
197		} else {
198			quoted = (*in == '\\');
199			if (!quoted) {
200				*out = *in;
201				out++;
202				newLength++;
203			}
204		}
205		in++;
206	}
207	*out = '\0';
208	stringToParse.UnlockBuffer(newLength);
209
210	return B_OK;
211}
212
213
214status_t
215HashMapCatalog::SetString(const char *string, const char *translated,
216	const char *context, const char *comment)
217{
218	BString stringCopy(string);
219	status_t result = parseQuotedChars(stringCopy);
220	if (result != B_OK)
221		return result;
222
223	BString translatedCopy(translated);
224	if ((result = parseQuotedChars(translatedCopy)) != B_OK)
225		return result;
226
227	BString commentCopy(comment);
228	if ((result = parseQuotedChars(commentCopy)) != B_OK)
229		return result;
230
231	CatKey key(stringCopy.String(), context, commentCopy.String());
232	return fCatMap.Put(key, translatedCopy.String());
233		// overwrite existing element
234}
235
236
237status_t
238HashMapCatalog::SetString(int32 id, const char *translated)
239{
240	BString translatedCopy(translated);
241	status_t result = parseQuotedChars(translatedCopy);
242	if (result != B_OK)
243		return result;
244	CatKey key(id);
245	return fCatMap.Put(key, translatedCopy.String());
246		// overwrite existing element
247}
248
249
250status_t
251HashMapCatalog::SetString(const CatKey& key, const char *translated)
252{
253	BString translatedCopy(translated);
254	status_t result = parseQuotedChars(translatedCopy);
255	if (result != B_OK)
256		return result;
257	return fCatMap.Put(key, translatedCopy.String());
258		// overwrite existing element
259}
260
261
262/*
263 * computes a checksum (we call it fingerprint) on all the catalog-keys. We do
264 * not include the values, since we want catalogs for different languages of the
265 * same app to have the same fingerprint, since we use it to separate different
266 * catalog-versions. We use a simple sum because there is no well known
267 * checksum algorithm that gives the same result if the string are sorted in the
268 * wrong order, and this does happen, as an hash map is an unsorted container.
269 */
270uint32
271HashMapCatalog::ComputeFingerprint() const
272{
273	uint32 checksum = 0;
274
275	int32 hash;
276	CatMap::Iterator iter = fCatMap.GetIterator();
277	CatMap::Entry entry;
278	while (iter.HasNext()) {
279		entry = iter.Next();
280		hash = B_HOST_TO_LENDIAN_INT32(entry.key.fHashVal);
281		checksum += hash;
282	}
283	return checksum;
284}
285
286
287void
288HashMapCatalog::UpdateFingerprint()
289{
290	fFingerprint = ComputeFingerprint();
291}
292
293
294}	// namespace BPrivate
295