| // Copyright 2010-2014, 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 |