summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorachuith@chromium.org <achuith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-25 00:00:32 +0000
committerachuith@chromium.org <achuith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-25 00:00:32 +0000
commitf99a712d8b469025ccafdbe006fcebd6544271f8 (patch)
treea9fd5e293918d875a9f3de7fe9c286da7cd29a88
parent72e39d4c4ea617083668e883fc0186dea640ca8e (diff)
downloadchromium_src-f99a712d8b469025ccafdbe006fcebd6544271f8.zip
chromium_src-f99a712d8b469025ccafdbe006fcebd6544271f8.tar.gz
chromium_src-f99a712d8b469025ccafdbe006fcebd6544271f8.tar.bz2
GDataDB support with leveldb.
* Define GDataDB interface with methods to Put, Get and Delete. Also define a path-based iterator. * GDataLevelDB implements GDataDB using leveldb. * Add methods SerializeToString and FromProtoString to serialize GDataEntry to strings and vice versa. * GDataDBTests test Put, Get, Delete for files and directories. * Iterator tests in GDataDBTests. * GDataDBFactory class to create GDataLevelDB instance. TODO: * There is no integration with GDataRootDirectory/GDataFileSystem yet. BUG=chromium-os:29232 TEST=unittests pass. Review URL: https://chromiumcodereview.appspot.com/10168025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133815 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/gdata/gdata_db.h69
-rw-r--r--chrome/browser/chromeos/gdata/gdata_db_factory.cc22
-rw-r--r--chrome/browser/chromeos/gdata/gdata_db_factory.h25
-rw-r--r--chrome/browser/chromeos/gdata/gdata_db_unittest.cc266
-rw-r--r--chrome/browser/chromeos/gdata/gdata_files.cc70
-rw-r--r--chrome/browser/chromeos/gdata/gdata_files.h28
-rw-r--r--chrome/browser/chromeos/gdata/gdata_leveldb.cc204
-rw-r--r--chrome/browser/chromeos/gdata/gdata_leveldb.h60
-rw-r--r--chrome/chrome_browser.gypi5
-rw-r--r--chrome/chrome_tests.gypi1
10 files changed, 736 insertions, 14 deletions
diff --git a/chrome/browser/chromeos/gdata/gdata_db.h b/chrome/browser/chromeos/gdata/gdata_db.h
new file mode 100644
index 0000000..67d1e58
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_db.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_H_
+#define CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_H_
+#pragma once
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+class FilePath;
+
+namespace gdata {
+
+class GDataEntry;
+class GDataDBIter;
+
+// GData Database interface class.
+class GDataDB {
+ public:
+ enum Status {
+ DB_OK = 0,
+ DB_KEY_NOT_FOUND, // Key not found.
+ DB_CORRUPTION, // Database file corrupt.
+ DB_IO_ERROR, // File I/O error.
+ DB_INTERNAL_ERROR,
+ };
+
+ virtual ~GDataDB() {}
+
+ // Puts |entry| to the database.
+ virtual Status Put(const GDataEntry& entry) = 0;
+
+ // Deletes a database entry with key |resource_id| or |path| respectively.
+ virtual Status DeleteByResourceId(const std::string& resource_id) = 0;
+ virtual Status DeleteByPath(const FilePath& path) = 0;
+
+ // Fetches a GDataEntry* by key |resource_id| or |path| respectively.
+ virtual Status GetByResourceId(const std::string& resource_id,
+ scoped_ptr<GDataEntry>* entry) = 0;
+ virtual Status GetByPath(const FilePath& path,
+ scoped_ptr<GDataEntry>* entry) = 0;
+
+ // Creates an iterator to fetch all GDataEntry's under |path|.
+ // Will not return NULL.
+ virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) = 0;
+
+ protected:
+ GDataDB() {}
+};
+
+// GData Database Iterator interface class.
+class GDataDBIter {
+ public:
+ virtual ~GDataDBIter() {}
+
+ // Fetches the next |entry| in the iteration sequence. Returns false when
+ // there are no more entries.
+ virtual bool GetNext(std::string* path, scoped_ptr<GDataEntry>* entry) = 0;
+
+ protected:
+ GDataDBIter() {}
+};
+
+} // namespace gdata
+
+#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_H_
diff --git a/chrome/browser/chromeos/gdata/gdata_db_factory.cc b/chrome/browser/chromeos/gdata/gdata_db_factory.cc
new file mode 100644
index 0000000..31c88ca
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_db_factory.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/gdata/gdata_db_factory.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "chrome/browser/chromeos/gdata/gdata_leveldb.h"
+
+namespace gdata {
+namespace db_factory {
+
+scoped_ptr<GDataDB> CreateGDataDB(const FilePath& db_path) {
+ DVLOG(1) << "CreateGDataDB " << db_path.value();
+ GDataLevelDB* level_db = new GDataLevelDB();
+ level_db->Init(db_path);
+ return scoped_ptr<GDataDB>(level_db);
+}
+
+} // namespace db_factory
+} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/gdata_db_factory.h b/chrome/browser/chromeos/gdata/gdata_db_factory.h
new file mode 100644
index 0000000..ad88e1f
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_db_factory.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_FACTORY_H_
+#pragma once
+
+#include "base/memory/scoped_ptr.h"
+
+class FilePath;
+
+namespace gdata {
+
+class GDataDB;
+
+namespace db_factory {
+
+// Factory method to create an instance of GDataDB.
+scoped_ptr<GDataDB> CreateGDataDB(const FilePath& db_path);
+
+} // namespace db_factory
+} // namespace gdata
+
+#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_DB_FACTORY_H_
diff --git a/chrome/browser/chromeos/gdata/gdata_db_unittest.cc b/chrome/browser/chromeos/gdata/gdata_db_unittest.cc
new file mode 100644
index 0000000..f35db48
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_db_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/gdata/gdata_db.h"
+
+#include "base/string_number_conversions.h"
+#include "chrome/browser/chromeos/gdata/gdata_db_factory.h"
+#include "chrome/browser/chromeos/gdata/gdata_files.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "chrome/test/base/testing_profile.h"
+
+namespace gdata {
+namespace {
+
+class GDataDBTest : public testing::Test {
+ public:
+ GDataDBTest() {
+ }
+
+ virtual ~GDataDBTest() {
+ }
+
+ protected:
+ // testing::Test implementation.
+ virtual void SetUp() OVERRIDE;
+
+ // Tests GDataDB::GetPath and GDataDB::ResourceId, ensuring that an entry
+ // matching |source| does not exist.
+ void TestGetNotFound(const GDataEntry& source);
+
+ // Tests GDataDB::GetPath and GDataDB::ResourceId, ensuring that an entry
+ // matching |source| exists.
+ void TestGetFound(const GDataEntry& source);
+
+ // Initialize the database with the following entries:
+ // dir1
+ // dir2
+ // dir1/dir3
+ // dir1/file4
+ // dir1/file5
+ // dir2/file6
+ // dir2/file7
+ // dir2/file8
+ // dir1/dir3/file9
+ // dir1/dir3/file10
+ void InitDB();
+
+ // Helper functions to add a directory/file, incrementing index.
+ GDataDirectory* AddDirectory(GDataDirectory* parent, int sequence_id);
+ GDataFile* AddFile(GDataDirectory* parent, int sequence_id);
+
+ // Tests GDataDB::NewIterator and GDataDBIter::GetNext.
+ // Creates an iterator with start at |parent|, and iterates comparing with
+ // expected |filenames|.
+ void TestIter(const std::string& parent,
+ const char* file_paths[],
+ size_t file_paths_size);
+
+ scoped_ptr<TestingProfile> profile_;
+ scoped_ptr<GDataDB> gdata_db_;
+ GDataRootDirectory root_;
+};
+
+void GDataDBTest::SetUp() {
+ profile_.reset(new TestingProfile());
+ gdata_db_ = db_factory::CreateGDataDB(
+ profile_->GetPath().Append("testdb"));
+}
+
+void GDataDBTest::TestGetNotFound(const GDataEntry& source) {
+ scoped_ptr<GDataEntry> entry;
+ GDataDB::Status status = gdata_db_->GetByPath(source.GetFilePath(), &entry);
+ EXPECT_EQ(GDataDB::DB_KEY_NOT_FOUND, status);
+ EXPECT_FALSE(entry.get());
+
+ status = gdata_db_->GetByResourceId(source.resource_id(), &entry);
+ EXPECT_EQ(GDataDB::DB_KEY_NOT_FOUND, status);
+ EXPECT_FALSE(entry.get());
+}
+
+void GDataDBTest::TestGetFound(const GDataEntry& source) {
+ scoped_ptr<GDataEntry> entry;
+ GDataDB::Status status = gdata_db_->GetByPath(source.GetFilePath(), &entry);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+ ASSERT_TRUE(entry.get());
+ EXPECT_EQ(source.file_name(), entry->file_name());
+ EXPECT_EQ(source.resource_id(), entry->resource_id());
+ EXPECT_EQ(source.content_url(), entry->content_url());
+ entry.reset();
+
+ status = gdata_db_->GetByResourceId(source.resource_id(), &entry);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+ ASSERT_TRUE(entry.get());
+ EXPECT_EQ(source.file_name(), entry->file_name());
+ EXPECT_EQ(source.resource_id(), entry->resource_id());
+ EXPECT_EQ(source.content_url(), entry->content_url());
+}
+
+void GDataDBTest::InitDB() {
+ int sequence_id = 1;
+ GDataDirectory* dir1 = AddDirectory(NULL, sequence_id++);
+ GDataDirectory* dir2 = AddDirectory(NULL, sequence_id++);
+ GDataDirectory* dir3 = AddDirectory(dir1, sequence_id++);
+
+ AddFile(dir1, sequence_id++);
+ AddFile(dir1, sequence_id++);
+
+ AddFile(dir2, sequence_id++);
+ AddFile(dir2, sequence_id++);
+ AddFile(dir2, sequence_id++);
+
+ AddFile(dir3, sequence_id++);
+ AddFile(dir3, sequence_id++);
+}
+
+GDataDirectory* GDataDBTest::AddDirectory(GDataDirectory* parent,
+ int sequence_id) {
+ GDataDirectory* dir = new GDataDirectory(parent ? parent : &root_, &root_);
+ const std::string dir_name = "dir" + base::IntToString(sequence_id);
+ const std::string resource_id = std::string("dir_resource_id:") +
+ dir_name;
+ dir->set_file_name(dir_name);
+ dir->set_resource_id(resource_id);
+ GDataDB::Status status = gdata_db_->Put(*dir);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+ DVLOG(1) << "AddDirectory " << dir->GetFilePath().value()
+ << ", " << resource_id;
+ return dir;
+}
+
+GDataFile* GDataDBTest::AddFile(GDataDirectory* parent,
+ int sequence_id) {
+ GDataFile* file = new GDataFile(parent, &root_);
+ const std::string file_name = "file" + base::IntToString(sequence_id);
+ const std::string resource_id = std::string("file_resource_id:") +
+ file_name;
+ file->set_file_name(file_name);
+ file->set_resource_id(resource_id);
+ GDataDB::Status status = gdata_db_->Put(*file);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+ DVLOG(1) << "AddFile " << file->GetFilePath().value()
+ << ", " << resource_id;
+ return file;
+}
+
+void GDataDBTest::TestIter(const std::string& parent,
+ const char* file_paths[],
+ size_t file_paths_size) {
+ scoped_ptr<GDataDBIter> iter = gdata_db_->CreateIterator(
+ FilePath::FromUTF8Unsafe(parent));
+ for (size_t i = 0; ; ++i) {
+ scoped_ptr<GDataEntry> entry;
+ std::string path;
+ if (!iter->GetNext(&path, &entry)) {
+ EXPECT_EQ(i, file_paths_size);
+ break;
+ }
+ ASSERT_LT(i, file_paths_size);
+ // TODO(achuith): Also test entry->GetFilePath().
+ EXPECT_EQ(FilePath(file_paths[i]).BaseName().value(), entry->file_name());
+ EXPECT_EQ(file_paths[i], path);
+ DVLOG(1) << "Iter " << path;
+ }
+}
+
+} // namespace
+
+TEST_F(GDataDBTest, PutTest) {
+ GDataDirectory* dir = new GDataDirectory(&root_, &root_);
+ dir->set_file_name("dir");
+ dir->set_resource_id("dir_resource_id");
+ dir->set_content_url(GURL("http://content/dir"));
+ dir->set_upload_url(GURL("http://upload/dir"));
+
+ TestGetNotFound(*dir);
+
+ GDataDB::Status status = gdata_db_->Put(*dir);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+
+ TestGetFound(*dir);
+
+ scoped_ptr<GDataEntry> entry;
+ gdata_db_->GetByPath(dir->GetFilePath(), &entry);
+ EXPECT_EQ(dir->upload_url(), entry->AsGDataDirectory()->upload_url());
+ EXPECT_TRUE(entry->AsGDataDirectory()->file_info().is_directory);
+
+ status = gdata_db_->DeleteByPath(dir->GetFilePath());
+ EXPECT_EQ(GDataDB::DB_OK, status);
+
+ TestGetNotFound(*dir);
+
+ GDataFile* file = new GDataFile(dir, &root_);
+ file->set_file_name("file1");
+ file->set_resource_id("file1_resource_id");
+ file->set_content_url(GURL("http://content/dir1/file1"));
+ file->set_file_md5("file1_md5");
+
+ TestGetNotFound(*file);
+
+ status = gdata_db_->Put(*file);
+ EXPECT_EQ(GDataDB::DB_OK, status);
+
+ TestGetFound(*file);
+
+ gdata_db_->GetByPath(file->GetFilePath(), &entry);
+ EXPECT_EQ(file->file_md5(), entry->AsGDataFile()->file_md5());
+ EXPECT_FALSE(entry->AsGDataFile()->file_info().is_directory);
+
+ status = gdata_db_->DeleteByPath(file->GetFilePath());
+ EXPECT_EQ(GDataDB::DB_OK, status);
+
+ TestGetNotFound(*file);
+}
+
+TEST_F(GDataDBTest, IterTest) {
+ InitDB();
+
+ const char* dir1_children[] = {
+ "dir1",
+ "dir1/dir3",
+ "dir1/dir3/file10",
+ "dir1/dir3/file9",
+ "dir1/file4",
+ "dir1/file5",
+ };
+ TestIter("dir1", dir1_children, arraysize(dir1_children));
+
+ const char* dir2_children[] = {
+ "dir2",
+ "dir2/file6",
+ "dir2/file7",
+ "dir2/file8",
+ };
+ TestIter("dir2", dir2_children, arraysize(dir2_children));
+
+ const char* dir3_children[] = {
+ "dir1/dir3",
+ "dir1/dir3/file10",
+ "dir1/dir3/file9",
+ };
+ TestIter("dir1/dir3", dir3_children, arraysize(dir3_children));
+
+ const char* file10[] = {
+ "dir1/dir3/file10",
+ };
+ TestIter(file10[0], file10, arraysize(file10));
+
+ const char* all_entries[] = {
+ "dir1",
+ "dir1/dir3",
+ "dir1/dir3/file10",
+ "dir1/dir3/file9",
+ "dir1/file4",
+ "dir1/file5",
+ "dir2",
+ "dir2/file6",
+ "dir2/file7",
+ "dir2/file8",
+ };
+ TestIter("", all_entries, arraysize(all_entries));
+
+ TestIter("dir4", NULL, 0);
+}
+
+} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/gdata_files.cc b/chrome/browser/chromeos/gdata/gdata_files.cc
index 9dcd99a..43a3cca 100644
--- a/chrome/browser/chromeos/gdata/gdata_files.cc
+++ b/chrome/browser/chromeos/gdata/gdata_files.cc
@@ -78,10 +78,20 @@ GDataRootDirectory* GDataEntry::AsGDataRootDirectory() {
return NULL;
}
-FilePath GDataEntry::GetFilePath() {
+const GDataFile* GDataEntry::AsGDataFileConst() const {
+ // cast away const and call the non-const version. This is safe.
+ return const_cast<GDataEntry*>(this)->AsGDataFile();
+}
+
+const GDataDirectory* GDataEntry::AsGDataDirectoryConst() const {
+ // cast away const and call the non-const version. This is safe.
+ return const_cast<GDataEntry*>(this)->AsGDataDirectory();
+}
+
+FilePath GDataEntry::GetFilePath() const {
FilePath path;
std::vector<FilePath::StringType> parts;
- for (GDataEntry* entry = this; entry != NULL; entry = entry->parent())
+ for (const GDataEntry* entry = this; entry != NULL; entry = entry->parent_)
parts.push_back(entry->file_name());
// Paste paths parts back together in reverse order from upward tree
@@ -133,6 +143,7 @@ GDataFile::GDataFile(GDataDirectory* parent, GDataRootDirectory* root)
: GDataEntry(parent, root),
kind_(gdata::DocumentEntry::UNKNOWN),
is_hosted_document_(false) {
+ file_info_.is_directory = false;
}
GDataFile::~GDataFile() {
@@ -581,6 +592,7 @@ void GDataEntry::ToProto(GDataEntryProto* proto) const {
}
void GDataFile::FromProto(const GDataFileProto& proto) {
+ DCHECK(!proto.gdata_entry().file_info().is_directory());
GDataEntry::FromProto(proto.gdata_entry());
kind_ = DocumentEntry::EntryKind(proto.kind());
thumbnail_url_ = GURL(proto.thumbnail_url());
@@ -595,6 +607,7 @@ void GDataFile::FromProto(const GDataFileProto& proto) {
void GDataFile::ToProto(GDataFileProto* proto) const {
GDataEntry::ToProto(proto->mutable_gdata_entry());
+ DCHECK(!proto->gdata_entry().file_info().is_directory());
proto->set_kind(kind_);
proto->set_thumbnail_url(thumbnail_url_.spec());
proto->set_alternate_url(alternate_url_.spec());
@@ -607,6 +620,7 @@ void GDataFile::ToProto(GDataFileProto* proto) const {
}
void GDataDirectory::FromProto(const GDataDirectoryProto& proto) {
+ DCHECK(proto.gdata_entry().file_info().is_directory());
GDataEntry::FromProto(proto.gdata_entry());
refresh_time_ = base::Time::FromInternalValue(proto.refresh_time());
start_feed_url_ = GURL(proto.start_feed_url());
@@ -627,6 +641,7 @@ void GDataDirectory::FromProto(const GDataDirectoryProto& proto) {
void GDataDirectory::ToProto(GDataDirectoryProto* proto) const {
GDataEntry::ToProto(proto->mutable_gdata_entry());
+ DCHECK(proto->gdata_entry().file_info().is_directory());
proto->set_refresh_time(refresh_time_.ToInternalValue());
proto->set_start_feed_url(start_feed_url_.spec());
proto->set_next_feed_url(next_feed_url_.spec());
@@ -654,18 +669,61 @@ void GDataRootDirectory::ToProto(GDataRootDirectoryProto* proto) const {
proto->set_largest_changestamp(largest_changestamp_);
}
-void GDataRootDirectory::SerializeToString(std::string* proto_string) const {
+void GDataEntry::SerializeToString(std::string* serialized_proto) const {
+ const GDataFile* file = AsGDataFileConst();
+ const GDataDirectory* dir = AsGDataDirectoryConst();
+
+ if (file) {
+ scoped_ptr<GDataFileProto> proto(new GDataFileProto());
+ file->ToProto(proto.get());
+ const bool ok = proto->SerializeToString(serialized_proto);
+ DCHECK(ok);
+ } else if (dir) {
+ scoped_ptr<GDataDirectoryProto> proto(new GDataDirectoryProto());
+ dir->ToProto(proto.get());
+ const bool ok = proto->SerializeToString(serialized_proto);
+ DCHECK(ok);
+ }
+}
+
+// static
+scoped_ptr<GDataEntry> GDataEntry::FromProtoString(
+ const std::string& serialized_proto) {
+ // First try to parse as GDataDirectoryProto. Note that this can succeed for
+ // a serialized_proto that's really a GDataFileProto - we have to check
+ // is_directory to be sure.
+ scoped_ptr<GDataDirectoryProto> dir_proto(new GDataDirectoryProto());
+ bool ok = dir_proto->ParseFromString(serialized_proto);
+ if (ok && dir_proto->gdata_entry().file_info().is_directory()) {
+ GDataDirectory* dir = new GDataDirectory(NULL, NULL);
+ dir->FromProto(*dir_proto);
+ return scoped_ptr<GDataEntry>(dir);
+ }
+
+ scoped_ptr<GDataFileProto> file_proto(new GDataFileProto());
+ ok = file_proto->ParseFromString(serialized_proto);
+ if (ok) {
+ DCHECK(!file_proto->gdata_entry().file_info().is_directory());
+ GDataFile* file = new GDataFile(NULL, NULL);
+ file->FromProto(*file_proto);
+ return scoped_ptr<GDataEntry>(file);
+ }
+ return scoped_ptr<GDataEntry>(NULL);
+}
+
+void GDataRootDirectory::SerializeToString(
+ std::string* serialized_proto) const {
scoped_ptr<GDataRootDirectoryProto> proto(
new GDataRootDirectoryProto());
ToProto(proto.get());
- const bool ok = proto->SerializeToString(proto_string);
+ const bool ok = proto->SerializeToString(serialized_proto);
DCHECK(ok);
}
-bool GDataRootDirectory::ParseFromString(const std::string& proto_string) {
+bool GDataRootDirectory::ParseFromString(const std::string& serialized_proto) {
scoped_ptr<GDataRootDirectoryProto> proto(
new GDataRootDirectoryProto());
- bool ok = proto->ParseFromString(proto_string);
+ bool ok = proto->ParseFromString(serialized_proto);
if (ok) {
FromProto(*proto.get());
set_origin(FROM_CACHE);
diff --git a/chrome/browser/chromeos/gdata/gdata_files.h b/chrome/browser/chromeos/gdata/gdata_files.h
index b7e9031..1944235 100644
--- a/chrome/browser/chromeos/gdata/gdata_files.h
+++ b/chrome/browser/chromeos/gdata/gdata_files.h
@@ -59,15 +59,27 @@ class GDataEntry {
public:
explicit GDataEntry(GDataDirectory* parent, GDataRootDirectory* root);
virtual ~GDataEntry();
+
virtual GDataFile* AsGDataFile();
virtual GDataDirectory* AsGDataDirectory();
virtual GDataRootDirectory* AsGDataRootDirectory();
+ // const versions of AsGDataFile and AsGDataDirectory.
+ const GDataFile* AsGDataFileConst() const;
+ const GDataDirectory* AsGDataDirectoryConst() const;
+
// Converts DocumentEntry into GDataEntry.
static GDataEntry* FromDocumentEntry(GDataDirectory* parent,
DocumentEntry* doc,
GDataRootDirectory* root);
+ // Serialize/Parse to/from string via proto classes.
+ // TODO(achuith): Correctly set up parent_ and root_ links in
+ // FromProtoString.
+ void SerializeToString(std::string* serialized_proto) const;
+ static scoped_ptr<GDataEntry> FromProtoString(
+ const std::string& serialized_proto);
+
// Convert to/from proto.
void FromProto(const GDataEntryProto& proto);
void ToProto(GDataEntryProto* proto) const;
@@ -81,20 +93,20 @@ class GDataEntry {
GDataDirectory* parent() { return parent_; }
const base::PlatformFileInfo& file_info() const { return file_info_; }
+
const FilePath::StringType& file_name() const { return file_name_; }
- const FilePath::StringType& title() const {
- return title_;
- }
- void set_title(const FilePath::StringType& title) {
- title_ = title;
- }
void set_file_name(const FilePath::StringType& name) { file_name_ = name; }
+ const FilePath::StringType& title() const { return title_; }
+ void set_title(const FilePath::StringType& title) { title_ = title; }
+
// The unique resource ID associated with this file system entry.
const std::string& resource_id() const { return resource_id_; }
+ void set_resource_id(const std::string& res_id) { resource_id_ = res_id; }
// The content URL is used for downloading regular files as is.
const GURL& content_url() const { return content_url_; }
+ void set_content_url(const GURL& url) { content_url_ = url; }
// The edit URL is used for removing files and hosted documents.
const GURL& edit_url() const { return edit_url_; }
@@ -111,7 +123,7 @@ class GDataEntry {
// Returns virtual file path representing this file system entry. This path
// corresponds to file path expected by public methods of GDataFileSyste
// class.
- FilePath GetFilePath();
+ FilePath GetFilePath() const;
// Sets |file_name_| based on the value of |title_| without name
// de-duplication (see AddEntry() for details on de-duplication).
@@ -221,6 +233,7 @@ class GDataFile : public GDataEntry {
const std::string& etag() const { return etag_; }
const std::string& id() const { return id_; }
const std::string& file_md5() const { return file_md5_; }
+ void set_file_md5(const std::string& file_md5) { file_md5_ = file_md5; }
const std::string& document_extension() const { return document_extension_; }
bool is_hosted_document() const { return is_hosted_document_; }
@@ -398,7 +411,6 @@ class GDataRootDirectory : public GDataDirectory {
void set_serialized_size(size_t size) { serialized_size_ = size; }
// GDataEntry implementation.
-
virtual GDataRootDirectory* AsGDataRootDirectory() OVERRIDE;
// Add the entry to resource map.
diff --git a/chrome/browser/chromeos/gdata/gdata_leveldb.cc b/chrome/browser/chromeos/gdata/gdata_leveldb.cc
new file mode 100644
index 0000000..1966627
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_leveldb.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/gdata/gdata_leveldb.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "chrome/browser/chromeos/gdata/gdata_files.h"
+#include "leveldb/write_batch.h"
+
+namespace gdata {
+namespace {
+
+const char kResourceIdPrefix[] = "id:";
+const char kPathPrefix[] = "path:";
+
+// Append prefix id: to |resource_id|.
+std::string ResourceIdToKey(const std::string& resource_id) {
+ return std::string(kResourceIdPrefix) + resource_id;
+}
+
+// Append prefix path: to |path|.
+std::string PathToKey(const FilePath& path) {
+ return std::string(kPathPrefix) + path.value();
+}
+
+GDataDB::Status GetStatus(const leveldb::Status& db_status) {
+ if (db_status.ok())
+ return GDataDB::DB_OK;
+
+ if (db_status.IsNotFound())
+ return GDataDB::DB_KEY_NOT_FOUND;
+
+ if (db_status.IsCorruption())
+ return GDataDB::DB_CORRUPTION;
+
+ if (db_status.IsIOError())
+ return GDataDB::DB_IO_ERROR;
+
+ NOTREACHED();
+ return GDataDB::DB_INTERNAL_ERROR;
+}
+
+} // namespace
+
+GDataLevelDB::GDataLevelDB() {
+}
+
+GDataLevelDB::~GDataLevelDB() {
+}
+
+void GDataLevelDB::Init(const FilePath& db_path) {
+ leveldb::DB* level_db = NULL;
+ leveldb::Options options;
+ options.create_if_missing = true;
+ leveldb::Status db_status = leveldb::DB::Open(options,
+ db_path.Append("level_db").value(), &level_db);
+ DCHECK(level_db);
+ // TODO(achuith): If db cannot be opened, we should try to recover it.
+ // If that fails, we should just delete it and create a new file.
+ DCHECK(db_status.ok());
+ level_db_.reset(level_db);
+}
+
+GDataDB::Status GDataLevelDB::Put(const GDataEntry& entry) {
+ // Write the serialized proto.
+ std::string serialized_proto;
+ entry.SerializeToString(&serialized_proto);
+
+ leveldb::WriteBatch batch;
+ const std::string resource_id_key =
+ ResourceIdToKey(entry.resource_id());
+ const std::string path_key = PathToKey(entry.GetFilePath());
+ batch.Put(leveldb::Slice(resource_id_key), leveldb::Slice(serialized_proto));
+ // Note we store the resource_id without prefix when it's the value.
+ batch.Put(leveldb::Slice(path_key), leveldb::Slice(entry.resource_id()));
+ leveldb::Status db_status = level_db_->Write(
+ leveldb::WriteOptions(),
+ &batch);
+
+ DVLOG(1) << "GDataLevelDB::Put resource_id key = " << resource_id_key
+ << ", path key = " << path_key;
+ return GetStatus(db_status);
+}
+
+GDataDB::Status GDataLevelDB::DeleteByResourceId(
+ const std::string& resource_id) {
+ scoped_ptr<GDataEntry> entry;
+ Status status = GetByResourceId(resource_id, &entry);
+ if (status == DB_KEY_NOT_FOUND)
+ return DB_OK;
+ else if (status != DB_OK)
+ return status;
+
+ leveldb::WriteBatch batch;
+ const std::string resource_id_key = ResourceIdToKey(resource_id);
+ const std::string path_key = PathToKey(entry->GetFilePath());
+ batch.Delete(leveldb::Slice(resource_id_key));
+ batch.Delete(leveldb::Slice(path_key));
+
+ leveldb::Status db_status = level_db_->Write(leveldb::WriteOptions(),
+ &batch);
+ return GetStatus(db_status);
+}
+
+GDataDB::Status GDataLevelDB::DeleteByPath(
+ const FilePath& path) {
+ std::string resource_id;
+ const Status status = ResourceIdForPath(path, &resource_id);
+ if (status != DB_OK)
+ return status;
+ return DeleteByResourceId(resource_id);
+}
+
+GDataDB::Status GDataLevelDB::GetByResourceId(const std::string& resource_id,
+ scoped_ptr<GDataEntry>* entry) {
+ entry->reset();
+ std::string serialized_proto;
+ const std::string resource_id_key = ResourceIdToKey(resource_id);
+ const leveldb::Status db_status = level_db_->Get(leveldb::ReadOptions(),
+ leveldb::Slice(resource_id_key), &serialized_proto);
+
+ if (db_status.IsNotFound())
+ return DB_KEY_NOT_FOUND;
+
+ if (db_status.ok()) {
+ DCHECK(!serialized_proto.empty());
+ *entry = GDataEntry::FromProtoString(serialized_proto);
+ DCHECK(entry->get());
+ return DB_OK;
+ }
+ return GetStatus(db_status);
+}
+
+GDataDB::Status GDataLevelDB::GetByPath(const FilePath& path,
+ scoped_ptr<GDataEntry>* entry) {
+ entry->reset();
+ std::string resource_id;
+ const Status status = ResourceIdForPath(path, &resource_id);
+ if (status != DB_OK)
+ return status;
+ return GetByResourceId(resource_id, entry);
+}
+
+GDataDB::Status GDataLevelDB::ResourceIdForPath(const FilePath& path,
+ std::string* resource_id) {
+ const std::string path_key = PathToKey(path);
+ const leveldb::Status db_status = level_db_->Get(
+ leveldb::ReadOptions(), path_key, resource_id);
+
+ return GetStatus(db_status);
+}
+
+scoped_ptr<GDataDBIter> GDataLevelDB::CreateIterator(const FilePath& path) {
+ return scoped_ptr<GDataDBIter>(new GDataLevelDBIter(
+ scoped_ptr<leveldb::Iterator>(
+ level_db_->NewIterator(leveldb::ReadOptions())),
+ this,
+ path));
+}
+
+GDataLevelDBIter::GDataLevelDBIter(scoped_ptr<leveldb::Iterator> level_db_iter,
+ GDataDB* db,
+ const FilePath& path)
+ : level_db_iter_(level_db_iter.Pass()),
+ db_(db),
+ path_(path) {
+ const std::string path_key = PathToKey(path);
+ level_db_iter_->Seek(leveldb::Slice(path_key));
+}
+
+GDataLevelDBIter::~GDataLevelDBIter() {
+}
+
+bool GDataLevelDBIter::GetNext(std::string* path,
+ scoped_ptr<GDataEntry>* entry) {
+ DCHECK(path);
+ DCHECK(entry);
+ path->clear();
+ entry->reset();
+
+ if (!level_db_iter_->Valid())
+ return false;
+
+ // Only consider keys under |path|.
+ const std::string path_key = PathToKey(path_);
+ leveldb::Slice key_slice(level_db_iter_->key());
+ if (!key_slice.starts_with(path_key))
+ return false;
+
+ GDataDB::Status status =
+ db_->GetByResourceId(level_db_iter_->value().ToString(), entry);
+ DCHECK_EQ(GDataDB::DB_OK, status);
+
+ key_slice.remove_prefix(sizeof(kPathPrefix) - 1);
+ path->assign(key_slice.ToString());
+
+ level_db_iter_->Next();
+ return true;
+}
+
+} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/gdata_leveldb.h b/chrome/browser/chromeos/gdata/gdata_leveldb.h
new file mode 100644
index 0000000..e2385cc
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_leveldb.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GDATA_GDATA_LEVELDB_H_
+#define CHROME_BROWSER_CHROMEOS_GDATA_GDATA_LEVELDB_H_
+#pragma once
+
+#include <leveldb/db.h>
+#include <string>
+
+#include "base/file_path.h"
+#include "chrome/browser/chromeos/gdata/gdata_db.h"
+
+namespace gdata {
+
+class GDataLevelDB : public GDataDB {
+ public:
+ GDataLevelDB();
+
+ void Init(const FilePath& db_path);
+
+ private:
+ virtual ~GDataLevelDB();
+
+ // GDataDB implementation.
+ virtual Status Put(const GDataEntry& file) OVERRIDE;
+ virtual Status DeleteByResourceId(const std::string& resource_id) OVERRIDE;
+ virtual Status DeleteByPath(const FilePath& path) OVERRIDE;
+ virtual Status GetByResourceId(const std::string& resource_id,
+ scoped_ptr<GDataEntry>* file) OVERRIDE;
+ virtual Status GetByPath(const FilePath& path,
+ scoped_ptr<GDataEntry>* file) OVERRIDE;
+ virtual scoped_ptr<GDataDBIter> CreateIterator(const FilePath& path) OVERRIDE;
+
+ // Returns |resource_id| for |path| by looking up path_db_.
+ Status ResourceIdForPath(const FilePath& path, std::string* resource_id);
+
+ scoped_ptr<leveldb::DB> level_db_;
+};
+
+class GDataLevelDBIter : public GDataDBIter {
+ public:
+ GDataLevelDBIter(scoped_ptr<leveldb::Iterator> level_db_iter,
+ GDataDB* db, const FilePath& path);
+ private:
+ virtual ~GDataLevelDBIter();
+
+ // GDataDBIter implementation.
+ virtual bool GetNext(std::string* path,
+ scoped_ptr<GDataEntry>* entry) OVERRIDE;
+
+ scoped_ptr<leveldb::Iterator> level_db_iter_;
+ GDataDB* db_;
+ const FilePath path_;
+};
+
+} // namespace gdata
+
+#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_LEVELDB_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 76845ad..3e3d25d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -509,6 +509,9 @@
'browser/chromeos/external_protocol_dialog.h',
'browser/chromeos/gdata/gdata_auth_service.cc',
'browser/chromeos/gdata/gdata_auth_service.h',
+ 'browser/chromeos/gdata/gdata_db.h',
+ 'browser/chromeos/gdata/gdata_db_factory.cc',
+ 'browser/chromeos/gdata/gdata_db_factory.h',
'browser/chromeos/gdata/gdata_documents_service.cc',
'browser/chromeos/gdata/gdata_documents_service.h',
'browser/chromeos/gdata/gdata_download_observer.cc',
@@ -520,6 +523,8 @@
'browser/chromeos/gdata/gdata_file_system_proxy.h',
'browser/chromeos/gdata/gdata_files.cc',
'browser/chromeos/gdata/gdata_files.h',
+ 'browser/chromeos/gdata/gdata_leveldb.cc',
+ 'browser/chromeos/gdata/gdata_leveldb.h',
'browser/chromeos/gdata/gdata_operation_registry.cc',
'browser/chromeos/gdata/gdata_operation_registry.h',
'browser/chromeos/gdata/gdata_operations.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 016a6bf..15cff58 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1203,6 +1203,7 @@
'browser/chromeos/dbus/proxy_resolution_service_provider_unittest.cc',
'browser/chromeos/extensions/file_browser_notifications_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc',
+ 'browser/chromeos/gdata/gdata_db_unittest.cc',
'browser/chromeos/gdata/gdata_file_system_unittest.cc',
'browser/chromeos/gdata/gdata_files_unittest.cc',
'browser/chromeos/gdata/gdata_operation_registry_unittest.cc',