blob: a0752dd80e7eabf43b1033f715e698d25104438c [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.h"
#include <algorithm>
#include "base/logging.h"
#include "base/port.h"
#include "base/protobuf/protobuf.h"
#include "base/protobuf/repeated_field.h"
#include "dictionary/user_dictionary_importer.h"
#include "dictionary/user_dictionary_storage.h"
#include "dictionary/user_dictionary_storage.pb.h"
#include "dictionary/user_dictionary_util.h"
namespace mozc {
namespace user_dictionary {
using ::mozc::protobuf::RepeatedPtrField;
class UserDictionarySession::UndoCommand {
public:
virtual ~UndoCommand() {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) = 0;
protected:
UndoCommand() {
}
private:
DISALLOW_COPY_AND_ASSIGN(UndoCommand);
};
namespace {
// Implement "undo commands" for each operations.
class UndoCreateDictionaryCommand : public UserDictionarySession::UndoCommand {
public:
UndoCreateDictionaryCommand() {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
if (storage->dictionaries_size() == 0) {
return false;
}
storage->mutable_dictionaries()->RemoveLast();
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(UndoCreateDictionaryCommand);
};
class UndoDeleteDictionaryCommand : public UserDictionarySession::UndoCommand {
public:
// This instance takes the ownership of the given dictionary.
UndoDeleteDictionaryCommand(int index, UserDictionary *dictionary)
: index_(index), dictionary_(dictionary) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
if (dictionary_.get() == NULL) {
return false;
}
RepeatedPtrField<UserDictionary> *dictionaries =
storage->mutable_dictionaries();
dictionaries->AddAllocated(dictionary_.release());
// Adjust the position of the reverted dictionary.
rotate(dictionaries->pointer_begin() + index_,
dictionaries->pointer_end() - 1,
dictionaries->pointer_end());
return true;
}
private:
int index_;
scoped_ptr<UserDictionary> dictionary_;
DISALLOW_COPY_AND_ASSIGN(UndoDeleteDictionaryCommand);
};
class UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand
: public UserDictionarySession::UndoCommand {
public:
// This instance takes the ownership of the given dictionary.
explicit UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand(
UserDictionary *dictionary)
: dictionary_(dictionary) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
if (storage->dictionaries_size() != 1) {
return false;
}
dictionary_->Swap(storage->mutable_dictionaries(0));
return true;
}
private:
scoped_ptr<UserDictionary> dictionary_;
DISALLOW_COPY_AND_ASSIGN(
UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand);
};
class UndoRenameDictionaryCommand : public UserDictionarySession::UndoCommand {
public:
UndoRenameDictionaryCommand(
uint64 dictionary_id, const string &original_name)
: dictionary_id_(dictionary_id), original_name_(original_name) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage, dictionary_id_);
if (dictionary == NULL) {
return false;
}
dictionary->set_name(original_name_);
return true;
}
private:
uint64 dictionary_id_;
string original_name_;
DISALLOW_COPY_AND_ASSIGN(UndoRenameDictionaryCommand);
};
class UndoAddEntryCommand : public UserDictionarySession::UndoCommand {
public:
explicit UndoAddEntryCommand(uint64 dictionary_id)
: dictionary_id_(dictionary_id) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage, dictionary_id_);
if (dictionary == NULL || dictionary->entries_size() == 0) {
return false;
}
dictionary->mutable_entries()->RemoveLast();
return true;
}
private:
uint64 dictionary_id_;
DISALLOW_COPY_AND_ASSIGN(UndoAddEntryCommand);
};
class UndoEditEntryCommand : public UserDictionarySession::UndoCommand {
public:
UndoEditEntryCommand(uint64 dictionary_id, int index,
const UserDictionary::Entry &original_entry)
: dictionary_id_(dictionary_id), index_(index),
original_entry_(original_entry) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage, dictionary_id_);
if (dictionary == NULL ||
index_ < 0 || dictionary->entries_size() <= index_) {
return false;
}
dictionary->mutable_entries(index_)->CopyFrom(original_entry_);
return true;
}
private:
uint64 dictionary_id_;
int index_;
UserDictionary::Entry original_entry_;
DISALLOW_COPY_AND_ASSIGN(UndoEditEntryCommand);
};
struct DeleteEntryComparator {
bool operator()(const pair<int, UserDictionary::Entry*> &entry1,
const pair<int, UserDictionary::Entry*> &entry2) {
return entry1.first < entry2.first;
}
};
class UndoDeleteEntryCommand : public UserDictionarySession::UndoCommand {
public:
// This instance takes the ownership of the given entries.
UndoDeleteEntryCommand(
uint64 dictionary_id,
const vector<pair<int, UserDictionary::Entry*> > deleted_entries)
: dictionary_id_(dictionary_id), deleted_entries_(deleted_entries) {
sort(deleted_entries_.begin(), deleted_entries_.end(),
DeleteEntryComparator());
}
virtual ~UndoDeleteEntryCommand() {
for (size_t i = 0; i < deleted_entries_.size(); ++i) {
delete deleted_entries_[i].second;
}
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage, dictionary_id_);
if (dictionary == NULL) {
return false;
}
// Check validity of the holding indices.
const int num_merged_entries =
dictionary->entries_size() + deleted_entries_.size();
for (size_t i = 0; i < deleted_entries_.size(); ++i) {
const int index = deleted_entries_[i].first;
if (index < 0 || num_merged_entries <= index) {
return false;
}
}
RepeatedPtrField<UserDictionary::Entry> *entries =
dictionary->mutable_entries();
// Move instances to backup vector.
vector<UserDictionary::Entry*> backup(
entries->pointer_begin(), entries->pointer_end());
while (entries->size() > 0) {
entries->ReleaseLast();
}
// Merge two vectors into entries.
int backup_index = 0;
int deleted_entry_index = 0;
for (int index = 0; deleted_entry_index < deleted_entries_.size();
++index) {
if (index == deleted_entries_[deleted_entry_index].first) {
entries->AddAllocated(deleted_entries_[deleted_entry_index].second);
++deleted_entry_index;
} else {
entries->AddAllocated(backup[backup_index]);
++backup_index;
}
}
// Add remaining entries in backup to the entries.
for (; backup_index < backup.size(); ++backup_index) {
entries->AddAllocated(backup[backup_index]);
}
// Release the entries.
deleted_entries_.clear();
return true;
}
private:
uint64 dictionary_id_;
vector<pair<int, UserDictionary::Entry*> > deleted_entries_;
DISALLOW_COPY_AND_ASSIGN(UndoDeleteEntryCommand);
};
class UndoImportFromStringCommand : public UserDictionarySession::UndoCommand {
public:
UndoImportFromStringCommand(uint64 dictionary_id, int original_num_entries)
: dictionary_id_(dictionary_id),
original_num_entries_(original_num_entries) {
}
virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage, dictionary_id_);
if (dictionary == NULL) {
return false;
}
RepeatedPtrField<UserDictionary::Entry> *entries =
dictionary->mutable_entries();
while (original_num_entries_ < entries->size()) {
entries->RemoveLast();
}
return true;
}
private:
uint64 dictionary_id_;
int original_num_entries_;
DISALLOW_COPY_AND_ASSIGN(UndoImportFromStringCommand);
};
// The limit of the number of commands remembered by the session for undo.
const int kMaxUndoHistory = 30;
// The default name of a dictionary, which is created to ensure "non-empty"
// storage.
const char kDefaultDictionaryName[] = "user dictionary";
} // namespace
UserDictionarySession::UserDictionarySession(const string &filepath)
: storage_(new mozc::UserDictionaryStorage(filepath)),
default_dictionary_name_(kDefaultDictionaryName) {
}
UserDictionarySession::~UserDictionarySession() {
ClearUndoHistory();
}
// TODO(hidehiko) move this to header.
const UserDictionaryStorage &UserDictionarySession::storage() const {
return *storage_;
}
mozc::UserDictionaryStorage *UserDictionarySession::mutable_storage() {
return storage_.get();
}
UserDictionaryCommandStatus::Status
UserDictionarySession::SetDefaultDictionaryName(
const string &dictionary_name) {
// Validate the name for the default dictionary. The name is used to create
// a dictionary "for an empty storage", so check the validity with the
// default instance of UserDictionaryStorage.
UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::ValidateDictionaryName(
UserDictionaryStorage::default_instance(), dictionary_name);
if (status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
default_dictionary_name_ = dictionary_name;
}
return status;
}
UserDictionaryCommandStatus::Status UserDictionarySession::Load() {
return LoadInternal(false);
}
UserDictionaryCommandStatus::Status
UserDictionarySession::LoadWithEnsuringNonEmptyStorage() {
return LoadInternal(true);
}
UserDictionaryCommandStatus::Status UserDictionarySession::LoadInternal(
bool ensure_non_empty_storage) {
UserDictionaryCommandStatus::Status status;
if (storage_->Load()) {
status = UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
} else {
switch (storage_->GetLastError()) {
case mozc::UserDictionaryStorage::FILE_NOT_EXISTS:
status = UserDictionaryCommandStatus::FILE_NOT_FOUND;
break;
case mozc::UserDictionaryStorage::BROKEN_FILE:
status = UserDictionaryCommandStatus::INVALID_FILE_FORMAT;
break;
default:
LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
status = UserDictionaryCommandStatus::UNKNOWN_ERROR;
break;
}
}
if ((ensure_non_empty_storage && EnsureNonEmptyStorage()) ||
status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
// If the storage is updated, clear the undo history.
ClearUndoHistory();
}
return status;
}
namespace {
// Locker of mozc::UserDictionaryStorage in RAII idiom.
class ScopedUserDictionaryLocker {
public:
explicit ScopedUserDictionaryLocker(mozc::UserDictionaryStorage *storage)
: storage_(storage) {
is_locked_ = storage_->Lock();
}
~ScopedUserDictionaryLocker() {
if (is_locked_) {
storage_->UnLock();
}
}
bool is_locked() const { return is_locked_; }
private:
mozc::UserDictionaryStorage *storage_;
bool is_locked_;
DISALLOW_COPY_AND_ASSIGN(ScopedUserDictionaryLocker);
};
} // namespace
UserDictionaryCommandStatus::Status UserDictionarySession::Save() {
ScopedUserDictionaryLocker locker(storage_.get());
if (!locker.is_locked()) {
LOG(ERROR) << "Failed to take a lock.";
return UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
if (!storage_->Save()) {
switch (storage_->GetLastError()) {
case mozc::UserDictionaryStorage::TOO_BIG_FILE_BYTES:
return UserDictionaryCommandStatus::FILE_SIZE_LIMIT_EXCEEDED;
// TODO(hidehiko): Handle SYNC_FAILURE.
default:
LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
return UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
// Should never reach here.
}
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::Undo() {
if (undo_history_.empty()) {
return UserDictionaryCommandStatus::NO_UNDO_HISTORY;
}
scoped_ptr<UndoCommand> undo_command(undo_history_.back());
undo_history_.pop_back();
return undo_command->RunUndo(storage_.get()) ?
UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS :
UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
UserDictionaryCommandStatus::Status UserDictionarySession::CreateDictionary(
const string &dictionary_name, uint64 *new_dictionary_id) {
UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::CreateDictionary(
storage_.get(), dictionary_name, new_dictionary_id);
if (status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
AddUndoCommand(new UndoCreateDictionaryCommand);
}
return status;
}
UserDictionaryCommandStatus::Status UserDictionarySession::DeleteDictionary(
uint64 dictionary_id) {
return DeleteDictionaryInternal(dictionary_id, false);
}
UserDictionaryCommandStatus::Status
UserDictionarySession::DeleteDictionaryWithEnsuringNonEmptyStorage(
uint64 dictionary_id) {
return DeleteDictionaryInternal(dictionary_id, true);
}
UserDictionaryCommandStatus::Status
UserDictionarySession::DeleteDictionaryInternal(
uint64 dictionary_id, bool ensure_non_empty_storage) {
int original_index;
UserDictionary *deleted_dictionary;
if (!UserDictionaryUtil::DeleteDictionary(
storage_.get(), dictionary_id,
&original_index, &deleted_dictionary)) {
// Failed to delete the dictionary.
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
}
if ((ensure_non_empty_storage && EnsureNonEmptyStorage())) {
// The storage was empty.
AddUndoCommand(new UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand(
deleted_dictionary));
} else {
AddUndoCommand(
new UndoDeleteDictionaryCommand(original_index, deleted_dictionary));
}
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::RenameDictionary(
uint64 dictionary_id, const string &dictionary_name) {
string original_name;
const UserDictionary *dictionary =
UserDictionaryUtil::GetUserDictionaryById(*storage_, dictionary_id);
if (dictionary != NULL) {
// Note that if dictionary is null, it means the dictionary_id is invalid
// so following RenameDictionary will fail, and error handling is done
// in the following codes.
original_name = dictionary->name();
}
if (!storage_->RenameDictionary(dictionary_id, dictionary_name)) {
switch (storage_->GetLastError()) {
case mozc::UserDictionaryStorage::EMPTY_DICTIONARY_NAME:
return UserDictionaryCommandStatus::DICTIONARY_NAME_EMPTY;
case mozc::UserDictionaryStorage::TOO_LONG_DICTIONARY_NAME:
return UserDictionaryCommandStatus::DICTIONARY_NAME_TOO_LONG;
case mozc::UserDictionaryStorage::INVALID_CHARACTERS_IN_DICTIONARY_NAME:
return UserDictionaryCommandStatus
::DICTIONARY_NAME_CONTAINS_INVALID_CHARACTER;
case mozc::UserDictionaryStorage::DUPLICATED_DICTIONARY_NAME:
return UserDictionaryCommandStatus::DICTIONARY_NAME_DUPLICATED;
case mozc::UserDictionaryStorage::INVALID_DICTIONARY_ID:
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
default:
LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
return UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
// Should never reach here.
}
AddUndoCommand(
new UndoRenameDictionaryCommand(dictionary_id, original_name));
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::AddEntry(
uint64 dictionary_id, const UserDictionary::Entry &entry) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage_.get(), dictionary_id);
if (dictionary == NULL) {
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
}
if (UserDictionaryUtil::IsDictionaryFull(*dictionary)) {
return UserDictionaryCommandStatus::ENTRY_SIZE_LIMIT_EXCEEDED;
}
const UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::ValidateEntry(entry);
if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
// Invalid entry.
return status;
}
UserDictionary::Entry *new_entry = dictionary->add_entries();
new_entry->CopyFrom(entry);
UserDictionaryUtil::SanitizeEntry(new_entry);
AddUndoCommand(new UndoAddEntryCommand(dictionary_id));
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::EditEntry(
uint64 dictionary_id, int index, const UserDictionary::Entry &entry) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage_.get(), dictionary_id);
if (dictionary == NULL) {
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
}
if (index < 0 || dictionary->entries_size() <= index) {
return UserDictionaryCommandStatus::ENTRY_INDEX_OUT_OF_RANGE;
}
const UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::ValidateEntry(entry);
if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
// Invalid entry.
return status;
}
UserDictionary::Entry *target_entry = dictionary->mutable_entries(index);
AddUndoCommand(
new UndoEditEntryCommand(dictionary_id, index, *target_entry));
target_entry->CopyFrom(entry);
UserDictionaryUtil::SanitizeEntry(target_entry);
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::DeleteEntry(
uint64 dictionary_id, const vector<int> &index_list) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage_.get(), dictionary_id);
if (dictionary == NULL) {
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
}
for (size_t i = 0; i < index_list.size(); ++i) {
const int index = index_list[i];
if (index < 0 || dictionary->entries_size() <= index) {
return UserDictionaryCommandStatus::ENTRY_INDEX_OUT_OF_RANGE;
}
}
vector<pair<int, UserDictionary::Entry*> > deleted_entries;
deleted_entries.reserve(index_list.size());
RepeatedPtrField<UserDictionary::Entry> *entries =
dictionary->mutable_entries();
UserDictionary::Entry **data = entries->mutable_data();
for (size_t i = 0; i < index_list.size(); ++i) {
const int index = index_list[i];
deleted_entries.push_back(make_pair(index, data[index]));
data[index] = NULL;
}
UserDictionary::Entry **tail = remove(
data, data + entries->size(), static_cast<UserDictionary::Entry*>(NULL));
const int remaining_size = tail - data;
while (entries->size() > remaining_size) {
entries->ReleaseLast();
}
AddUndoCommand(new UndoDeleteEntryCommand(dictionary_id, deleted_entries));
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
}
UserDictionaryCommandStatus::Status UserDictionarySession::ImportFromString(
uint64 dictionary_id, const string &data) {
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage_.get(), dictionary_id);
if (dictionary == NULL) {
return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
}
int original_num_entries = dictionary->entries_size();
UserDictionaryCommandStatus::Status status =
ImportFromStringInternal(dictionary, data);
// Remember the command regardless of whether the importing is successfully
// done or not, because ImportFromStringInternal updates the dictionary
// always.
AddUndoCommand(
new UndoImportFromStringCommand(dictionary_id, original_num_entries));
return status;
}
UserDictionaryCommandStatus::Status
UserDictionarySession::ImportFromStringInternal(
UserDictionary* dictionary, const string &data) {
UserDictionaryImporter::ErrorType import_result;
{
UserDictionaryImporter::StringTextLineIterator iter(data);
import_result =
UserDictionaryImporter::ImportFromTextLineIterator(
UserDictionaryImporter::IME_AUTO_DETECT, &iter, dictionary);
}
LOG_IF(WARNING, import_result != UserDictionaryImporter::IMPORT_NO_ERROR)
<< "Import failed: " << import_result;
// Return status code.
switch (import_result) {
case UserDictionaryImporter::IMPORT_NO_ERROR:
// Succeeded.
return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
// Failed on some reasons.
case UserDictionaryImporter::IMPORT_TOO_MANY_WORDS:
return UserDictionaryCommandStatus::IMPORT_TOO_MANY_WORDS;
case UserDictionaryImporter::IMPORT_INVALID_ENTRIES:
return UserDictionaryCommandStatus::IMPORT_INVALID_ENTRIES;
default:
LOG(ERROR) << "Unknown error: " << import_result;
return UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
}
UserDictionaryCommandStatus::Status
UserDictionarySession::ImportToNewDictionaryFromString(
const string &dictionary_name, const string &data,
uint64 *new_dictionary_id) {
UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::CreateDictionary(
storage_.get(), dictionary_name, new_dictionary_id);
if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
return status;
}
// We can use undo command for CreateDictionary here, too.
AddUndoCommand(new UndoCreateDictionaryCommand);
UserDictionary *dictionary =
UserDictionaryUtil::GetMutableUserDictionaryById(
storage_.get(), *new_dictionary_id);
if (dictionary == NULL) {
// The dictionary should be always found.
return UserDictionaryCommandStatus::UNKNOWN_ERROR;
}
return ImportFromStringInternal(dictionary, data);
}
bool UserDictionarySession::EnsureNonEmptyStorage() {
if (storage_->dictionaries_size() > 0) {
// The storage already has at least one dictionary. Do nothing.
return false;
}
// Creates a dictionary with the default name. Should never fail.
uint64 new_dictionary_id;
UserDictionaryCommandStatus::Status status =
UserDictionaryUtil::CreateDictionary(
storage_.get(), default_dictionary_name_, &new_dictionary_id);
CHECK_EQ(
status, UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
return true;
}
void UserDictionarySession::ClearUndoHistory() {
for (deque<UndoCommand *>::iterator iter = undo_history_.begin();
iter != undo_history_.end(); ++iter) {
delete *iter;
}
undo_history_.clear();
}
void UserDictionarySession::AddUndoCommand(UndoCommand *undo_command) {
// To avoid OOM due to huge undo history, we limit the undo-able
// command size by kMaxUndoHistory.
while (undo_history_.size() >= kMaxUndoHistory) {
delete undo_history_.front();
undo_history_.pop_front();
}
undo_history_.push_back(undo_command);
}
} // namespace user_dictionary
} // namespace mozc