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