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