summaryrefslogtreecommitdiff
path: root/chromium/google_apis/drive/gdata_wapi_parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/google_apis/drive/gdata_wapi_parser.cc')
-rw-r--r--chromium/google_apis/drive/gdata_wapi_parser.cc883
1 files changed, 883 insertions, 0 deletions
diff --git a/chromium/google_apis/drive/gdata_wapi_parser.cc b/chromium/google_apis/drive/gdata_wapi_parser.cc
new file mode 100644
index 00000000000..82f9d43151f
--- /dev/null
+++ b/chromium/google_apis/drive/gdata_wapi_parser.cc
@@ -0,0 +1,883 @@
+// 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 "google_apis/drive/gdata_wapi_parser.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/json/json_value_converter.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "google_apis/drive/time_util.h"
+
+using base::Value;
+using base::DictionaryValue;
+using base::ListValue;
+
+namespace google_apis {
+
+namespace {
+
+// Term values for kSchemeKind category:
+const char kTermPrefix[] = "http://schemas.google.com/docs/2007#";
+
+// Node names.
+const char kEntryNode[] = "entry";
+
+// Field names.
+const char kAuthorField[] = "author";
+const char kCategoryField[] = "category";
+const char kChangestampField[] = "docs$changestamp.value";
+const char kContentField[] = "content";
+const char kDeletedField[] = "gd$deleted";
+const char kETagField[] = "gd$etag";
+const char kEmailField[] = "email.$t";
+const char kEntryField[] = "entry";
+const char kFeedField[] = "feed";
+const char kFeedLinkField[] = "gd$feedLink";
+const char kFileNameField[] = "docs$filename.$t";
+const char kHrefField[] = "href";
+const char kIDField[] = "id.$t";
+const char kInstalledAppField[] = "docs$installedApp";
+const char kInstalledAppNameField[] = "docs$installedAppName";
+const char kInstalledAppIdField[] = "docs$installedAppId";
+const char kInstalledAppIconField[] = "docs$installedAppIcon";
+const char kInstalledAppIconCategoryField[] = "docs$installedAppIconCategory";
+const char kInstalledAppIconSizeField[] = "docs$installedAppIconSize";
+const char kInstalledAppObjectTypeField[] = "docs$installedAppObjectType";
+const char kInstalledAppPrimaryFileExtensionField[] =
+ "docs$installedAppPrimaryFileExtension";
+const char kInstalledAppPrimaryMimeTypeField[] =
+ "docs$installedAppPrimaryMimeType";
+const char kInstalledAppSecondaryFileExtensionField[] =
+ "docs$installedAppSecondaryFileExtension";
+const char kInstalledAppSecondaryMimeTypeField[] =
+ "docs$installedAppSecondaryMimeType";
+const char kInstalledAppSupportsCreateField[] =
+ "docs$installedAppSupportsCreate";
+const char kItemsPerPageField[] = "openSearch$itemsPerPage.$t";
+const char kLabelField[] = "label";
+const char kLargestChangestampField[] = "docs$largestChangestamp.value";
+const char kLastViewedField[] = "gd$lastViewed.$t";
+const char kLinkField[] = "link";
+const char kMD5Field[] = "docs$md5Checksum.$t";
+const char kNameField[] = "name.$t";
+const char kPublishedField[] = "published.$t";
+const char kQuotaBytesTotalField[] = "gd$quotaBytesTotal.$t";
+const char kQuotaBytesUsedField[] = "gd$quotaBytesUsed.$t";
+const char kRelField[] = "rel";
+const char kRemovedField[] = "docs$removed";
+const char kResourceIdField[] = "gd$resourceId.$t";
+const char kSchemeField[] = "scheme";
+const char kSizeField[] = "docs$size.$t";
+const char kSrcField[] = "src";
+const char kStartIndexField[] = "openSearch$startIndex.$t";
+const char kSuggestedFileNameField[] = "docs$suggestedFilename.$t";
+const char kTField[] = "$t";
+const char kTermField[] = "term";
+const char kTitleField[] = "title";
+const char kTitleTField[] = "title.$t";
+const char kTypeField[] = "type";
+const char kUpdatedField[] = "updated.$t";
+
+// Link Prefixes
+const char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-";
+const size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1;
+
+struct EntryKindMap {
+ DriveEntryKind kind;
+ const char* entry;
+ const char* extension;
+};
+
+const EntryKindMap kEntryKindMap[] = {
+ { ENTRY_KIND_UNKNOWN, "unknown", NULL},
+ { ENTRY_KIND_ITEM, "item", NULL},
+ { ENTRY_KIND_DOCUMENT, "document", ".gdoc"},
+ { ENTRY_KIND_SPREADSHEET, "spreadsheet", ".gsheet"},
+ { ENTRY_KIND_PRESENTATION, "presentation", ".gslides" },
+ { ENTRY_KIND_DRAWING, "drawing", ".gdraw"},
+ { ENTRY_KIND_TABLE, "table", ".gtable"},
+ { ENTRY_KIND_FORM, "form", ".gform"},
+ { ENTRY_KIND_EXTERNAL_APP, "externalapp", ".glink"},
+ { ENTRY_KIND_SITE, "site", NULL},
+ { ENTRY_KIND_FOLDER, "folder", NULL},
+ { ENTRY_KIND_FILE, "file", NULL},
+ { ENTRY_KIND_PDF, "pdf", NULL},
+};
+COMPILE_ASSERT(arraysize(kEntryKindMap) == ENTRY_KIND_MAX_VALUE,
+ EntryKindMap_and_DriveEntryKind_are_not_in_sync);
+
+struct LinkTypeMap {
+ Link::LinkType type;
+ const char* rel;
+};
+
+const LinkTypeMap kLinkTypeMap[] = {
+ { Link::LINK_SELF,
+ "self" },
+ { Link::LINK_NEXT,
+ "next" },
+ { Link::LINK_PARENT,
+ "http://schemas.google.com/docs/2007#parent" },
+ { Link::LINK_ALTERNATE,
+ "alternate"},
+ { Link::LINK_EDIT,
+ "edit" },
+ { Link::LINK_EDIT_MEDIA,
+ "edit-media" },
+ { Link::LINK_ALT_EDIT_MEDIA,
+ "http://schemas.google.com/docs/2007#alt-edit-media" },
+ { Link::LINK_ALT_POST,
+ "http://schemas.google.com/docs/2007#alt-post" },
+ { Link::LINK_FEED,
+ "http://schemas.google.com/g/2005#feed"},
+ { Link::LINK_POST,
+ "http://schemas.google.com/g/2005#post"},
+ { Link::LINK_BATCH,
+ "http://schemas.google.com/g/2005#batch"},
+ { Link::LINK_THUMBNAIL,
+ "http://schemas.google.com/docs/2007/thumbnail"},
+ { Link::LINK_RESUMABLE_EDIT_MEDIA,
+ "http://schemas.google.com/g/2005#resumable-edit-media"},
+ { Link::LINK_RESUMABLE_CREATE_MEDIA,
+ "http://schemas.google.com/g/2005#resumable-create-media"},
+ { Link::LINK_TABLES_FEED,
+ "http://schemas.google.com/spreadsheets/2006#tablesfeed"},
+ { Link::LINK_WORKSHEET_FEED,
+ "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"},
+ { Link::LINK_EMBED,
+ "http://schemas.google.com/docs/2007#embed"},
+ { Link::LINK_PRODUCT,
+ "http://schemas.google.com/docs/2007#product"},
+ { Link::LINK_ICON,
+ "http://schemas.google.com/docs/2007#icon"},
+ { Link::LINK_SHARE,
+ "http://schemas.google.com/docs/2007#share"},
+};
+
+struct ResourceLinkTypeMap {
+ ResourceLink::ResourceLinkType type;
+ const char* rel;
+};
+
+const ResourceLinkTypeMap kFeedLinkTypeMap[] = {
+ { ResourceLink::FEED_LINK_ACL,
+ "http://schemas.google.com/acl/2007#accessControlList" },
+ { ResourceLink::FEED_LINK_REVISIONS,
+ "http://schemas.google.com/docs/2007/revisions" },
+};
+
+struct CategoryTypeMap {
+ Category::CategoryType type;
+ const char* scheme;
+};
+
+const CategoryTypeMap kCategoryTypeMap[] = {
+ { Category::CATEGORY_KIND, "http://schemas.google.com/g/2005#kind" },
+ { Category::CATEGORY_LABEL, "http://schemas.google.com/g/2005/labels" },
+};
+
+struct AppIconCategoryMap {
+ AppIcon::IconCategory category;
+ const char* category_name;
+};
+
+const AppIconCategoryMap kAppIconCategoryMap[] = {
+ { AppIcon::ICON_DOCUMENT, "document" },
+ { AppIcon::ICON_APPLICATION, "application" },
+ { AppIcon::ICON_SHARED_DOCUMENT, "documentShared" },
+};
+
+// Converts |url_string| to |result|. Always returns true to be used
+// for JSONValueConverter::RegisterCustomField method.
+// TODO(mukai): make it return false in case of invalid |url_string|.
+bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
+ *result = GURL(url_string.as_string());
+ return true;
+}
+
+// Converts boolean string values like "true" into bool.
+bool GetBoolFromString(const base::StringPiece& value, bool* result) {
+ *result = (value == "true");
+ return true;
+}
+
+bool SortBySize(const InstalledApp::IconList::value_type& a,
+ const InstalledApp::IconList::value_type& b) {
+ return a.first < b.first;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Author implementation
+
+Author::Author() {
+}
+
+// static
+void Author::RegisterJSONConverter(
+ base::JSONValueConverter<Author>* converter) {
+ converter->RegisterStringField(kNameField, &Author::name_);
+ converter->RegisterStringField(kEmailField, &Author::email_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Link implementation
+
+Link::Link() : type_(Link::LINK_UNKNOWN) {
+}
+
+Link::~Link() {
+}
+
+// static
+bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) {
+ DCHECK(app_id);
+ // Fast return path if the link clearly isn't an OPEN_WITH link.
+ if (rel.size() < kOpenWithPrefixSize) {
+ app_id->clear();
+ return true;
+ }
+
+ const std::string kOpenWithPrefixStr(kOpenWithPrefix);
+ if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) {
+ *app_id = rel.as_string().substr(kOpenWithPrefixStr.size());
+ return true;
+ }
+
+ app_id->clear();
+ return true;
+}
+
+// static.
+bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) {
+ DCHECK(type);
+ for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) {
+ if (rel == kLinkTypeMap[i].rel) {
+ *type = kLinkTypeMap[i].type;
+ return true;
+ }
+ }
+
+ // OPEN_WITH links have extra information at the end of the rel that is unique
+ // for each one, so we can't just check the usual map. This check is slightly
+ // redundant to provide a quick skip if it's obviously not an OPEN_WITH url.
+ if (rel.size() >= kOpenWithPrefixSize &&
+ StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) {
+ *type = LINK_OPEN_WITH;
+ return true;
+ }
+
+ // Let unknown link types through, just report it; if the link type is needed
+ // in the future, add it into LinkType and kLinkTypeMap.
+ DVLOG(1) << "Ignoring unknown link type for rel " << rel;
+ *type = LINK_UNKNOWN;
+ return true;
+}
+
+// static
+void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) {
+ converter->RegisterCustomField<Link::LinkType>(kRelField,
+ &Link::type_,
+ &Link::GetLinkType);
+ // We have to register kRelField twice because we extract two different pieces
+ // of data from the same rel field.
+ converter->RegisterCustomField<std::string>(kRelField,
+ &Link::app_id_,
+ &Link::GetAppID);
+ converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString);
+ converter->RegisterStringField(kTitleField, &Link::title_);
+ converter->RegisterStringField(kTypeField, &Link::mime_type_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ResourceLink implementation
+
+ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN) {
+}
+
+// static.
+bool ResourceLink::GetFeedLinkType(
+ const base::StringPiece& rel, ResourceLink::ResourceLinkType* result) {
+ for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) {
+ if (rel == kFeedLinkTypeMap[i].rel) {
+ *result = kFeedLinkTypeMap[i].type;
+ return true;
+ }
+ }
+ DVLOG(1) << "Unknown feed link type for rel " << rel;
+ return false;
+}
+
+// static
+void ResourceLink::RegisterJSONConverter(
+ base::JSONValueConverter<ResourceLink>* converter) {
+ converter->RegisterCustomField<ResourceLink::ResourceLinkType>(
+ kRelField, &ResourceLink::type_, &ResourceLink::GetFeedLinkType);
+ converter->RegisterCustomField(
+ kHrefField, &ResourceLink::href_, &GetGURLFromString);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Category implementation
+
+Category::Category() : type_(CATEGORY_UNKNOWN) {
+}
+
+// Converts category.scheme into CategoryType enum.
+bool Category::GetCategoryTypeFromScheme(
+ const base::StringPiece& scheme, Category::CategoryType* result) {
+ for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) {
+ if (scheme == kCategoryTypeMap[i].scheme) {
+ *result = kCategoryTypeMap[i].type;
+ return true;
+ }
+ }
+ DVLOG(1) << "Unknown feed link type for scheme " << scheme;
+ return false;
+}
+
+// static
+void Category::RegisterJSONConverter(
+ base::JSONValueConverter<Category>* converter) {
+ converter->RegisterStringField(kLabelField, &Category::label_);
+ converter->RegisterCustomField<Category::CategoryType>(
+ kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme);
+ converter->RegisterStringField(kTermField, &Category::term_);
+}
+
+const Link* CommonMetadata::GetLinkByType(Link::LinkType type) const {
+ for (size_t i = 0; i < links_.size(); ++i) {
+ if (links_[i]->type() == type)
+ return links_[i];
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Content implementation
+
+Content::Content() {
+}
+
+// static
+void Content::RegisterJSONConverter(
+ base::JSONValueConverter<Content>* converter) {
+ converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString);
+ converter->RegisterStringField(kTypeField, &Content::mime_type_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppIcon implementation
+
+AppIcon::AppIcon() : category_(AppIcon::ICON_UNKNOWN), icon_side_length_(0) {
+}
+
+AppIcon::~AppIcon() {
+}
+
+// static
+void AppIcon::RegisterJSONConverter(
+ base::JSONValueConverter<AppIcon>* converter) {
+ converter->RegisterCustomField<AppIcon::IconCategory>(
+ kInstalledAppIconCategoryField,
+ &AppIcon::category_,
+ &AppIcon::GetIconCategory);
+ converter->RegisterCustomField<int>(kInstalledAppIconSizeField,
+ &AppIcon::icon_side_length_,
+ base::StringToInt);
+ converter->RegisterRepeatedMessage(kLinkField, &AppIcon::links_);
+}
+
+GURL AppIcon::GetIconURL() const {
+ for (size_t i = 0; i < links_.size(); ++i) {
+ if (links_[i]->type() == Link::LINK_ICON)
+ return links_[i]->href();
+ }
+ return GURL();
+}
+
+// static
+bool AppIcon::GetIconCategory(const base::StringPiece& category,
+ AppIcon::IconCategory* result) {
+ for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
+ if (category == kAppIconCategoryMap[i].category_name) {
+ *result = kAppIconCategoryMap[i].category;
+ return true;
+ }
+ }
+ DVLOG(1) << "Unknown icon category " << category;
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CommonMetadata implementation
+
+CommonMetadata::CommonMetadata() {
+}
+
+CommonMetadata::~CommonMetadata() {
+}
+
+// static
+template<typename CommonMetadataDescendant>
+void CommonMetadata::RegisterJSONConverter(
+ base::JSONValueConverter<CommonMetadataDescendant>* converter) {
+ converter->RegisterStringField(kETagField, &CommonMetadata::etag_);
+ converter->template RegisterRepeatedMessage<Author>(
+ kAuthorField, &CommonMetadata::authors_);
+ converter->template RegisterRepeatedMessage<Link>(
+ kLinkField, &CommonMetadata::links_);
+ converter->template RegisterRepeatedMessage<Category>(
+ kCategoryField, &CommonMetadata::categories_);
+ converter->template RegisterCustomField<base::Time>(
+ kUpdatedField, &CommonMetadata::updated_time_, &util::GetTimeFromString);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ResourceEntry implementation
+
+ResourceEntry::ResourceEntry()
+ : kind_(ENTRY_KIND_UNKNOWN),
+ file_size_(0),
+ deleted_(false),
+ removed_(false),
+ changestamp_(0),
+ image_width_(-1),
+ image_height_(-1),
+ image_rotation_(-1) {
+}
+
+ResourceEntry::~ResourceEntry() {
+}
+
+bool ResourceEntry::HasFieldPresent(const base::Value* value,
+ bool* result) {
+ *result = (value != NULL);
+ return true;
+}
+
+bool ResourceEntry::ParseChangestamp(const base::Value* value,
+ int64* result) {
+ DCHECK(result);
+ if (!value) {
+ *result = 0;
+ return true;
+ }
+
+ std::string string_value;
+ if (value->GetAsString(&string_value) &&
+ base::StringToInt64(string_value, result))
+ return true;
+
+ return false;
+}
+
+// static
+void ResourceEntry::RegisterJSONConverter(
+ base::JSONValueConverter<ResourceEntry>* converter) {
+ // Inherit the parent registrations.
+ CommonMetadata::RegisterJSONConverter(converter);
+ converter->RegisterStringField(
+ kResourceIdField, &ResourceEntry::resource_id_);
+ converter->RegisterStringField(kIDField, &ResourceEntry::id_);
+ converter->RegisterStringField(kTitleTField, &ResourceEntry::title_);
+ converter->RegisterCustomField<base::Time>(
+ kPublishedField, &ResourceEntry::published_time_,
+ &util::GetTimeFromString);
+ converter->RegisterCustomField<base::Time>(
+ kLastViewedField, &ResourceEntry::last_viewed_time_,
+ &util::GetTimeFromString);
+ converter->RegisterRepeatedMessage(
+ kFeedLinkField, &ResourceEntry::resource_links_);
+ converter->RegisterNestedField(kContentField, &ResourceEntry::content_);
+
+ // File properties. If the resource type is not a normal file, then
+ // that's no problem because those feed must not have these fields
+ // themselves, which does not report errors.
+ converter->RegisterStringField(kFileNameField, &ResourceEntry::filename_);
+ converter->RegisterStringField(kMD5Field, &ResourceEntry::file_md5_);
+ converter->RegisterCustomField<int64>(
+ kSizeField, &ResourceEntry::file_size_, &base::StringToInt64);
+ converter->RegisterStringField(
+ kSuggestedFileNameField, &ResourceEntry::suggested_filename_);
+ // Deleted are treated as 'trashed' items on web client side. Removed files
+ // are gone for good. We treat both cases as 'deleted' for this client.
+ converter->RegisterCustomValueField<bool>(
+ kDeletedField, &ResourceEntry::deleted_, &ResourceEntry::HasFieldPresent);
+ converter->RegisterCustomValueField<bool>(
+ kRemovedField, &ResourceEntry::removed_, &ResourceEntry::HasFieldPresent);
+ converter->RegisterCustomValueField<int64>(
+ kChangestampField, &ResourceEntry::changestamp_,
+ &ResourceEntry::ParseChangestamp);
+ // ImageMediaMetadata fields are not supported by WAPI.
+}
+
+std::string ResourceEntry::GetHostedDocumentExtension() const {
+ for (size_t i = 0; i < arraysize(kEntryKindMap); i++) {
+ if (kEntryKindMap[i].kind == kind_) {
+ if (kEntryKindMap[i].extension)
+ return std::string(kEntryKindMap[i].extension);
+ else
+ return std::string();
+ }
+ }
+ return std::string();
+}
+
+// static
+int ResourceEntry::ClassifyEntryKindByFileExtension(
+ const base::FilePath& file_path) {
+#if defined(OS_WIN)
+ std::string file_extension = WideToUTF8(file_path.Extension());
+#else
+ std::string file_extension = file_path.Extension();
+#endif
+ for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) {
+ const char* document_extension = kEntryKindMap[i].extension;
+ if (document_extension && file_extension == document_extension)
+ return ClassifyEntryKind(kEntryKindMap[i].kind);
+ }
+ return 0;
+}
+
+// static
+DriveEntryKind ResourceEntry::GetEntryKindFromTerm(
+ const std::string& term) {
+ if (!StartsWithASCII(term, kTermPrefix, false)) {
+ DVLOG(1) << "Unexpected term prefix term " << term;
+ return ENTRY_KIND_UNKNOWN;
+ }
+
+ std::string type = term.substr(strlen(kTermPrefix));
+ for (size_t i = 0; i < arraysize(kEntryKindMap); i++) {
+ if (type == kEntryKindMap[i].entry)
+ return kEntryKindMap[i].kind;
+ }
+ DVLOG(1) << "Unknown entry type for term " << term << ", type " << type;
+ return ENTRY_KIND_UNKNOWN;
+}
+
+// static
+int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind) {
+ int classes = 0;
+
+ // All DriveEntryKind members are listed here, so the compiler catches if a
+ // newly added member is missing here.
+ switch (kind) {
+ case ENTRY_KIND_UNKNOWN:
+ // Special entries.
+ case ENTRY_KIND_ITEM:
+ case ENTRY_KIND_SITE:
+ break;
+
+ // Hosted Google document.
+ case ENTRY_KIND_DOCUMENT:
+ case ENTRY_KIND_SPREADSHEET:
+ case ENTRY_KIND_PRESENTATION:
+ case ENTRY_KIND_DRAWING:
+ case ENTRY_KIND_TABLE:
+ case ENTRY_KIND_FORM:
+ classes = KIND_OF_GOOGLE_DOCUMENT | KIND_OF_HOSTED_DOCUMENT;
+ break;
+
+ // Hosted external application document.
+ case ENTRY_KIND_EXTERNAL_APP:
+ classes = KIND_OF_EXTERNAL_DOCUMENT | KIND_OF_HOSTED_DOCUMENT;
+ break;
+
+ // Folders, collections.
+ case ENTRY_KIND_FOLDER:
+ classes = KIND_OF_FOLDER;
+ break;
+
+ // Regular files.
+ case ENTRY_KIND_FILE:
+ case ENTRY_KIND_PDF:
+ classes = KIND_OF_FILE;
+ break;
+
+ case ENTRY_KIND_MAX_VALUE:
+ NOTREACHED();
+ }
+
+ return classes;
+}
+
+void ResourceEntry::FillRemainingFields() {
+ // Set |kind_| and |labels_| based on the |categories_| in the class.
+ // JSONValueConverter does not have the ability to catch an element in a list
+ // based on a predicate. Thus we need to iterate over |categories_| and
+ // find the elements to set these fields as a post-process.
+ for (size_t i = 0; i < categories_.size(); ++i) {
+ const Category* category = categories_[i];
+ if (category->type() == Category::CATEGORY_KIND)
+ kind_ = GetEntryKindFromTerm(category->term());
+ else if (category->type() == Category::CATEGORY_LABEL)
+ labels_.push_back(category->label());
+ }
+}
+
+// static
+scoped_ptr<ResourceEntry> ResourceEntry::ExtractAndParse(
+ const base::Value& value) {
+ const base::DictionaryValue* as_dict = NULL;
+ const base::DictionaryValue* entry_dict = NULL;
+ if (value.GetAsDictionary(&as_dict) &&
+ as_dict->GetDictionary(kEntryField, &entry_dict)) {
+ return ResourceEntry::CreateFrom(*entry_dict);
+ }
+ return scoped_ptr<ResourceEntry>();
+}
+
+// static
+scoped_ptr<ResourceEntry> ResourceEntry::CreateFrom(const base::Value& value) {
+ base::JSONValueConverter<ResourceEntry> converter;
+ scoped_ptr<ResourceEntry> entry(new ResourceEntry());
+ if (!converter.Convert(value, entry.get())) {
+ DVLOG(1) << "Invalid resource entry!";
+ return scoped_ptr<ResourceEntry>();
+ }
+
+ entry->FillRemainingFields();
+ return entry.Pass();
+}
+
+// static
+std::string ResourceEntry::GetEntryNodeName() {
+ return kEntryNode;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ResourceList implementation
+
+ResourceList::ResourceList()
+ : start_index_(0),
+ items_per_page_(0),
+ largest_changestamp_(0) {
+}
+
+ResourceList::~ResourceList() {
+}
+
+// static
+void ResourceList::RegisterJSONConverter(
+ base::JSONValueConverter<ResourceList>* converter) {
+ // inheritance
+ CommonMetadata::RegisterJSONConverter(converter);
+ // TODO(zelidrag): Once we figure out where these will be used, we should
+ // check for valid start_index_ and items_per_page_ values.
+ converter->RegisterCustomField<int>(
+ kStartIndexField, &ResourceList::start_index_, &base::StringToInt);
+ converter->RegisterCustomField<int>(
+ kItemsPerPageField, &ResourceList::items_per_page_, &base::StringToInt);
+ converter->RegisterStringField(kTitleTField, &ResourceList::title_);
+ converter->RegisterRepeatedMessage(kEntryField, &ResourceList::entries_);
+ converter->RegisterCustomField<int64>(
+ kLargestChangestampField, &ResourceList::largest_changestamp_,
+ &base::StringToInt64);
+}
+
+bool ResourceList::Parse(const base::Value& value) {
+ base::JSONValueConverter<ResourceList> converter;
+ if (!converter.Convert(value, this)) {
+ DVLOG(1) << "Invalid resource list!";
+ return false;
+ }
+
+ ScopedVector<ResourceEntry>::iterator iter = entries_.begin();
+ while (iter != entries_.end()) {
+ ResourceEntry* entry = (*iter);
+ entry->FillRemainingFields();
+ ++iter;
+ }
+ return true;
+}
+
+// static
+scoped_ptr<ResourceList> ResourceList::ExtractAndParse(
+ const base::Value& value) {
+ const base::DictionaryValue* as_dict = NULL;
+ const base::DictionaryValue* feed_dict = NULL;
+ if (value.GetAsDictionary(&as_dict) &&
+ as_dict->GetDictionary(kFeedField, &feed_dict)) {
+ return ResourceList::CreateFrom(*feed_dict);
+ }
+ return scoped_ptr<ResourceList>();
+}
+
+// static
+scoped_ptr<ResourceList> ResourceList::CreateFrom(const base::Value& value) {
+ scoped_ptr<ResourceList> feed(new ResourceList());
+ if (!feed->Parse(value)) {
+ DVLOG(1) << "Invalid resource list!";
+ return scoped_ptr<ResourceList>();
+ }
+
+ return feed.Pass();
+}
+
+bool ResourceList::GetNextFeedURL(GURL* url) const {
+ DCHECK(url);
+ for (size_t i = 0; i < links_.size(); ++i) {
+ if (links_[i]->type() == Link::LINK_NEXT) {
+ *url = links_[i]->href();
+ return true;
+ }
+ }
+ return false;
+}
+
+void ResourceList::ReleaseEntries(std::vector<ResourceEntry*>* entries) {
+ entries_.release(entries);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// InstalledApp implementation
+
+InstalledApp::InstalledApp() : supports_create_(false) {
+}
+
+InstalledApp::~InstalledApp() {
+}
+
+InstalledApp::IconList InstalledApp::GetIconsForCategory(
+ AppIcon::IconCategory category) const {
+ IconList result;
+
+ for (ScopedVector<AppIcon>::const_iterator icon_iter = app_icons_.begin();
+ icon_iter != app_icons_.end(); ++icon_iter) {
+ if ((*icon_iter)->category() != category)
+ continue;
+ GURL icon_url = (*icon_iter)->GetIconURL();
+ if (icon_url.is_empty())
+ continue;
+ result.push_back(std::make_pair((*icon_iter)->icon_side_length(),
+ icon_url));
+ }
+
+ // Return a sorted list, smallest to largest.
+ std::sort(result.begin(), result.end(), SortBySize);
+ return result;
+}
+
+GURL InstalledApp::GetProductUrl() const {
+ for (ScopedVector<Link>::const_iterator it = links_.begin();
+ it != links_.end(); ++it) {
+ const Link* link = *it;
+ if (link->type() == Link::LINK_PRODUCT)
+ return link->href();
+ }
+ return GURL();
+}
+
+// static
+bool InstalledApp::GetValueString(const base::Value* value,
+ std::string* result) {
+ const base::DictionaryValue* dict = NULL;
+ if (!value->GetAsDictionary(&dict))
+ return false;
+
+ if (!dict->GetString(kTField, result))
+ return false;
+
+ return true;
+}
+
+// static
+void InstalledApp::RegisterJSONConverter(
+ base::JSONValueConverter<InstalledApp>* converter) {
+ converter->RegisterRepeatedMessage(kInstalledAppIconField,
+ &InstalledApp::app_icons_);
+ converter->RegisterStringField(kInstalledAppIdField,
+ &InstalledApp::app_id_);
+ converter->RegisterStringField(kInstalledAppNameField,
+ &InstalledApp::app_name_);
+ converter->RegisterStringField(kInstalledAppObjectTypeField,
+ &InstalledApp::object_type_);
+ converter->RegisterCustomField<bool>(kInstalledAppSupportsCreateField,
+ &InstalledApp::supports_create_,
+ &GetBoolFromString);
+ converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField,
+ &InstalledApp::primary_mimetypes_,
+ &GetValueString);
+ converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField,
+ &InstalledApp::secondary_mimetypes_,
+ &GetValueString);
+ converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField,
+ &InstalledApp::primary_extensions_,
+ &GetValueString);
+ converter->RegisterRepeatedCustomValue(
+ kInstalledAppSecondaryFileExtensionField,
+ &InstalledApp::secondary_extensions_,
+ &GetValueString);
+ converter->RegisterRepeatedMessage(kLinkField, &InstalledApp::links_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AccountMetadata implementation
+
+AccountMetadata::AccountMetadata()
+ : quota_bytes_total_(0),
+ quota_bytes_used_(0),
+ largest_changestamp_(0) {
+}
+
+AccountMetadata::~AccountMetadata() {
+}
+
+// static
+void AccountMetadata::RegisterJSONConverter(
+ base::JSONValueConverter<AccountMetadata>* converter) {
+ converter->RegisterCustomField<int64>(
+ kQuotaBytesTotalField,
+ &AccountMetadata::quota_bytes_total_,
+ &base::StringToInt64);
+ converter->RegisterCustomField<int64>(
+ kQuotaBytesUsedField,
+ &AccountMetadata::quota_bytes_used_,
+ &base::StringToInt64);
+ converter->RegisterCustomField<int64>(
+ kLargestChangestampField,
+ &AccountMetadata::largest_changestamp_,
+ &base::StringToInt64);
+ converter->RegisterRepeatedMessage(kInstalledAppField,
+ &AccountMetadata::installed_apps_);
+}
+
+// static
+scoped_ptr<AccountMetadata> AccountMetadata::CreateFrom(
+ const base::Value& value) {
+ scoped_ptr<AccountMetadata> metadata(new AccountMetadata());
+ const base::DictionaryValue* dictionary = NULL;
+ const base::Value* entry = NULL;
+ if (!value.GetAsDictionary(&dictionary) ||
+ !dictionary->Get(kEntryField, &entry) ||
+ !metadata->Parse(*entry)) {
+ LOG(ERROR) << "Unable to create: Invalid account metadata feed!";
+ return scoped_ptr<AccountMetadata>();
+ }
+
+ return metadata.Pass();
+}
+
+bool AccountMetadata::Parse(const base::Value& value) {
+ base::JSONValueConverter<AccountMetadata> converter;
+ if (!converter.Convert(value, this)) {
+ LOG(ERROR) << "Unable to parse: Invalid account metadata feed!";
+ return false;
+ }
+ return true;
+}
+
+} // namespace google_apis