1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "dictionary/user_dictionary_session.h"
31
32 #include <algorithm>
33 #include <memory>
34
35 #include "base/logging.h"
36 #include "base/port.h"
37 #include "base/protobuf/protobuf.h"
38 #include "base/protobuf/repeated_field.h"
39 #include "dictionary/user_dictionary_importer.h"
40 #include "dictionary/user_dictionary_storage.h"
41 #include "dictionary/user_dictionary_util.h"
42 #include "protocol/user_dictionary_storage.pb.h"
43
44 namespace mozc {
45 namespace user_dictionary {
46
47 using ::mozc::protobuf::RepeatedPtrField;
48
49 class UserDictionarySession::UndoCommand {
50 public:
~UndoCommand()51 virtual ~UndoCommand() {
52 }
53 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) = 0;
54
55 protected:
UndoCommand()56 UndoCommand() {
57 }
58
59 private:
60 DISALLOW_COPY_AND_ASSIGN(UndoCommand);
61 };
62
63 namespace {
64 // Implement "undo commands" for each operations.
65
66 class UndoCreateDictionaryCommand : public UserDictionarySession::UndoCommand {
67 public:
UndoCreateDictionaryCommand()68 UndoCreateDictionaryCommand() {
69 }
70
RunUndo(mozc::UserDictionaryStorage * storage)71 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
72 if (storage->user_dictionary_storage_base.dictionaries_size() == 0) {
73 return false;
74 }
75
76 storage->user_dictionary_storage_base.mutable_dictionaries()->RemoveLast();
77 return true;
78 }
79
80 private:
81 DISALLOW_COPY_AND_ASSIGN(UndoCreateDictionaryCommand);
82 };
83
84 class UndoDeleteDictionaryCommand : public UserDictionarySession::UndoCommand {
85 public:
86 // This instance takes the ownership of the given dictionary.
UndoDeleteDictionaryCommand(int index,UserDictionary * dictionary)87 UndoDeleteDictionaryCommand(int index, UserDictionary *dictionary)
88 : index_(index), dictionary_(dictionary) {
89 }
90
RunUndo(mozc::UserDictionaryStorage * storage)91 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
92 if (dictionary_.get() == NULL) {
93 return false;
94 }
95
96 RepeatedPtrField<UserDictionary> *dictionaries =
97 storage->user_dictionary_storage_base.mutable_dictionaries();
98 dictionaries->AddAllocated(dictionary_.release());
99
100 // Adjust the position of the reverted dictionary.
101 std::rotate(dictionaries->pointer_begin() + index_,
102 dictionaries->pointer_end() - 1, dictionaries->pointer_end());
103 return true;
104 }
105
106 private:
107 int index_;
108 std::unique_ptr<UserDictionary> dictionary_;
109
110 DISALLOW_COPY_AND_ASSIGN(UndoDeleteDictionaryCommand);
111 };
112
113 class UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand
114 : public UserDictionarySession::UndoCommand {
115 public:
116 // This instance takes the ownership of the given dictionary.
UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand(UserDictionary * dictionary)117 explicit UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand(
118 UserDictionary *dictionary)
119 : dictionary_(dictionary) {
120 }
121
RunUndo(mozc::UserDictionaryStorage * storage)122 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
123 if (storage->user_dictionary_storage_base.dictionaries_size() != 1) {
124 return false;
125 }
126 dictionary_->Swap(storage->user_dictionary_storage_base.mutable_dictionaries(0));
127 return true;
128 }
129
130 private:
131 std::unique_ptr<UserDictionary> dictionary_;
132
133 DISALLOW_COPY_AND_ASSIGN(
134 UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand);
135 };
136
137 class UndoRenameDictionaryCommand : public UserDictionarySession::UndoCommand {
138 public:
UndoRenameDictionaryCommand(uint64 dictionary_id,const string & original_name)139 UndoRenameDictionaryCommand(
140 uint64 dictionary_id, const string &original_name)
141 : dictionary_id_(dictionary_id), original_name_(original_name) {
142 }
143
RunUndo(mozc::UserDictionaryStorage * storage)144 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
145 UserDictionary *dictionary =
146 UserDictionaryUtil::GetMutableUserDictionaryById(
147 &storage->user_dictionary_storage_base, dictionary_id_);
148 if (dictionary == NULL) {
149 return false;
150 }
151
152 dictionary->set_name(original_name_);
153 return true;
154 }
155
156 private:
157 uint64 dictionary_id_;
158 string original_name_;
159
160 DISALLOW_COPY_AND_ASSIGN(UndoRenameDictionaryCommand);
161 };
162
163 class UndoAddEntryCommand : public UserDictionarySession::UndoCommand {
164 public:
UndoAddEntryCommand(uint64 dictionary_id)165 explicit UndoAddEntryCommand(uint64 dictionary_id)
166 : dictionary_id_(dictionary_id) {
167 }
168
RunUndo(mozc::UserDictionaryStorage * storage)169 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
170 UserDictionary *dictionary =
171 UserDictionaryUtil::GetMutableUserDictionaryById(
172 &storage->user_dictionary_storage_base, dictionary_id_);
173 if (dictionary == NULL || dictionary->entries_size() == 0) {
174 return false;
175 }
176
177 dictionary->mutable_entries()->RemoveLast();
178 return true;
179 }
180
181 private:
182 uint64 dictionary_id_;
183
184 DISALLOW_COPY_AND_ASSIGN(UndoAddEntryCommand);
185 };
186
187 class UndoEditEntryCommand : public UserDictionarySession::UndoCommand {
188 public:
UndoEditEntryCommand(uint64 dictionary_id,int index,const UserDictionary::Entry & original_entry)189 UndoEditEntryCommand(uint64 dictionary_id, int index,
190 const UserDictionary::Entry &original_entry)
191 : dictionary_id_(dictionary_id), index_(index),
192 original_entry_(original_entry) {
193 }
194
RunUndo(mozc::UserDictionaryStorage * storage)195 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
196 UserDictionary *dictionary =
197 UserDictionaryUtil::GetMutableUserDictionaryById(
198 &storage->user_dictionary_storage_base, dictionary_id_);
199 if (dictionary == NULL ||
200 index_ < 0 || dictionary->entries_size() <= index_) {
201 return false;
202 }
203
204 dictionary->mutable_entries(index_)->CopyFrom(original_entry_);
205 return true;
206 }
207
208 private:
209 uint64 dictionary_id_;
210 int index_;
211 UserDictionary::Entry original_entry_;
212
213 DISALLOW_COPY_AND_ASSIGN(UndoEditEntryCommand);
214 };
215
216 struct DeleteEntryComparator {
operator ()mozc::user_dictionary::__anoncb973ba20111::DeleteEntryComparator217 bool operator()(const std::pair<int, UserDictionary::Entry*> &entry1,
218 const std::pair<int, UserDictionary::Entry*> &entry2) {
219 return entry1.first < entry2.first;
220 }
221 };
222
223 class UndoDeleteEntryCommand : public UserDictionarySession::UndoCommand {
224 public:
225 // This instance takes the ownership of the given entries.
UndoDeleteEntryCommand(uint64 dictionary_id,const std::vector<std::pair<int,UserDictionary::Entry * >> deleted_entries)226 UndoDeleteEntryCommand(
227 uint64 dictionary_id,
228 const std::vector<std::pair<int, UserDictionary::Entry *> >
229 deleted_entries)
230 : dictionary_id_(dictionary_id), deleted_entries_(deleted_entries) {
231 std::sort(deleted_entries_.begin(), deleted_entries_.end(),
232 DeleteEntryComparator());
233 }
~UndoDeleteEntryCommand()234 virtual ~UndoDeleteEntryCommand() {
235 for (size_t i = 0; i < deleted_entries_.size(); ++i) {
236 delete deleted_entries_[i].second;
237 }
238 }
239
RunUndo(mozc::UserDictionaryStorage * storage)240 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
241 UserDictionary *dictionary =
242 UserDictionaryUtil::GetMutableUserDictionaryById(
243 &storage->user_dictionary_storage_base, dictionary_id_);
244 if (dictionary == NULL) {
245 return false;
246 }
247
248 // Check validity of the holding indices.
249 const int num_merged_entries =
250 dictionary->entries_size() + deleted_entries_.size();
251 for (size_t i = 0; i < deleted_entries_.size(); ++i) {
252 const int index = deleted_entries_[i].first;
253 if (index < 0 || num_merged_entries <= index) {
254 return false;
255 }
256 }
257
258 RepeatedPtrField<UserDictionary::Entry> *entries =
259 dictionary->mutable_entries();
260
261 // Move instances to backup vector.
262 std::vector<UserDictionary::Entry*> backup(
263 entries->pointer_begin(), entries->pointer_end());
264 while (entries->size() > 0) {
265 entries->ReleaseLast();
266 }
267
268 // Merge two vectors into entries.
269 int backup_index = 0;
270 int deleted_entry_index = 0;
271 for (int index = 0; deleted_entry_index < deleted_entries_.size();
272 ++index) {
273 if (index == deleted_entries_[deleted_entry_index].first) {
274 entries->AddAllocated(deleted_entries_[deleted_entry_index].second);
275 ++deleted_entry_index;
276 } else {
277 entries->AddAllocated(backup[backup_index]);
278 ++backup_index;
279 }
280 }
281
282 // Add remaining entries in backup to the entries.
283 for (; backup_index < backup.size(); ++backup_index) {
284 entries->AddAllocated(backup[backup_index]);
285 }
286
287 // Release the entries.
288 deleted_entries_.clear();
289 return true;
290 }
291
292 private:
293 uint64 dictionary_id_;
294 std::vector<std::pair<int, UserDictionary::Entry*> > deleted_entries_;
295
296 DISALLOW_COPY_AND_ASSIGN(UndoDeleteEntryCommand);
297 };
298
299 class UndoImportFromStringCommand : public UserDictionarySession::UndoCommand {
300 public:
UndoImportFromStringCommand(uint64 dictionary_id,int original_num_entries)301 UndoImportFromStringCommand(uint64 dictionary_id, int original_num_entries)
302 : dictionary_id_(dictionary_id),
303 original_num_entries_(original_num_entries) {
304 }
305
RunUndo(mozc::UserDictionaryStorage * storage)306 virtual bool RunUndo(mozc::UserDictionaryStorage *storage) {
307 UserDictionary *dictionary =
308 UserDictionaryUtil::GetMutableUserDictionaryById(
309 &storage->user_dictionary_storage_base, dictionary_id_);
310 if (dictionary == NULL) {
311 return false;
312 }
313
314 RepeatedPtrField<UserDictionary::Entry> *entries =
315 dictionary->mutable_entries();
316 while (original_num_entries_ < entries->size()) {
317 entries->RemoveLast();
318 }
319 return true;
320 }
321
322 private:
323 uint64 dictionary_id_;
324 int original_num_entries_;
325
326 DISALLOW_COPY_AND_ASSIGN(UndoImportFromStringCommand);
327 };
328
329 // The limit of the number of commands remembered by the session for undo.
330 const int kMaxUndoHistory = 30;
331
332 // The default name of a dictionary, which is created to ensure "non-empty"
333 // storage.
334 const char kDefaultDictionaryName[] = "user dictionary";
335
336 } // namespace
337
UserDictionarySession(const string & filepath)338 UserDictionarySession::UserDictionarySession(const string &filepath)
339 : storage_(new mozc::UserDictionaryStorage(filepath)),
340 default_dictionary_name_(kDefaultDictionaryName) {
341 }
~UserDictionarySession()342 UserDictionarySession::~UserDictionarySession() {
343 ClearUndoHistory();
344 }
345
346 // TODO(hidehiko) move this to header.
storage() const347 const UserDictionaryStorage &UserDictionarySession::storage() const {
348 return storage_->user_dictionary_storage_base;
349 }
mutable_storage()350 mozc::UserDictionaryStorage *UserDictionarySession::mutable_storage() {
351 return storage_.get();
352 }
353
354 UserDictionaryCommandStatus::Status
SetDefaultDictionaryName(const string & dictionary_name)355 UserDictionarySession::SetDefaultDictionaryName(
356 const string &dictionary_name) {
357 // Validate the name for the default dictionary. The name is used to create
358 // a dictionary "for an empty storage", so check the validity with the
359 // default instance of UserDictionaryStorage.
360 UserDictionaryCommandStatus::Status status =
361 UserDictionaryUtil::ValidateDictionaryName(
362 UserDictionaryStorage::default_instance(), dictionary_name);
363 if (status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
364 default_dictionary_name_ = dictionary_name;
365 }
366 return status;
367 }
368
Load()369 UserDictionaryCommandStatus::Status UserDictionarySession::Load() {
370 return LoadInternal(false);
371 }
372
373 UserDictionaryCommandStatus::Status
LoadWithEnsuringNonEmptyStorage()374 UserDictionarySession::LoadWithEnsuringNonEmptyStorage() {
375 return LoadInternal(true);
376 }
377
LoadInternal(bool ensure_non_empty_storage)378 UserDictionaryCommandStatus::Status UserDictionarySession::LoadInternal(
379 bool ensure_non_empty_storage) {
380 UserDictionaryCommandStatus::Status status;
381 if (storage_->Load()) {
382 status = UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
383 } else {
384 switch (storage_->GetLastError()) {
385 case mozc::UserDictionaryStorage::FILE_NOT_EXISTS:
386 status = UserDictionaryCommandStatus::FILE_NOT_FOUND;
387 break;
388 case mozc::UserDictionaryStorage::BROKEN_FILE:
389 status = UserDictionaryCommandStatus::INVALID_FILE_FORMAT;
390 break;
391 default:
392 LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
393 status = UserDictionaryCommandStatus::UNKNOWN_ERROR;
394 break;
395 }
396 }
397
398 if ((ensure_non_empty_storage && EnsureNonEmptyStorage()) ||
399 status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
400 // If the storage is updated, clear the undo history.
401 ClearUndoHistory();
402 }
403 return status;
404 }
405
406 namespace {
407 // Locker of mozc::UserDictionaryStorage in RAII idiom.
408 class ScopedUserDictionaryLocker {
409 public:
ScopedUserDictionaryLocker(mozc::UserDictionaryStorage * storage)410 explicit ScopedUserDictionaryLocker(mozc::UserDictionaryStorage *storage)
411 : storage_(storage) {
412 is_locked_ = storage_->Lock();
413 }
~ScopedUserDictionaryLocker()414 ~ScopedUserDictionaryLocker() {
415 if (is_locked_) {
416 storage_->UnLock();
417 }
418 }
419
is_locked() const420 bool is_locked() const { return is_locked_; }
421 private:
422 mozc::UserDictionaryStorage *storage_;
423 bool is_locked_;
424
425 DISALLOW_COPY_AND_ASSIGN(ScopedUserDictionaryLocker);
426 };
427 } // namespace
428
Save()429 UserDictionaryCommandStatus::Status UserDictionarySession::Save() {
430 ScopedUserDictionaryLocker locker(storage_.get());
431 if (!locker.is_locked()) {
432 LOG(ERROR) << "Failed to take a lock.";
433 return UserDictionaryCommandStatus::UNKNOWN_ERROR;
434 }
435
436 if (!storage_->Save()) {
437 switch (storage_->GetLastError()) {
438 case mozc::UserDictionaryStorage::TOO_BIG_FILE_BYTES:
439 return UserDictionaryCommandStatus::FILE_SIZE_LIMIT_EXCEEDED;
440 // TODO(hidehiko): Handle SYNC_FAILURE.
441 default:
442 LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
443 return UserDictionaryCommandStatus::UNKNOWN_ERROR;
444 }
445 // Should never reach here.
446 }
447
448 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
449 }
450
Undo()451 UserDictionaryCommandStatus::Status UserDictionarySession::Undo() {
452 if (undo_history_.empty()) {
453 return UserDictionaryCommandStatus::NO_UNDO_HISTORY;
454 }
455
456 std::unique_ptr<UndoCommand> undo_command(undo_history_.back());
457 undo_history_.pop_back();
458 return undo_command->RunUndo(storage_.get()) ?
459 UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS :
460 UserDictionaryCommandStatus::UNKNOWN_ERROR;
461 }
462
CreateDictionary(const string & dictionary_name,uint64 * new_dictionary_id)463 UserDictionaryCommandStatus::Status UserDictionarySession::CreateDictionary(
464 const string &dictionary_name, uint64 *new_dictionary_id) {
465 UserDictionaryCommandStatus::Status status =
466 UserDictionaryUtil::CreateDictionary(
467 &storage_->user_dictionary_storage_base, dictionary_name, new_dictionary_id);
468 if (status == UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
469 AddUndoCommand(new UndoCreateDictionaryCommand);
470 }
471 return status;
472 }
473
DeleteDictionary(uint64 dictionary_id)474 UserDictionaryCommandStatus::Status UserDictionarySession::DeleteDictionary(
475 uint64 dictionary_id) {
476 return DeleteDictionaryInternal(dictionary_id, false);
477 }
478
479 UserDictionaryCommandStatus::Status
DeleteDictionaryWithEnsuringNonEmptyStorage(uint64 dictionary_id)480 UserDictionarySession::DeleteDictionaryWithEnsuringNonEmptyStorage(
481 uint64 dictionary_id) {
482 return DeleteDictionaryInternal(dictionary_id, true);
483 }
484
485 UserDictionaryCommandStatus::Status
DeleteDictionaryInternal(uint64 dictionary_id,bool ensure_non_empty_storage)486 UserDictionarySession::DeleteDictionaryInternal(
487 uint64 dictionary_id, bool ensure_non_empty_storage) {
488 int original_index;
489 UserDictionary *deleted_dictionary;
490 if (!UserDictionaryUtil::DeleteDictionary(
491 &storage_->user_dictionary_storage_base, dictionary_id,
492 &original_index, &deleted_dictionary)) {
493 // Failed to delete the dictionary.
494 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
495 }
496
497 if ((ensure_non_empty_storage && EnsureNonEmptyStorage())) {
498 // The storage was empty.
499 AddUndoCommand(new UndoDeleteDictionaryWithEnsuringNonEmptyStorageCommand(
500 deleted_dictionary));
501 } else {
502 AddUndoCommand(
503 new UndoDeleteDictionaryCommand(original_index, deleted_dictionary));
504 }
505
506 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
507 }
508
RenameDictionary(uint64 dictionary_id,const string & dictionary_name)509 UserDictionaryCommandStatus::Status UserDictionarySession::RenameDictionary(
510 uint64 dictionary_id, const string &dictionary_name) {
511 string original_name;
512 const UserDictionary *dictionary =
513 UserDictionaryUtil::GetUserDictionaryById(storage_->user_dictionary_storage_base, dictionary_id);
514 if (dictionary != NULL) {
515 // Note that if dictionary is null, it means the dictionary_id is invalid
516 // so following RenameDictionary will fail, and error handling is done
517 // in the following codes.
518 original_name = dictionary->name();
519 }
520
521 if (!storage_->RenameDictionary(dictionary_id, dictionary_name)) {
522 switch (storage_->GetLastError()) {
523 case mozc::UserDictionaryStorage::EMPTY_DICTIONARY_NAME:
524 return UserDictionaryCommandStatus::DICTIONARY_NAME_EMPTY;
525 case mozc::UserDictionaryStorage::TOO_LONG_DICTIONARY_NAME:
526 return UserDictionaryCommandStatus::DICTIONARY_NAME_TOO_LONG;
527 case mozc::UserDictionaryStorage::INVALID_CHARACTERS_IN_DICTIONARY_NAME:
528 return UserDictionaryCommandStatus
529 ::DICTIONARY_NAME_CONTAINS_INVALID_CHARACTER;
530 case mozc::UserDictionaryStorage::DUPLICATED_DICTIONARY_NAME:
531 return UserDictionaryCommandStatus::DICTIONARY_NAME_DUPLICATED;
532 case mozc::UserDictionaryStorage::INVALID_DICTIONARY_ID:
533 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
534 default:
535 LOG(ERROR) << "Unknown error code: " << storage_->GetLastError();
536 return UserDictionaryCommandStatus::UNKNOWN_ERROR;
537 }
538 // Should never reach here.
539 }
540
541 AddUndoCommand(
542 new UndoRenameDictionaryCommand(dictionary_id, original_name));
543 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
544 }
545
AddEntry(uint64 dictionary_id,const UserDictionary::Entry & entry)546 UserDictionaryCommandStatus::Status UserDictionarySession::AddEntry(
547 uint64 dictionary_id, const UserDictionary::Entry &entry) {
548 UserDictionary *dictionary =
549 UserDictionaryUtil::GetMutableUserDictionaryById(
550 &storage_->user_dictionary_storage_base, dictionary_id);
551 if (dictionary == NULL) {
552 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
553 }
554
555 if (UserDictionaryUtil::IsDictionaryFull(*dictionary)) {
556 return UserDictionaryCommandStatus::ENTRY_SIZE_LIMIT_EXCEEDED;
557 }
558
559 const UserDictionaryCommandStatus::Status status =
560 UserDictionaryUtil::ValidateEntry(entry);
561 if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
562 // Invalid entry.
563 return status;
564 }
565
566 UserDictionary::Entry *new_entry = dictionary->add_entries();
567 new_entry->CopyFrom(entry);
568 UserDictionaryUtil::SanitizeEntry(new_entry);
569
570 AddUndoCommand(new UndoAddEntryCommand(dictionary_id));
571 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
572 }
573
EditEntry(uint64 dictionary_id,int index,const UserDictionary::Entry & entry)574 UserDictionaryCommandStatus::Status UserDictionarySession::EditEntry(
575 uint64 dictionary_id, int index, const UserDictionary::Entry &entry) {
576 UserDictionary *dictionary =
577 UserDictionaryUtil::GetMutableUserDictionaryById(
578 &storage_->user_dictionary_storage_base, dictionary_id);
579 if (dictionary == NULL) {
580 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
581 }
582
583 if (index < 0 || dictionary->entries_size() <= index) {
584 return UserDictionaryCommandStatus::ENTRY_INDEX_OUT_OF_RANGE;
585 }
586
587 const UserDictionaryCommandStatus::Status status =
588 UserDictionaryUtil::ValidateEntry(entry);
589 if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
590 // Invalid entry.
591 return status;
592 }
593
594 UserDictionary::Entry *target_entry = dictionary->mutable_entries(index);
595 AddUndoCommand(
596 new UndoEditEntryCommand(dictionary_id, index, *target_entry));
597
598 target_entry->CopyFrom(entry);
599 UserDictionaryUtil::SanitizeEntry(target_entry);
600 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
601 }
602
DeleteEntry(uint64 dictionary_id,const std::vector<int> & index_list)603 UserDictionaryCommandStatus::Status UserDictionarySession::DeleteEntry(
604 uint64 dictionary_id, const std::vector<int> &index_list) {
605 UserDictionary *dictionary =
606 UserDictionaryUtil::GetMutableUserDictionaryById(
607 &storage_->user_dictionary_storage_base, dictionary_id);
608 if (dictionary == NULL) {
609 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
610 }
611
612 for (size_t i = 0; i < index_list.size(); ++i) {
613 const int index = index_list[i];
614 if (index < 0 || dictionary->entries_size() <= index) {
615 return UserDictionaryCommandStatus::ENTRY_INDEX_OUT_OF_RANGE;
616 }
617 }
618
619 std::vector<std::pair<int, UserDictionary::Entry*> > deleted_entries;
620 deleted_entries.reserve(index_list.size());
621
622 RepeatedPtrField<UserDictionary::Entry> *entries =
623 dictionary->mutable_entries();
624 UserDictionary::Entry **data = entries->mutable_data();
625 for (size_t i = 0; i < index_list.size(); ++i) {
626 const int index = index_list[i];
627
628 deleted_entries.push_back(std::make_pair(index, data[index]));
629 data[index] = NULL;
630 }
631
632 UserDictionary::Entry **tail = std::remove(
633 data, data + entries->size(), static_cast<UserDictionary::Entry *>(NULL));
634 const int remaining_size = tail - data;
635 while (entries->size() > remaining_size) {
636 entries->ReleaseLast();
637 }
638
639 AddUndoCommand(new UndoDeleteEntryCommand(dictionary_id, deleted_entries));
640 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
641 }
642
ImportFromString(uint64 dictionary_id,const string & data)643 UserDictionaryCommandStatus::Status UserDictionarySession::ImportFromString(
644 uint64 dictionary_id, const string &data) {
645 UserDictionary *dictionary =
646 UserDictionaryUtil::GetMutableUserDictionaryById(
647 &storage_->user_dictionary_storage_base, dictionary_id);
648 if (dictionary == NULL) {
649 return UserDictionaryCommandStatus::UNKNOWN_DICTIONARY_ID;
650 }
651
652 int original_num_entries = dictionary->entries_size();
653 UserDictionaryCommandStatus::Status status =
654 ImportFromStringInternal(dictionary, data);
655
656 // Remember the command regardless of whether the importing is successfully
657 // done or not, because ImportFromStringInternal updates the dictionary
658 // always.
659 AddUndoCommand(
660 new UndoImportFromStringCommand(dictionary_id, original_num_entries));
661
662 return status;
663 }
664
665 UserDictionaryCommandStatus::Status
ImportFromStringInternal(UserDictionary * dictionary,const string & data)666 UserDictionarySession::ImportFromStringInternal(
667 UserDictionary* dictionary, const string &data) {
668 UserDictionaryImporter::ErrorType import_result;
669 {
670 UserDictionaryImporter::StringTextLineIterator iter(data);
671 import_result =
672 UserDictionaryImporter::ImportFromTextLineIterator(
673 UserDictionaryImporter::IME_AUTO_DETECT, &iter, dictionary);
674 }
675
676 LOG_IF(WARNING, import_result != UserDictionaryImporter::IMPORT_NO_ERROR)
677 << "Import failed: " << import_result;
678
679 // Return status code.
680 switch (import_result) {
681 case UserDictionaryImporter::IMPORT_NO_ERROR:
682 // Succeeded.
683 return UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS;
684
685 // Failed on some reasons.
686 case UserDictionaryImporter::IMPORT_TOO_MANY_WORDS:
687 return UserDictionaryCommandStatus::IMPORT_TOO_MANY_WORDS;
688 case UserDictionaryImporter::IMPORT_INVALID_ENTRIES:
689 return UserDictionaryCommandStatus::IMPORT_INVALID_ENTRIES;
690 default:
691 LOG(ERROR) << "Unknown error: " << import_result;
692 return UserDictionaryCommandStatus::UNKNOWN_ERROR;
693 }
694 }
695
696 UserDictionaryCommandStatus::Status
ImportToNewDictionaryFromString(const string & dictionary_name,const string & data,uint64 * new_dictionary_id)697 UserDictionarySession::ImportToNewDictionaryFromString(
698 const string &dictionary_name, const string &data,
699 uint64 *new_dictionary_id) {
700 UserDictionaryCommandStatus::Status status =
701 UserDictionaryUtil::CreateDictionary(
702 &storage_->user_dictionary_storage_base, dictionary_name, new_dictionary_id);
703 if (status != UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS) {
704 return status;
705 }
706
707 // We can use undo command for CreateDictionary here, too.
708 AddUndoCommand(new UndoCreateDictionaryCommand);
709
710 UserDictionary *dictionary =
711 UserDictionaryUtil::GetMutableUserDictionaryById(
712 &storage_->user_dictionary_storage_base, *new_dictionary_id);
713 if (dictionary == NULL) {
714 // The dictionary should be always found.
715 return UserDictionaryCommandStatus::UNKNOWN_ERROR;
716 }
717
718 return ImportFromStringInternal(dictionary, data);
719 }
720
EnsureNonEmptyStorage()721 bool UserDictionarySession::EnsureNonEmptyStorage() {
722 if (storage_->user_dictionary_storage_base.dictionaries_size() > 0) {
723 // The storage already has at least one dictionary. Do nothing.
724 return false;
725 }
726
727 // Creates a dictionary with the default name. Should never fail.
728 uint64 new_dictionary_id;
729 UserDictionaryCommandStatus::Status status =
730 UserDictionaryUtil::CreateDictionary(
731 &storage_->user_dictionary_storage_base, default_dictionary_name_, &new_dictionary_id);
732 CHECK_EQ(
733 status, UserDictionaryCommandStatus::USER_DICTIONARY_COMMAND_SUCCESS);
734 return true;
735 }
736
ClearUndoHistory()737 void UserDictionarySession::ClearUndoHistory() {
738 for (std::deque<UndoCommand *>::iterator iter = undo_history_.begin();
739 iter != undo_history_.end(); ++iter) {
740 delete *iter;
741 }
742 undo_history_.clear();
743 }
744
AddUndoCommand(UndoCommand * undo_command)745 void UserDictionarySession::AddUndoCommand(UndoCommand *undo_command) {
746 // To avoid OOM due to huge undo history, we limit the undo-able
747 // command size by kMaxUndoHistory.
748 while (undo_history_.size() >= kMaxUndoHistory) {
749 delete undo_history_.front();
750 undo_history_.pop_front();
751 }
752
753 undo_history_.push_back(undo_command);
754 }
755
ClearDictionariesAndUndoHistory()756 void UserDictionarySession::ClearDictionariesAndUndoHistory() {
757 ScopedUserDictionaryLocker l(storage_.get());
758 storage_->user_dictionary_storage_base.clear_dictionaries();
759 ClearUndoHistory();
760 }
761
762 } // namespace user_dictionary
763 } // namespace mozc
764