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
CatKey(const char * str,const char * ctx,const char * cmt)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
CatKey(uint32 id)41 CatKey::CatKey(uint32 id)
42 :
43 fHashVal(id),
44 fFlags(0)
45 {
46 }
47
48
CatKey()49 CatKey::CatKey()
50 :
51 fHashVal(0),
52 fFlags(0)
53 {
54 }
55
56
57 bool
operator ==(const CatKey & right) const58 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
operator !=(const CatKey & right) const70 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
GetStringParts(BString * str,BString * ctx,BString * cmt) const82 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
HashFun(const char * s,int startValue)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
MakeEmpty()109 HashMapCatalog::MakeEmpty()
110 {
111 fCatMap.Clear();
112 }
113
114
115 int32
CountItems() const116 HashMapCatalog::CountItems() const
117 {
118 return fCatMap.Size();
119 }
120
121
122 const char *
GetString(const char * string,const char * context,const char * comment)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 *
GetString(uint32 id)132 HashMapCatalog::GetString(uint32 id)
133 {
134 CatKey key(id);
135 return GetString(key);
136 }
137
138
139 const char *
GetString(const CatKey & key)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
parseQuotedChars(BString & stringToParse)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
SetString(const char * string,const char * translated,const char * context,const char * comment)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
SetString(int32 id,const char * translated)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
SetString(const CatKey & key,const char * translated)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
ComputeFingerprint() const271 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
UpdateFingerprint()288 HashMapCatalog::UpdateFingerprint()
289 {
290 fFingerprint = ComputeFingerprint();
291 }
292
293
294 } // namespace BPrivate
295