Codebase list mozc / upstream/1.4.1003.102 sync / user_dictionary_adapter.cc
upstream/1.4.1003.102

Tree @upstream/1.4.1003.102 (Download .tar.gz)

user_dictionary_adapter.cc @upstream/1.4.1003.102raw · history · blame

// Copyright 2010-2012, 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 "sync/user_dictionary_adapter.h"

#include <algorithm>
#include <string>

#include "base/base.h"
#include "base/util.h"
#include "dictionary/user_dictionary_storage.h"
#include "dictionary/user_dictionary_util.h"
#include "storage/registry.h"
#include "sync/logging.h"
#include "sync/user_dictionary_sync_util.h"
#include "sync/sync.pb.h"
#include "sync/sync_util.h"

namespace mozc {
namespace sync {
namespace {
const uint32 kBucketSize = 256;
const char kLastBucketIdKey[] = "sync.user_dictionary_last_bucket_id";
}  // anonymous namespace

UserDictionaryAdapter::UserDictionaryAdapter() {
  // set default user dictionary
  SetUserDictionaryFileName(UserDictionaryUtil::GetUserDictionaryFileName());
}

UserDictionaryAdapter::~UserDictionaryAdapter() {}

bool UserDictionaryAdapter::SetDownloadedItems(
    const ime_sync::SyncItems &items) {
  vector<const UserDictionarySyncUtil::UserDictionaryStorageBase *>
      remote_updates;

  SYNC_VLOG(1) << "Start SetDownloadedItems: "
               << items.size() << " items";

  if (items.size() == 0) {
    SYNC_VLOG(1) << "No items found";
    return true;
  }

  // Aggregate all remote updates.
  uint32 bucket_id = kuint32max;
  for (size_t i = 0; i < items.size(); ++i) {
    const ime_sync::SyncItem &item = items.Get(i);
    if (item.component() != component_id() ||
        !item.key().HasExtension(sync::UserDictionaryKey::ext) ||
        !item.value().HasExtension(sync::UserDictionaryValue::ext)) {
      continue;
    }
    const sync::UserDictionaryKey &key =
        item.key().GetExtension(sync::UserDictionaryKey::ext);
    const sync::UserDictionaryValue &value =
        item.value().GetExtension(sync::UserDictionaryValue::ext);

    if (!value.has_user_dictionary_storage()) {
      SYNC_VLOG(1) << "value has no user_dictionary_storage";
      continue;
    }

    if (!key.has_bucket_id()) {
      SYNC_VLOG(1) << "value has no bucket_id";
      continue;
    }

    remote_updates.push_back(&(value.user_dictionary_storage()));
    bucket_id = key.bucket_id();
  }

  if (bucket_id != kuint32max && !SetBucketId(bucket_id)) {
    SYNC_VLOG(1) << "cannot save bucket id";
    return false;
  }

  SYNC_VLOG(1) << "current backet_id=" << bucket_id;

  const string prev_file = GetLastSyncedUserDictionaryFileName();
  const string cur_file = GetUserDictionaryFileName();

  SYNC_VLOG(1) << "comparing " << prev_file << " with " << cur_file;
  if (Util::IsEqualFile(prev_file, cur_file)) {
    if (remote_updates.empty()) {
      SYNC_VLOG(1) << "no local_update and no remote_updates.";
      return true;
    }

    SYNC_VLOG(1) << "no local_update and has remote_updates.";
    UserDictionaryStorage cur_storage(cur_file);
    cur_storage.Load();
    SYNC_VLOG(1) << "merging remote_updates to current storage.";
    UserDictionarySyncUtil::MergeUpdates(remote_updates, &cur_storage);
    if (!UserDictionarySyncUtil::VerifyLockAndSaveStorage(&cur_storage)) {
      SYNC_VLOG(1) << "cannot save cur_storage.";
      return false;
    }
    SYNC_VLOG(1) << "copying " << cur_file << " to " << prev_file;
    if (!SyncUtil::CopyLastSyncedFile(cur_file, prev_file)) {
      SYNC_VLOG(1) << "cannot copy " << cur_file << " to " << prev_file;
      return false;
    }
  } else {   // Updates found on the local.
    if (remote_updates.empty()) {
      SYNC_VLOG(1) << "has local_update and no remote_updates.";
      return true;
    }

    // In this case, we simply merge the |local_update| and |remote_updates|.
    SYNC_VLOG(1) << "has local_update and has remote_updates.";

    SYNC_VLOG(1) << "loading " << prev_file;
    UserDictionaryStorage prev_storage(prev_file);
    prev_storage.Load();

    SYNC_VLOG(1) << "loading " << cur_file;
    UserDictionaryStorage cur_storage(cur_file);
    cur_storage.Load();

    // Obtain local update.
    SYNC_VLOG(1) << "making local update";
    UserDictionarySyncUtil::UserDictionaryStorageBase local_update;
    UserDictionarySyncUtil::CreateUpdate(prev_storage, cur_storage,
                                         &local_update);

    if (local_update.dictionaries_size() == 0) {
      SYNC_VLOG(1) << "has no local_update in actual.";
      // no updates are found on the local.
      UserDictionarySyncUtil::MergeUpdates(remote_updates, &cur_storage);
      if (!UserDictionarySyncUtil::VerifyLockAndSaveStorage(&cur_storage)) {
        SYNC_VLOG(1) << "cannot save cur_storage.";
        return false;
      }
      SYNC_VLOG(1) << "copying " << cur_file << " to " << prev_file;
      if (!SyncUtil::CopyLastSyncedFile(cur_file, prev_file)) {
        SYNC_VLOG(1) << "cannot copy " << cur_file << " to " << prev_file;
        return false;
      }
    } else {
      // This case causes a conflict, so we make a backup just in case.
      SYNC_VLOG(1) << "making a backup " << cur_storage.filename() << ".bak";
      if (!Util::CopyFile(cur_storage.filename(),
                          cur_storage.filename() + ".bak")) {
        SYNC_VLOG(1) << "cannot make backup file";
      }

      // First, apply the |remote_updates| to the previous storage.
      // |prev_storage| only reflects the |remote_updates|.
      SYNC_VLOG(1) << "merging remote_updates into prev_storage";
      UserDictionarySyncUtil::MergeUpdates(remote_updates, &prev_storage);

      // We apply the |remote_updates| and |local_update| to
      // the prev_storage. It can be seen as an approximation of
      // mixing |remote_updates| and |local_update|, it is not
      // perfect though.
      SYNC_VLOG(1) << "coping prev_storage into cur_storage";
      cur_storage.CopyFrom(prev_storage);

      SYNC_VLOG(1) << "merging local_update to cur_storage";
      UserDictionarySyncUtil::MergeUpdate(local_update, &cur_storage);

      SYNC_VLOG(1) << "saving cur_storage";
      if (!UserDictionarySyncUtil::VerifyLockAndSaveStorage(&cur_storage)) {
        SYNC_VLOG(1) << "cannot save cur_storage.";
        return false;
      }
      // Even if a sync dictionary of |prev_storage| exceeds its limit after
      // applying |remote_update| on prev_storage, we must save it. So we use
      // LockAndSaveStorage() without verifications. Please refer
      // http://b/5948831 for details.
      SYNC_VLOG(1) << "saving prev_storage";
      if (!UserDictionarySyncUtil::LockAndSaveStorage(&prev_storage)) {
        SYNC_VLOG(1) << "cannot save prev_storage.";
        return false;
      }
    }
  }

  return true;
}

bool UserDictionaryAdapter::GetItemsToUpload(ime_sync::SyncItems *items) {
  DCHECK(items);
  SYNC_VLOG(1) << "Start GetItemsToUpload()";

  if (!Util::FileExists(GetUserDictionaryFileName())) {
    SYNC_VLOG(1) << GetUserDictionaryFileName() << " does not exist.";
    return true;
  }

  const string prev_file = GetLastSyncedUserDictionaryFileName();
  const string cur_file = GetUserDictionaryFileName();

  // No updates found on the local.
  if (Util::IsEqualFile(prev_file, cur_file)) {
    SYNC_VLOG(1) << "No changes found in local dictionary files.";
    return true;
  }

  UserDictionaryStorage prev_storage(prev_file);
  prev_storage.Load();

  UserDictionaryStorage cur_storage(cur_file);
  cur_storage.Load();

  // No updates found on the local.
  if (UserDictionarySyncUtil::IsEqualStorage(prev_storage, cur_storage)) {
    SYNC_VLOG(1) << "No need to upload updates.";
    return true;
  }

  // tmp_file is a 'pending' last synced dictionary.
  // Here we make a temporary file so that we can rollback the
  // last synced file if upload is failed.
  const string tmp_file = GetTempLastSyncedUserDictionaryFileName();
  if (!SyncUtil::CopyLastSyncedFile(cur_file, tmp_file)) {
    SYNC_VLOG(1) << "cannot copy " << cur_file << " to " << tmp_file;
    return false;
  }

  ime_sync::SyncItem *item = items->Add();
  CHECK(item);

  item->set_component(component_id());

  sync::UserDictionaryKey *key =
      item->mutable_key()->MutableExtension(
          sync::UserDictionaryKey::ext);
  sync::UserDictionaryValue *value =
      item->mutable_value()->MutableExtension(
          sync::UserDictionaryValue::ext);
  CHECK(key);
  CHECK(value);

  UserDictionarySyncUtil::UserDictionaryStorageBase *local_update =
      value->mutable_user_dictionary_storage();
  CHECK(local_update);

  // Obtain local update.
  UserDictionarySyncUtil::CreateUpdate(prev_storage, cur_storage,
                                       local_update);

  // No need to update the file.
  if (local_update->dictionaries_size() == 0) {
    SYNC_VLOG(1) << "No local update";
    Util::Unlink(tmp_file);
    items->RemoveLast();
    return true;
  }

  uint32 next_bucket_id = GetNextBucketId();

  // If the diff is too big or next_bucket_id is 0,
  // create a snapshot instead.
  if (next_bucket_id == 0 ||
      UserDictionarySyncUtil::ShouldCreateSnapshot(*local_update)) {
    SYNC_VLOG(1) << "Start creating snapshot";
    // 0 is reserved for snapshot.
    next_bucket_id = 0;
    UserDictionarySyncUtil::CreateSnapshot(cur_storage, local_update);
  }

  key->set_bucket_id(next_bucket_id);

  return true;
}

bool UserDictionaryAdapter::MarkUploaded(
    const ime_sync::SyncItem& item, bool uploaded) {
  SYNC_VLOG(1) << "Start MarkUploaded() uploaded=" << uploaded;

  const string prev_file = GetLastSyncedUserDictionaryFileName();
  const string tmp_file = GetTempLastSyncedUserDictionaryFileName();

  if (!uploaded) {
    // Rollback the last synced file by removing the pending file.
    SYNC_VLOG(1) << "rollbacking the last synced file: " << tmp_file;
    Util::Unlink(tmp_file);
    return true;
  }

  // Push the pending last synced file atomically.
  SYNC_VLOG(1) << "AtomicRename " << tmp_file << " to " << prev_file;
  if (!Util::AtomicRename(tmp_file, prev_file)) {
    SYNC_VLOG(1) << "cannot update: " << prev_file;
    return false;
  }

  const uint32 next_bucket_id = GetNextBucketId();
  SYNC_VLOG(1) << "updating next_bucket_id=" << next_bucket_id;
  if (!SetBucketId(next_bucket_id)) {
    SYNC_VLOG(1) << "cannot set bucket id";
    return false;
  }

  return true;
}

bool UserDictionaryAdapter::Clear() {
  SYNC_VLOG(1) << "start Clear()";
  Util::Unlink(GetLastSyncedUserDictionaryFileName());
  Util::Unlink(GetTempLastSyncedUserDictionaryFileName());
  return true;
}

ime_sync::Component UserDictionaryAdapter::component_id() const {
  return ime_sync::MOZC_USER_DICTIONARY;
}

void UserDictionaryAdapter::SetUserDictionaryFileName(const string &filename) {
  VLOG(1) << "Setting UserDictionaryFileName: " << filename;
  user_dictionary_filename_ = filename;
}

string UserDictionaryAdapter::GetUserDictionaryFileName() const {
  return user_dictionary_filename_;
}

string UserDictionaryAdapter::GetLastSyncedUserDictionaryFileName() const {
  const char kSuffix[] = ".last_synced";
#ifdef OS_WINDOWS
  return GetUserDictionaryFileName() + kSuffix;
#else
  const string dirname = Util::Dirname(GetUserDictionaryFileName());
  const string basename = Util::Basename(GetUserDictionaryFileName());
  return Util::JoinPath(dirname, "." + basename + kSuffix);
#endif
}

string UserDictionaryAdapter::GetTempLastSyncedUserDictionaryFileName() const {
  const char kSuffix[] = ".pending";
  return GetLastSyncedUserDictionaryFileName() + kSuffix;
}

// static
uint32 UserDictionaryAdapter::bucket_size() const {
  return kBucketSize;
}

bool UserDictionaryAdapter::SetBucketId(uint32 bucket_id) {
  if (bucket_id >= bucket_size()) {
    LOG(ERROR) << "invalid bucket_id is given. reset to default";
  }
  if (!mozc::storage::Registry::Insert(kLastBucketIdKey, bucket_id) ||
      !mozc::storage::Registry::Sync()) {
    LOG(ERROR) << "cannot save: "
               << kLastBucketIdKey << " " << bucket_id;
    return false;
  }
  return true;
}

uint32 UserDictionaryAdapter::GetNextBucketId() const {
  uint32 value = 0;
  if (!mozc::storage::Registry::Lookup(kLastBucketIdKey, &value)) {
    LOG(ERROR) << "cannot read: " << kLastBucketIdKey;
    return static_cast<uint32>(0);
  }
  if (value >= bucket_size()) {
    LOG(ERROR) << "invalid bucket_id is saved. reset to default";
    return static_cast<uint32>(0);
  }
  return (value + 1) % bucket_size();
}
}  // namespace sync
}  // namespace mozc