blob: a0886200cb4e3e5b0a4ad28eef7f8ace285e6058 [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.
#include "dictionary/user_dictionary_session_handler.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/port.h"
#include "base/protobuf/protobuf.h"
#include "base/protobuf/repeated_field.h"
#include "base/scoped_ptr.h"
#include "base/util.h"
#include "dictionary/user_dictionary_session.h"
#include "dictionary/user_dictionary_storage.pb.h"
#include "dictionary/user_dictionary_util.h"
namespace mozc {
namespace user_dictionary {
UserDictionarySessionHandler::UserDictionarySessionHandler()
: session_id_(kInvalidSessionId),
dictionary_path_(UserDictionaryUtil::GetUserDictionaryFileName()) {
}
UserDictionarySessionHandler::~UserDictionarySessionHandler() {
}
bool UserDictionarySessionHandler::Evaluate(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
if (!command.has_type()) {
return false;
}
switch (command.type()) {
case UserDictionaryCommand::NO_OPERATION:
NoOperation(command, status);
break;
case UserDictionaryCommand::CREATE_SESSION:
CreateSession(command, status);
break;
case UserDictionaryCommand::DELETE_SESSION:
DeleteSession(command, status);
break;
case UserDictionaryCommand::SET_DEFAULT_DICTIONARY_NAME:
SetDefaultDictionaryName(command, status);
break;
case UserDictionaryCommand::CHECK_UNDOABILITY:
CheckUndoability(command, status);
break;
case UserDictionaryCommand::UNDO:
Undo(command, status);
break;
case UserDictionaryCommand::LOAD:
Load(command, status);
break;
case UserDictionaryCommand::SAVE:
Save(command, status);
break;
case UserDictionaryCommand::CLEAR_STORAGE:
ClearStorage(command, status);
break;
case UserDictionaryCommand::GET_USER_DICTIONARY_NAME_LIST:
GetUserDictionaryNameList(command, status);
break;
case UserDictionaryCommand::GET_ENTRY_SIZE:
GetEntrySize(command, status);
break;
case UserDictionaryCommand::GET_ENTRIES:
GetEntries(command, status);
break;
case UserDictionaryCommand::CHECK_NEW_DICTIONARY_AVAILABILITY:
CheckNewDictionaryAvailability(command, status);
break;
case UserDictionaryCommand::CREATE_DICTIONARY:
CreateDictionary(command, status);
break;
case UserDictionaryCommand::DELETE_DICTIONARY:
DeleteDictionary(command, status);
break;
case UserDictionaryCommand::RENAME_DICTIONARY:
RenameDictionary(command, status);
break;
case UserDictionaryCommand::CHECK_NEW_ENTRY_AVAILABILITY:
CheckNewEntryAvailability(command, status);
break;
case UserDictionaryCommand::ADD_ENTRY:
AddEntry(command, status);
break;
case UserDictionaryCommand::EDIT_ENTRY:
EditEntry(command, status);
break;
case UserDictionaryCommand::DELETE_ENTRY:
DeleteEntry(command, status);
break;
case UserDictionaryCommand::IMPORT_DATA:
ImportData(command, status);
break;
case UserDictionaryCommand::GET_STORAGE:
GetStorage(command, status);
break;
default:
status->set_status(UserDictionaryCommandStatus::UNKNOWN_COMMAND);
break;
}
return true;
}
void UserDictionarySessionHandler::NoOperation(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::ClearStorage(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
#ifdef __native_client__
// File operation is not supported on NaCl.
status->set_status(UserDictionaryCommandStatus::UNKNOWN_ERROR);
#else
FileUtil::Unlink(dictionary_path_);
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
#endif
}
void UserDictionarySessionHandler::CreateSession(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
uint64 new_id = CreateNewSessionId();
session_id_ = new_id;
session_.reset(new UserDictionarySession(dictionary_path_));
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
// Returns the created session's id.
status->set_session_id(new_id);
}
void UserDictionarySessionHandler::DeleteSession(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
session_id_ = kInvalidSessionId;
session_.reset();
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::SetDefaultDictionaryName(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_name()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
status->set_status(
session->SetDefaultDictionaryName(command.dictionary_name()));
}
void UserDictionarySessionHandler::CheckUndoability(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
status->set_status(
session->has_undo_history() ?
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS :
UserDictionaryCommandStatus::NO_UNDO_HISTORY);
}
void UserDictionarySessionHandler::Undo(const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
status->set_status(session->Undo());
}
void UserDictionarySessionHandler::Load(const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (command.ensure_non_empty_storage()) {
status->set_status(session->LoadWithEnsuringNonEmptyStorage());
} else {
status->set_status(session->Load());
}
}
void UserDictionarySessionHandler::Save(const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
status->set_status(session->Save());
}
void UserDictionarySessionHandler::GetUserDictionaryNameList(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
const UserDictionaryStorage &storage = session->storage();
UserDictionaryStorage *result_storage = status->mutable_storage();
for (int i = 0; i < storage.dictionaries_size(); ++i) {
const UserDictionary &dictionary = storage.dictionaries(i);
UserDictionary *result_dictionary = result_storage->add_dictionaries();
if (dictionary.has_id()) {
result_dictionary->set_id(dictionary.id());
}
if (dictionary.has_name()) {
result_dictionary->set_name(dictionary.name());
}
}
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::GetEntrySize(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
const UserDictionary *dictionary = UserDictionaryUtil::GetUserDictionaryById(
session_->storage(), command.dictionary_id());
if (dictionary == NULL) {
status->set_status(UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID);
return;
}
status->set_entry_size(dictionary->entries_size());
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::GetEntries(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id() || command.entry_index_size() <= 0) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
const UserDictionary *dictionary = UserDictionaryUtil::GetUserDictionaryById(
session_->storage(), command.dictionary_id());
if (dictionary == NULL) {
status->set_status(UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID);
return;
}
const int entry_index_size = command.entry_index_size();
for (int i = 0; i < entry_index_size; ++i) {
const int entry_index = command.entry_index(i);
if (entry_index < 0 || dictionary->entries_size() <= entry_index) {
status->set_status(UserDictionaryCommandStatus::ENTRY_INDEX_OUT_OF_RANGE);
return;
}
}
mozc::protobuf::RepeatedPtrField<UserDictionary::Entry> *mutable_entries =
status->mutable_entries();
mutable_entries->Clear();
mutable_entries->Reserve(entry_index_size);
for (int i = 0; i < entry_index_size; ++i) {
const int entry_index = command.entry_index(i);
mutable_entries->Add()->CopyFrom(dictionary->entries(entry_index));
}
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::CheckNewDictionaryAvailability(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (UserDictionaryUtil::IsStorageFull(session->storage())) {
status->set_status(
UserDictionaryCommandStatus::DICTIONARY_SIZE_LIMIT_EXCEEDED);
return;
}
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::CreateDictionary(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_name()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
uint64 new_dictionary_id;
status->set_status(session->CreateDictionary(
command.dictionary_name(), &new_dictionary_id));
if (status->status() ==
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
status->set_dictionary_id(new_dictionary_id);
}
}
void UserDictionarySessionHandler::DeleteDictionary(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
if (command.ensure_non_empty_storage()) {
status->set_status(session->DeleteDictionaryWithEnsuringNonEmptyStorage(
command.dictionary_id()));
} else {
status->set_status(session->DeleteDictionary(command.dictionary_id()));
}
}
void UserDictionarySessionHandler::RenameDictionary(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id() || !command.has_dictionary_name()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
status->set_status(session->RenameDictionary(
command.dictionary_id(), command.dictionary_name()));
}
void UserDictionarySessionHandler::CheckNewEntryAvailability(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
const UserDictionary *dictionary = UserDictionaryUtil::GetUserDictionaryById(
session->storage(), command.dictionary_id());
if (dictionary == NULL) {
status->set_status(UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID);
return;
}
if (UserDictionaryUtil::IsDictionaryFull(*dictionary)) {
status->set_status(
UserDictionaryCommandStatus::ENTRY_SIZE_LIMIT_EXCEEDED);
return;
}
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
void UserDictionarySessionHandler::AddEntry(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id() || !command.has_entry()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
status->set_status(
session->AddEntry(command.dictionary_id(), command.entry()));
}
void UserDictionarySessionHandler::EditEntry(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id() ||
command.entry_index_size() != 1 ||
!command.has_entry()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
status->set_status(session->EditEntry(
command.dictionary_id(), command.entry_index(0), command.entry()));
}
void UserDictionarySessionHandler::DeleteEntry(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!command.has_dictionary_id() || command.entry_index_size() == 0) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
const vector<int> index_list(command.entry_index().begin(),
command.entry_index().end());
status->set_status(
session->DeleteEntry(command.dictionary_id(), index_list));
}
void UserDictionarySessionHandler::ImportData(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
if (!(command.has_dictionary_id() || command.has_dictionary_name()) ||
!command.has_data()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return;
}
uint64 dictionary_id = 0;
UserDictionaryCommandStatus::Status result_status;
if (command.has_dictionary_id()) {
result_status =
session->ImportFromString(command.dictionary_id(), command.data());
if (result_status != UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID) {
dictionary_id = command.dictionary_id();
}
} else {
result_status =
session->ImportToNewDictionaryFromString(
command.dictionary_name(), command.data(), &dictionary_id);
}
if (dictionary_id != 0) {
status->set_dictionary_id(dictionary_id);
}
status->set_status(result_status);
}
void UserDictionarySessionHandler::GetStorage(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
UserDictionarySession *session = GetSession(command, status);
if (session == NULL) {
return;
}
status->mutable_storage()->CopyFrom(session->storage());
status->set_status(
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
}
UserDictionarySession *UserDictionarySessionHandler::GetSession(
const UserDictionaryCommand &command,
UserDictionaryCommandStatus *status) {
if (!command.has_session_id()) {
status->set_status(UserDictionaryCommandStatus::INVALID_ARGUMENT);
return NULL;
}
if (session_.get() == NULL || session_id_ != command.session_id()) {
status->set_status(UserDictionaryCommandStatus::UNKNOWN_SESSION_ID);
return NULL;
}
return session_.get();
}
uint64 UserDictionarySessionHandler::CreateNewSessionId() const {
uint64 id = kInvalidSessionId;
while (true) {
Util::GetRandomSequence(reinterpret_cast<char *>(&id), sizeof(id));
if (id != kInvalidSessionId &&
(session_.get() == NULL || session_id_ != id)) {
// New id is generated.
break;
}
LOG(WARNING)
<< "User Dictionary Session Id " << id << " is already used. Retry.";
}
DCHECK_NE(0, id);
return id;
}
} // namespace user_dictionary
} // namespace mozc