| // 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 |