xref: /haiku/src/kits/locale/HashMapCatalog.cpp (revision 60a6f1d5d7a8715cd3897dd0b626f2e4a64984a8)
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 
14 namespace 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 
28 CatKey::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 
41 CatKey::CatKey(uint32 id)
42 	:
43 	fHashVal(id),
44 	fFlags(0)
45 {
46 }
47 
48 
49 CatKey::CatKey()
50 	:
51 	fHashVal(0),
52 	fFlags(0)
53 {
54 }
55 
56 
57 bool
58 CatKey::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 
69 bool
70 CatKey::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 
81 status_t
82 CatKey::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 
92 uint32
93 CatKey::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 
108 void
109 HashMapCatalog::MakeEmpty()
110 {
111 	fCatMap.Clear();
112 }
113 
114 
115 int32
116 HashMapCatalog::CountItems() const
117 {
118 	return fCatMap.Size();
119 }
120 
121 
122 const char *
123 HashMapCatalog::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 
131 const char *
132 HashMapCatalog::GetString(uint32 id)
133 {
134 	CatKey key(id);
135 	return GetString(key);
136 }
137 
138 
139 const char *
140 HashMapCatalog::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 
150 static status_t
151 parseQuotedChars(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 
214 status_t
215 HashMapCatalog::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 
237 status_t
238 HashMapCatalog::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 
250 status_t
251 HashMapCatalog::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  */
270 uint32
271 HashMapCatalog::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 
287 void
288 HashMapCatalog::UpdateFingerprint()
289 {
290 	fFingerprint = ComputeFingerprint();
291 }
292 
293 
294 }	// namespace BPrivate
295