blob: 65c87ec0360e5393463800827c168ba707a8c183 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Handler of mozc configuration.
#include "config/config_handler.h"
#include <algorithm>
#include "base/config_file_stream.h"
#include "base/logging.h"
#include "base/number_util.h"
#include "base/port.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/system_util.h"
#include "base/util.h"
#include "base/version.h"
#include "config/config.pb.h"
namespace mozc {
namespace config {
namespace {
const char kFileNamePrefix[] = "user://config";
void AddCharacterFormRule(const char *group,
const Config::CharacterForm preedit_form,
const Config::CharacterForm conversion_form,
Config *config) {
Config::CharacterFormRule *rule =
config->add_character_form_rules();
rule->set_group(group);
rule->set_preedit_character_form(preedit_form);
rule->set_conversion_character_form(conversion_form);
}
bool GetPlatformSpecificDefaultEmojiSetting() {
// Disable Unicode emoji conversion by default on specific platforms.
bool use_emoji_conversion_default = true;
#if defined(OS_WIN)
if (!SystemUtil::IsWindows8OrLater()) {
use_emoji_conversion_default = false;
}
#elif defined(OS_ANDROID)
use_emoji_conversion_default = false;
#endif
return use_emoji_conversion_default;
}
class ConfigHandlerImpl {
public:
ConfigHandlerImpl() {
// <user_profile>/config1.db
filename_ = kFileNamePrefix;
filename_ += NumberUtil::SimpleItoa(CONFIG_VERSION);
filename_ += ".db";
Reload();
}
virtual ~ConfigHandlerImpl() {}
const Config &GetConfig() const;
bool GetConfig(Config *config) const;
const Config &GetStoredConfig() const;
bool GetStoredConfig(Config *config) const;
bool SetConfig(const Config &config);
void SetImposedConfig(const Config &config);
bool Reload();
void SetConfigFileName(const string &filename);
string GetConfigFileName();
private:
// copy config to config_ and do some
// platform dependent hooks/rewrites
bool SetConfigInternal(const Config &config);
void UpdateMergedConfig();
string filename_;
Config stored_config_;
Config imposed_config_;
// equals to config_.MergeFrom(imposed_config_)
Config merged_config_;
};
ConfigHandlerImpl *GetConfigHandlerImpl() {
return Singleton<ConfigHandlerImpl>::get();
}
const Config &ConfigHandlerImpl::GetConfig() const {
return merged_config_;
}
// return current Config
bool ConfigHandlerImpl::GetConfig(Config *config) const {
config->CopyFrom(merged_config_);
return true;
}
const Config &ConfigHandlerImpl::GetStoredConfig() const {
return stored_config_;
}
// return stored Config
bool ConfigHandlerImpl::GetStoredConfig(Config *config) const {
config->CopyFrom(stored_config_);
return true;
}
// set config and rewirte internal data
bool ConfigHandlerImpl::SetConfigInternal(const Config &config) {
stored_config_.CopyFrom(config);
#ifdef NO_LOGGING
// Delete the optional field from the config.
stored_config_.clear_verbose_level();
// Fall back if the default value is not the expected value.
if (stored_config_.verbose_level() != 0) {
stored_config_.set_verbose_level(0);
}
#endif
Logging::SetConfigVerboseLevel(stored_config_.verbose_level());
// Initialize platform specific configuration.
if (stored_config_.session_keymap() == Config::NONE) {
stored_config_.set_session_keymap(ConfigHandler::GetDefaultKeyMap());
}
#if defined(OS_ANDROID) && defined(CHANNEL_DEV)
stored_config_.mutable_general_config()->set_upload_usage_stats(true);
#endif // CHANNEL_DEV && OS_ANDROID
if (GetPlatformSpecificDefaultEmojiSetting() &&
!stored_config_.has_use_emoji_conversion()) {
stored_config_.set_use_emoji_conversion(true);
}
UpdateMergedConfig();
return true;
}
void ConfigHandlerImpl::UpdateMergedConfig() {
merged_config_.CopyFrom(stored_config_);
merged_config_.MergeFrom(imposed_config_);
}
bool ConfigHandlerImpl::SetConfig(const Config &config) {
Config output_config;
output_config.CopyFrom(config);
ConfigHandler::SetMetaData(&output_config);
VLOG(1) << "Setting new config: " << filename_;
ConfigFileStream::AtomicUpdate(filename_, output_config.SerializeAsString());
#ifdef DEBUG
string debug_content(
"# This is a text-based config file for debugging.\n"
"# Nothing happens when you edit this file manually.\n");
debug_content += output_config.DebugString();
ConfigFileStream::AtomicUpdate(filename_ + ".txt", debug_content);
#endif // DEBUG
return SetConfigInternal(output_config);
}
void ConfigHandlerImpl::SetImposedConfig(const Config &config) {
VLOG(1) << "Setting new overriding config";
imposed_config_.CopyFrom(config);
#ifdef DEBUG
string debug_content(
"# This is a text-based config file for debugging.\n"
"# Nothing happens when you edit this file manually.\n");
debug_content += config.DebugString();
ConfigFileStream::AtomicUpdate(filename_ + ".overriding.txt", debug_content);
#endif // DEBUG
UpdateMergedConfig();
}
// Reload from file
bool ConfigHandlerImpl::Reload() {
VLOG(1) << "Reloading config file: " << filename_;
scoped_ptr<istream> is(ConfigFileStream::OpenReadBinary(filename_));
Config input_proto;
bool ret_code = true;
if (is.get() == NULL) {
LOG(ERROR) << filename_ << " is not found";
ret_code = false;
} else if (!input_proto.ParseFromIstream(is.get())) {
LOG(ERROR) << filename_ << " is broken";
input_proto.Clear(); // revert to default setting
ret_code = false;
}
// we set default config when file is broekn
ret_code |= SetConfigInternal(input_proto);
return ret_code;
}
void ConfigHandlerImpl::SetConfigFileName(const string &filename) {
VLOG(1) << "set new config file name: " << filename;
filename_ = filename;
Reload();
}
string ConfigHandlerImpl::GetConfigFileName() {
#ifdef __native_client__
// Copies filename_ string here to prevent Copy-On-Write issues in
// multi-thread environment.
// See: http://stackoverflow.com/questions/1661154/c-stdstring-in-a-multi-threaded-program/
// TODO(hsumita): Remove this hack if not necessary.
return string(filename_.data(), filename_.size());
#else
return filename_;
#endif // __native_client__
}
} // namespace
const Config &ConfigHandler::GetConfig() {
return GetConfigHandlerImpl()->GetConfig();
}
// Returns current Config
bool ConfigHandler::GetConfig(Config *config) {
return GetConfigHandlerImpl()->GetConfig(config);
}
const Config &ConfigHandler::GetStoredConfig() {
return GetConfigHandlerImpl()->GetStoredConfig();
}
// Returns Stored Config
bool ConfigHandler::GetStoredConfig(Config *config) {
return GetConfigHandlerImpl()->GetStoredConfig(config);
}
bool ConfigHandler::SetConfig(const Config &config) {
return GetConfigHandlerImpl()->SetConfig(config);
}
// Sets overriding config
void ConfigHandler::SetImposedConfig(const Config &config) {
GetConfigHandlerImpl()->SetImposedConfig(config);
}
void ConfigHandler::GetDefaultConfig(Config *config) {
config->Clear();
config->set_session_keymap(ConfigHandler::GetDefaultKeyMap());
const Config::CharacterForm kFullWidth = Config::FULL_WIDTH;
const Config::CharacterForm kLastForm = Config::LAST_FORM;
// "ア"
AddCharacterFormRule("\xE3\x82\xA2", kFullWidth, kFullWidth, config);
AddCharacterFormRule("A", kFullWidth, kLastForm, config);
AddCharacterFormRule("0", kFullWidth, kLastForm, config);
AddCharacterFormRule("(){}[]", kFullWidth, kLastForm, config);
AddCharacterFormRule(".,", kFullWidth, kLastForm, config);
// "。、",
AddCharacterFormRule("\xE3\x80\x82\xE3\x80\x81", kFullWidth, kFullWidth,
config);
// "・「」"
AddCharacterFormRule("\xE3\x83\xBB\xE3\x80\x8C\xE3\x80\x8D",
kFullWidth, kFullWidth, config);
AddCharacterFormRule("\"'", kFullWidth, kLastForm, config);
AddCharacterFormRule(":;", kFullWidth, kLastForm, config);
AddCharacterFormRule("#%&@$^_|`\\", kFullWidth, kLastForm, config);
AddCharacterFormRule("~", kFullWidth, kLastForm, config);
AddCharacterFormRule("<>=+-/*", kFullWidth, kLastForm, config);
AddCharacterFormRule("?!", kFullWidth, kLastForm, config);
#if defined(OS_ANDROID) && defined(CHANNEL_DEV)
config->mutable_general_config()->set_upload_usage_stats(true);
#endif // OS_ANDROID && CHANNEL_DEV
if (GetPlatformSpecificDefaultEmojiSetting()) {
config->set_use_emoji_conversion(true);
}
}
// Reload from file
bool ConfigHandler::Reload() {
return GetConfigHandlerImpl()->Reload();
}
void ConfigHandler::SetConfigFileName(const string &filename) {
GetConfigHandlerImpl()->SetConfigFileName(filename);
}
string ConfigHandler::GetConfigFileName() {
return GetConfigHandlerImpl()->GetConfigFileName();
}
// static
void ConfigHandler::SetMetaData(Config *config) {
GeneralConfig *general_config = config->mutable_general_config();
general_config->set_config_version(CONFIG_VERSION);
general_config->set_last_modified_time(Util::GetTime());
general_config->set_last_modified_product_version(Version::GetMozcVersion());
general_config->set_platform(SystemUtil::GetOSVersionString());
}
Config::SessionKeymap ConfigHandler::GetDefaultKeyMap() {
#if defined(OS_MACOSX)
return config::Config::KOTOERI;
#elif defined(__native_client__) // OS_MACOSX
return config::Config::CHROMEOS;
#else // OS_MACOSX or __native_client__
return config::Config::MSIME;
#endif // OS_MACOSX or __native_client__
}
} // namespace config
} // namespace mozc