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