xref: /haiku/src/kits/locale/HashMapCatalog.cpp (revision 0d452c8f34013b611a54c746a71c05e28796eae2)
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  * BCatalogAddOn 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 // BHashMapCatalog
106 
107 
108 void
109 BHashMapCatalog::MakeEmpty()
110 {
111 	fCatMap.Clear();
112 }
113 
114 
115 int32
116 BHashMapCatalog::CountItems() const
117 {
118 	return fCatMap.Size();
119 }
120 
121 
122 const char *
123 BHashMapCatalog::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 BHashMapCatalog::GetString(uint32 id)
133 {
134 	CatKey key(id);
135 	return GetString(key);
136 }
137 
138 
139 const char *
140 BHashMapCatalog::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 == 'n')
163 				*out = '\n';
164 			else if (*in == 't')
165 				*out = '\t';
166 			else if (*in == '"')
167 				*out = '"';
168 			else if (*in == 'x') {
169 				if (in[1] == '\0' || in[2] == '\0')
170 					break;
171 				// Parse the 2-digit hex integer that follows
172 				char tmp[3];
173 				tmp[0] = in[1];
174 				tmp[1] = in[2];
175 				tmp[2] = '\0';
176 				unsigned int hexchar = strtoul(tmp, NULL, 16);
177 				*out = hexchar;
178 				// skip the number
179 				in += 2;
180 			} else {
181 				// drop quote from unknown quoting-sequence:
182 				*out = *in ;
183 			}
184 			quoted = false;
185 			out++;
186 			newLength++;
187 		} else {
188 			quoted = (*in == '\\');
189 			if (!quoted) {
190 				*out = *in;
191 				out++;
192 				newLength++;
193 			}
194 		}
195 		in++;
196 	}
197 	*out = '\0';
198 	stringToParse.UnlockBuffer(newLength);
199 
200 	return B_OK;
201 }
202 
203 
204 status_t
205 BHashMapCatalog::SetString(const char *string, const char *translated,
206 	const char *context, const char *comment)
207 {
208 	BString stringCopy(string);
209 	status_t result = parseQuotedChars(stringCopy);
210 	if (result != B_OK)
211 		return result;
212 
213 	BString translatedCopy(translated);
214 	if ((result = parseQuotedChars(translatedCopy)) != B_OK)
215 		return result;
216 
217 	BString commentCopy(comment);
218 	if ((result = parseQuotedChars(commentCopy)) != B_OK)
219 		return result;
220 
221 	CatKey key(stringCopy.String(), context, commentCopy.String());
222 	return fCatMap.Put(key, translatedCopy.String());
223 		// overwrite existing element
224 }
225 
226 
227 status_t
228 BHashMapCatalog::SetString(int32 id, const char *translated)
229 {
230 	BString translatedCopy(translated);
231 	status_t result = parseQuotedChars(translatedCopy);
232 	if (result != B_OK)
233 		return result;
234 	CatKey key(id);
235 	return fCatMap.Put(key, translatedCopy.String());
236 		// overwrite existing element
237 }
238 
239 
240 status_t
241 BHashMapCatalog::SetString(const CatKey& key, const char *translated)
242 {
243 	BString translatedCopy(translated);
244 	status_t result = parseQuotedChars(translatedCopy);
245 	if (result != B_OK)
246 		return result;
247 	return fCatMap.Put(key, translatedCopy.String());
248 		// overwrite existing element
249 }
250 
251 
252 /*
253  * computes a checksum (we call it fingerprint) on all the catalog-keys. We do
254  * not include the values, since we want catalogs for different languages of the
255  * same app to have the same fingerprint, since we use it to separate different
256  * catalog-versions. We use a simple sum because there is no well known
257  * checksum algorithm that gives the same result if the string are sorted in the
258  * wrong order, and this does happen, as an hash map is an unsorted container.
259  */
260 uint32
261 BHashMapCatalog::ComputeFingerprint() const
262 {
263 	uint32 checksum = 0;
264 
265 	int32 hash;
266 	CatMap::Iterator iter = fCatMap.GetIterator();
267 	CatMap::Entry entry;
268 	while (iter.HasNext())
269 	{
270 		entry = iter.Next();
271 		hash = B_HOST_TO_LENDIAN_INT32(entry.key.fHashVal);
272 		checksum += hash;
273 	}
274 	return checksum;
275 }
276 
277 
278 void
279 BHashMapCatalog::UpdateFingerprint()
280 {
281 	fFingerprint = ComputeFingerprint();
282 }
283 
284 
285 }	// namespace BPrivate
286