| Index: chrome/browser/extensions/extension_message_bundle.cc
|
| ===================================================================
|
| --- chrome/browser/extensions/extension_message_bundle.cc (revision 0)
|
| +++ chrome/browser/extensions/extension_message_bundle.cc (revision 0)
|
| @@ -0,0 +1,256 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/extensions/extension_message_bundle.h"
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/hash_tables.h"
|
| +#include "base/scoped_ptr.h"
|
| +#include "base/string_util.h"
|
| +#include "base/values.h"
|
| +
|
| +const wchar_t* ExtensionMessageBundle::kContentKey = L"content";
|
| +const wchar_t* ExtensionMessageBundle::kMessageKey = L"message";
|
| +const wchar_t* ExtensionMessageBundle::kPlaceholdersKey = L"placeholders";
|
| +
|
| +const char* ExtensionMessageBundle::kPlaceholderBegin = "$";
|
| +const char* ExtensionMessageBundle::kPlaceholderEnd = "$";
|
| +const char* ExtensionMessageBundle::kMessageBegin = "__MSG_";
|
| +const char* ExtensionMessageBundle::kMessageEnd = "__";
|
| +
|
| +const char* ExtensionMessageBundle::kExtensionName = "chrome_extension_name";
|
| +const char* ExtensionMessageBundle::kExtensionDescription =
|
| + "chrome_extension_description";
|
| +
|
| +// Formats message in case we encounter a bad formed key in the JSON object.
|
| +// Returns false and sets |error| to actual error message.
|
| +static bool BadKeyMessage(const std::string& name, std::string* error) {
|
| + *error = StringPrintf("Name of a key \"%s\" is invalid. Only ASCII [a-z], "
|
| + "[A-Z], [0-9] and \"_\" are allowed.", name.c_str());
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| +ExtensionMessageBundle* ExtensionMessageBundle::Create(
|
| + const DictionaryValue& default_locale_catalog,
|
| + const DictionaryValue& current_locale_catalog,
|
| + std::string* error) {
|
| + scoped_ptr<ExtensionMessageBundle> message_bundle(
|
| + new ExtensionMessageBundle);
|
| + if (!message_bundle->Init(default_locale_catalog,
|
| + current_locale_catalog,
|
| + error))
|
| + return NULL;
|
| +
|
| + return message_bundle.release();
|
| +}
|
| +
|
| +bool ExtensionMessageBundle::Init(const DictionaryValue& default_locale_catalog,
|
| + const DictionaryValue& current_locale_catalog,
|
| + std::string* error) {
|
| + dictionary_.clear();
|
| +
|
| + // Create a single dictionary out of default and current_locale catalogs.
|
| + // If message is missing from current_locale catalog, we take one from default
|
| + // catalog.
|
| + DictionaryValue::key_iterator key_it = current_locale_catalog.begin_keys();
|
| + for (; key_it != current_locale_catalog.end_keys(); ++key_it) {
|
| + std::string key(StringToLowerASCII(WideToUTF8(*key_it)));
|
| + if (!IsValidName(*key_it))
|
| + return BadKeyMessage(key, error);
|
| + std::string value;
|
| + if (!GetMessageValue(*key_it, current_locale_catalog, &value, error))
|
| + return false;
|
| + // Keys are not case-sensitive.
|
| + dictionary_[key] = value;
|
| + }
|
| +
|
| + key_it = default_locale_catalog.begin_keys();
|
| + for (; key_it != default_locale_catalog.end_keys(); ++key_it) {
|
| + std::string key(StringToLowerASCII(WideToUTF8(*key_it)));
|
| + if (!IsValidName(*key_it))
|
| + return BadKeyMessage(key, error);
|
| + // Add only messages that are not provided by app_catalog.
|
| + if (dictionary_.find(key) != dictionary_.end())
|
| + continue;
|
| + std::string value;
|
| + if (!GetMessageValue(*key_it, default_locale_catalog, &value, error))
|
| + return false;
|
| + // Keys are not case-sensitive.
|
| + dictionary_[key] = value;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ExtensionMessageBundle::GetMessageValue(const std::wstring& wkey,
|
| + const DictionaryValue& catalog,
|
| + std::string* value,
|
| + std::string* error) const {
|
| + std::string key(WideToUTF8(wkey));
|
| + // Get the top level tree for given key (name part).
|
| + DictionaryValue* name_tree;
|
| + if (!catalog.GetDictionary(wkey, &name_tree)) {
|
| + *error = StringPrintf("Not a valid tree for key %s.", key.c_str());
|
| + return false;
|
| + }
|
| + // Extract message from it.
|
| + if (!name_tree->GetString(kMessageKey, value)) {
|
| + *error = StringPrintf("There is no \"%s\" element for key %s.",
|
| + WideToUTF8(kMessageKey).c_str(),
|
| + key.c_str());
|
| + return false;
|
| + }
|
| +
|
| + SubstitutionMap placeholders;
|
| + if (!GetPlaceholders(*name_tree, key, &placeholders, error))
|
| + return false;
|
| +
|
| + if (!ReplacePlaceholders(placeholders, value, error))
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +ExtensionMessageBundle::ExtensionMessageBundle() {
|
| +}
|
| +
|
| +bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree,
|
| + const std::string& name_key,
|
| + SubstitutionMap* placeholders,
|
| + std::string* error) const {
|
| + if (!name_tree.HasKey(kPlaceholdersKey))
|
| + return true;
|
| +
|
| + DictionaryValue* placeholders_tree;
|
| + if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) {
|
| + *error = StringPrintf("Not a valid \"%s\" element for key %s.",
|
| + WideToUTF8(kPlaceholdersKey).c_str(),
|
| + name_key.c_str());
|
| + return false;
|
| + }
|
| +
|
| + for (DictionaryValue::key_iterator key_it = placeholders_tree->begin_keys();
|
| + key_it != placeholders_tree->end_keys();
|
| + ++key_it) {
|
| + DictionaryValue* placeholder;
|
| + std::string content_key = WideToUTF8(*key_it);
|
| + if (!IsValidName(*key_it))
|
| + return BadKeyMessage(content_key, error);
|
| + if (!placeholders_tree->GetDictionary(*key_it, &placeholder)) {
|
| + *error = StringPrintf("Invalid placeholder %s for key %s",
|
| + content_key.c_str(),
|
| + name_key.c_str());
|
| + return false;
|
| + }
|
| + std::string content;
|
| + if (!placeholder->GetString(kContentKey, &content)) {
|
| + *error = StringPrintf("Invalid \"%s\" element for key %s.",
|
| + WideToUTF8(kContentKey).c_str(),
|
| + name_key.c_str());
|
| + return false;
|
| + }
|
| + (*placeholders)[StringToLowerASCII(content_key)] = content;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ExtensionMessageBundle::ReplacePlaceholders(
|
| + const SubstitutionMap& placeholders,
|
| + std::string* message,
|
| + std::string* error) const {
|
| + return ReplaceVariables(placeholders,
|
| + kPlaceholderBegin,
|
| + kPlaceholderEnd,
|
| + message,
|
| + error);
|
| +}
|
| +
|
| +bool ExtensionMessageBundle::ReplaceMessages(std::string* text,
|
| + std::string* error) const {
|
| + return ReplaceVariables(dictionary_, kMessageBegin, kMessageEnd, text, error);
|
| +}
|
| +
|
| +// static
|
| +bool ExtensionMessageBundle::ReplaceVariables(
|
| + const SubstitutionMap& variables,
|
| + const std::string& var_begin_delimiter,
|
| + const std::string& var_end_delimiter,
|
| + std::string* message,
|
| + std::string* error) {
|
| + std::string::size_type beg_index = 0;
|
| + const std::string::size_type var_begin_delimiter_size =
|
| + var_begin_delimiter.size();
|
| + while (true) {
|
| + beg_index = message->find(var_begin_delimiter, beg_index);
|
| + if (beg_index == message->npos)
|
| + return true;
|
| +
|
| + // Advance it immediately to the begining of possible variable name.
|
| + beg_index += var_begin_delimiter_size;
|
| + if (beg_index >= message->size())
|
| + return true;
|
| + std::string::size_type end_index =
|
| + message->find(var_end_delimiter, beg_index);
|
| + if (end_index == message->npos)
|
| + return true;
|
| +
|
| + // Looking for 1 in substring of ...$1$....
|
| + const std::string& var_name =
|
| + message->substr(beg_index, end_index - beg_index);
|
| + if (!IsValidName(var_name))
|
| + continue;
|
| + SubstitutionMap::const_iterator it =
|
| + variables.find(StringToLowerASCII(var_name));
|
| + if (it == variables.end()) {
|
| + *error = StringPrintf("Variable %s%s%s used but not defined.",
|
| + var_begin_delimiter.c_str(),
|
| + var_name.c_str(),
|
| + var_end_delimiter.c_str());
|
| + return false;
|
| + }
|
| +
|
| + // Replace variable with its value.
|
| + std::string value = it->second;
|
| + message->replace(beg_index - var_begin_delimiter_size,
|
| + end_index - beg_index + var_begin_delimiter_size +
|
| + var_end_delimiter.size(),
|
| + value);
|
| +
|
| + // And position pointer to after the replacement.
|
| + beg_index += value.size() - var_begin_delimiter_size;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +template <typename str>
|
| +bool ExtensionMessageBundle::IsValidName(const str& name) {
|
| + if (name.empty())
|
| + return false;
|
| +
|
| + for (str::const_iterator it = name.begin(); it != name.end(); ++it) {
|
| + // Allow only ascii 0-9, a-z, A-Z, and _ in the name.
|
| + if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '_')
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Dictionary interface.
|
| +
|
| +std::string ExtensionMessageBundle::GetL10nMessage(
|
| + const std::string& name) const {
|
| + SubstitutionMap::const_iterator it =
|
| + dictionary_.find(StringToLowerASCII(name));
|
| + if (it != dictionary_.end()) {
|
| + return it->second;
|
| + }
|
| +
|
| + return "";
|
| +}
|
|
|
| Property changes on: chrome\browser\extensions\extension_message_bundle.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|