diff options
Diffstat (limited to 'chromium/google_apis/drive/gdata_wapi_parser.cc')
-rw-r--r-- | chromium/google_apis/drive/gdata_wapi_parser.cc | 883 |
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 |