/* * Copyright 2009, Adrien Destugues, pulkomandy@gmail.com. All rights reserved. * Distributed under the terms of the MIT License. */ #include #include #include namespace BPrivate { /* * This is the standard implementation of a localization catalog, using a hash * map. This class is abstract, you need to inherit it and provide methodes for * reading and writing the catalog to a file. Classes doing that are * HashMapCatalog and PlainTextCatalog. * If you ever need to create a catalog not built around an hash map, inherit * BCatalogData instead. Note that in this case you will not be able to use our * development tools anymore. */ CatKey::CatKey(const char *str, const char *ctx, const char *cmt) : fString(str), fContext(ctx), fComment(cmt), fFlags(0) { fHashVal = HashFun(fString.String(),0); fHashVal = HashFun(fContext.String(),fHashVal); fHashVal = HashFun(fComment.String(),fHashVal); } CatKey::CatKey(uint32 id) : fHashVal(id), fFlags(0) { } CatKey::CatKey() : fHashVal(0), fFlags(0) { } bool CatKey::operator== (const CatKey& right) const { // Two keys are equal if their hashval and key (string,context,comment) // are equal (testing only the hash would not filter out collisions): return fHashVal == right.fHashVal && fString == right.fString && fContext == right.fContext && fComment == right.fComment; } bool CatKey::operator!= (const CatKey& right) const { // Two keys are equal if their hashval and key (string,context,comment) // are equal (testing only the hash would not filter out collisions): return fHashVal != right.fHashVal || fString != right.fString || fContext != right.fContext || fComment != right.fComment; } status_t CatKey::GetStringParts(BString* str, BString* ctx, BString* cmt) const { if (str) *str = fString; if (ctx) *ctx = fContext; if (cmt) *cmt = fComment; return B_OK; } uint32 CatKey::HashFun(const char* s, int startValue) { unsigned long h = startValue; for ( ; *s; ++s) h = 5 * h + *s; // Add 1 to differenciate ("ab","cd","ef") from ("abcd","e","f") h = 5 * h + 1; return size_t(h); } // HashMapCatalog void HashMapCatalog::MakeEmpty() { fCatMap.Clear(); } int32 HashMapCatalog::CountItems() const { return fCatMap.Size(); } const char * HashMapCatalog::GetString(const char *string, const char *context, const char *comment) { CatKey key(string, context, comment); return GetString(key); } const char * HashMapCatalog::GetString(uint32 id) { CatKey key(id); return GetString(key); } const char * HashMapCatalog::GetString(const CatKey& key) { BString value = fCatMap.Get(key); if (value.Length() == 0) return NULL; else return value.String(); } static status_t parseQuotedChars(BString& stringToParse) { char* in = stringToParse.LockBuffer(0); if (in == NULL) return B_ERROR; char* out = in; int newLength = 0; bool quoted = false; while (*in != 0) { if (quoted) { if (*in == 'a') *out = '\a'; else if (*in == 'b') *out = '\b'; else if (*in == 'f') *out = '\f'; else if (*in == 'n') *out = '\n'; else if (*in == 'r') *out = '\r'; else if (*in == 't') *out = '\t'; else if (*in == 'v') *out = '\v'; else if (*in == '"') *out = '"'; else if (*in == 'x') { if (in[1] == '\0' || in[2] == '\0') break; // Parse the 2-digit hex integer that follows char tmp[3]; tmp[0] = in[1]; tmp[1] = in[2]; tmp[2] = '\0'; unsigned int hexchar = strtoul(tmp, NULL, 16); *out = hexchar; // skip the number in += 2; } else { // drop quote from unknown quoting-sequence: *out = *in ; } quoted = false; out++; newLength++; } else { quoted = (*in == '\\'); if (!quoted) { *out = *in; out++; newLength++; } } in++; } *out = '\0'; stringToParse.UnlockBuffer(newLength); return B_OK; } status_t HashMapCatalog::SetString(const char *string, const char *translated, const char *context, const char *comment) { BString stringCopy(string); status_t result = parseQuotedChars(stringCopy); if (result != B_OK) return result; BString translatedCopy(translated); if ((result = parseQuotedChars(translatedCopy)) != B_OK) return result; BString commentCopy(comment); if ((result = parseQuotedChars(commentCopy)) != B_OK) return result; CatKey key(stringCopy.String(), context, commentCopy.String()); return fCatMap.Put(key, translatedCopy.String()); // overwrite existing element } status_t HashMapCatalog::SetString(int32 id, const char *translated) { BString translatedCopy(translated); status_t result = parseQuotedChars(translatedCopy); if (result != B_OK) return result; CatKey key(id); return fCatMap.Put(key, translatedCopy.String()); // overwrite existing element } status_t HashMapCatalog::SetString(const CatKey& key, const char *translated) { BString translatedCopy(translated); status_t result = parseQuotedChars(translatedCopy); if (result != B_OK) return result; return fCatMap.Put(key, translatedCopy.String()); // overwrite existing element } /* * computes a checksum (we call it fingerprint) on all the catalog-keys. We do * not include the values, since we want catalogs for different languages of the * same app to have the same fingerprint, since we use it to separate different * catalog-versions. We use a simple sum because there is no well known * checksum algorithm that gives the same result if the string are sorted in the * wrong order, and this does happen, as an hash map is an unsorted container. */ uint32 HashMapCatalog::ComputeFingerprint() const { uint32 checksum = 0; int32 hash; CatMap::Iterator iter = fCatMap.GetIterator(); CatMap::Entry entry; while (iter.HasNext()) { entry = iter.Next(); hash = B_HOST_TO_LENDIAN_INT32(entry.key.fHashVal); checksum += hash; } return checksum; } void HashMapCatalog::UpdateFingerprint() { fFingerprint = ComputeFingerprint(); } } // namespace BPrivate