1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "google_apis/drive/gdata_wapi_parser.h"
10 #include "base/basictypes.h"
11 #include "base/files/file_path.h"
12 #include "base/json/json_value_converter.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "google_apis/drive/time_util.h"
22 using base::DictionaryValue
;
23 using base::ListValue
;
25 namespace google_apis
{
29 // Term values for kSchemeKind category:
30 const char kTermPrefix
[] = "http://schemas.google.com/docs/2007#";
33 const char kEntryNode
[] = "entry";
36 const char kAuthorField
[] = "author";
37 const char kCategoryField
[] = "category";
38 const char kChangestampField
[] = "docs$changestamp.value";
39 const char kContentField
[] = "content";
40 const char kDeletedField
[] = "gd$deleted";
41 const char kETagField
[] = "gd$etag";
42 const char kEmailField
[] = "email.$t";
43 const char kEntryField
[] = "entry";
44 const char kFeedField
[] = "feed";
45 const char kFeedLinkField
[] = "gd$feedLink";
46 const char kFileNameField
[] = "docs$filename.$t";
47 const char kHrefField
[] = "href";
48 const char kIDField
[] = "id.$t";
49 const char kInstalledAppField
[] = "docs$installedApp";
50 const char kInstalledAppNameField
[] = "docs$installedAppName";
51 const char kInstalledAppIdField
[] = "docs$installedAppId";
52 const char kInstalledAppIconField
[] = "docs$installedAppIcon";
53 const char kInstalledAppIconCategoryField
[] = "docs$installedAppIconCategory";
54 const char kInstalledAppIconSizeField
[] = "docs$installedAppIconSize";
55 const char kInstalledAppObjectTypeField
[] = "docs$installedAppObjectType";
56 const char kInstalledAppPrimaryFileExtensionField
[] =
57 "docs$installedAppPrimaryFileExtension";
58 const char kInstalledAppPrimaryMimeTypeField
[] =
59 "docs$installedAppPrimaryMimeType";
60 const char kInstalledAppSecondaryFileExtensionField
[] =
61 "docs$installedAppSecondaryFileExtension";
62 const char kInstalledAppSecondaryMimeTypeField
[] =
63 "docs$installedAppSecondaryMimeType";
64 const char kInstalledAppSupportsCreateField
[] =
65 "docs$installedAppSupportsCreate";
66 const char kItemsPerPageField
[] = "openSearch$itemsPerPage.$t";
67 const char kLabelField
[] = "label";
68 const char kLargestChangestampField
[] = "docs$largestChangestamp.value";
69 const char kLastViewedField
[] = "gd$lastViewed.$t";
70 const char kLinkField
[] = "link";
71 const char kMD5Field
[] = "docs$md5Checksum.$t";
72 const char kNameField
[] = "name.$t";
73 const char kPublishedField
[] = "published.$t";
74 const char kQuotaBytesTotalField
[] = "gd$quotaBytesTotal.$t";
75 const char kQuotaBytesUsedField
[] = "gd$quotaBytesUsed.$t";
76 const char kRelField
[] = "rel";
77 const char kRemovedField
[] = "docs$removed";
78 const char kResourceIdField
[] = "gd$resourceId.$t";
79 const char kSchemeField
[] = "scheme";
80 const char kSizeField
[] = "docs$size.$t";
81 const char kSrcField
[] = "src";
82 const char kStartIndexField
[] = "openSearch$startIndex.$t";
83 const char kSuggestedFileNameField
[] = "docs$suggestedFilename.$t";
84 const char kTField
[] = "$t";
85 const char kTermField
[] = "term";
86 const char kTitleField
[] = "title";
87 const char kTitleTField
[] = "title.$t";
88 const char kTypeField
[] = "type";
89 const char kUpdatedField
[] = "updated.$t";
92 const char kOpenWithPrefix
[] = "http://schemas.google.com/docs/2007#open-with-";
93 const size_t kOpenWithPrefixSize
= arraysize(kOpenWithPrefix
) - 1;
98 const char* extension
;
101 const EntryKindMap kEntryKindMap
[] = {
102 { ENTRY_KIND_UNKNOWN
, "unknown", NULL
},
103 { ENTRY_KIND_ITEM
, "item", NULL
},
104 { ENTRY_KIND_DOCUMENT
, "document", ".gdoc"},
105 { ENTRY_KIND_SPREADSHEET
, "spreadsheet", ".gsheet"},
106 { ENTRY_KIND_PRESENTATION
, "presentation", ".gslides" },
107 { ENTRY_KIND_DRAWING
, "drawing", ".gdraw"},
108 { ENTRY_KIND_TABLE
, "table", ".gtable"},
109 { ENTRY_KIND_FORM
, "form", ".gform"},
110 { ENTRY_KIND_EXTERNAL_APP
, "externalapp", ".glink"},
111 { ENTRY_KIND_SITE
, "site", NULL
},
112 { ENTRY_KIND_FOLDER
, "folder", NULL
},
113 { ENTRY_KIND_FILE
, "file", NULL
},
114 { ENTRY_KIND_PDF
, "pdf", NULL
},
116 COMPILE_ASSERT(arraysize(kEntryKindMap
) == ENTRY_KIND_MAX_VALUE
,
117 EntryKindMap_and_DriveEntryKind_are_not_in_sync
);
124 const LinkTypeMap kLinkTypeMap
[] = {
130 "http://schemas.google.com/docs/2007#parent" },
131 { Link::LINK_ALTERNATE
,
135 { Link::LINK_EDIT_MEDIA
,
137 { Link::LINK_ALT_EDIT_MEDIA
,
138 "http://schemas.google.com/docs/2007#alt-edit-media" },
139 { Link::LINK_ALT_POST
,
140 "http://schemas.google.com/docs/2007#alt-post" },
142 "http://schemas.google.com/g/2005#feed"},
144 "http://schemas.google.com/g/2005#post"},
146 "http://schemas.google.com/g/2005#batch"},
147 { Link::LINK_THUMBNAIL
,
148 "http://schemas.google.com/docs/2007/thumbnail"},
149 { Link::LINK_RESUMABLE_EDIT_MEDIA
,
150 "http://schemas.google.com/g/2005#resumable-edit-media"},
151 { Link::LINK_RESUMABLE_CREATE_MEDIA
,
152 "http://schemas.google.com/g/2005#resumable-create-media"},
153 { Link::LINK_TABLES_FEED
,
154 "http://schemas.google.com/spreadsheets/2006#tablesfeed"},
155 { Link::LINK_WORKSHEET_FEED
,
156 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"},
158 "http://schemas.google.com/docs/2007#embed"},
159 { Link::LINK_PRODUCT
,
160 "http://schemas.google.com/docs/2007#product"},
162 "http://schemas.google.com/docs/2007#icon"},
164 "http://schemas.google.com/docs/2007#share"},
167 struct ResourceLinkTypeMap
{
168 ResourceLink::ResourceLinkType type
;
172 const ResourceLinkTypeMap kFeedLinkTypeMap
[] = {
173 { ResourceLink::FEED_LINK_ACL
,
174 "http://schemas.google.com/acl/2007#accessControlList" },
175 { ResourceLink::FEED_LINK_REVISIONS
,
176 "http://schemas.google.com/docs/2007/revisions" },
179 struct CategoryTypeMap
{
180 Category::CategoryType type
;
184 const CategoryTypeMap kCategoryTypeMap
[] = {
185 { Category::CATEGORY_KIND
, "http://schemas.google.com/g/2005#kind" },
186 { Category::CATEGORY_LABEL
, "http://schemas.google.com/g/2005/labels" },
189 struct AppIconCategoryMap
{
190 AppIcon::IconCategory category
;
191 const char* category_name
;
194 const AppIconCategoryMap kAppIconCategoryMap
[] = {
195 { AppIcon::ICON_DOCUMENT
, "document" },
196 { AppIcon::ICON_APPLICATION
, "application" },
197 { AppIcon::ICON_SHARED_DOCUMENT
, "documentShared" },
200 // Converts |url_string| to |result|. Always returns true to be used
201 // for JSONValueConverter::RegisterCustomField method.
202 // TODO(mukai): make it return false in case of invalid |url_string|.
203 bool GetGURLFromString(const base::StringPiece
& url_string
, GURL
* result
) {
204 *result
= GURL(url_string
.as_string());
208 // Converts boolean string values like "true" into bool.
209 bool GetBoolFromString(const base::StringPiece
& value
, bool* result
) {
210 *result
= (value
== "true");
214 bool SortBySize(const InstalledApp::IconList::value_type
& a
,
215 const InstalledApp::IconList::value_type
& b
) {
216 return a
.first
< b
.first
;
221 ////////////////////////////////////////////////////////////////////////////////
222 // Author implementation
228 void Author::RegisterJSONConverter(
229 base::JSONValueConverter
<Author
>* converter
) {
230 converter
->RegisterStringField(kNameField
, &Author::name_
);
231 converter
->RegisterStringField(kEmailField
, &Author::email_
);
234 ////////////////////////////////////////////////////////////////////////////////
235 // Link implementation
237 Link::Link() : type_(Link::LINK_UNKNOWN
) {
244 bool Link::GetAppID(const base::StringPiece
& rel
, std::string
* app_id
) {
246 // Fast return path if the link clearly isn't an OPEN_WITH link.
247 if (rel
.size() < kOpenWithPrefixSize
) {
252 const std::string
kOpenWithPrefixStr(kOpenWithPrefix
);
253 if (StartsWithASCII(rel
.as_string(), kOpenWithPrefixStr
, false)) {
254 *app_id
= rel
.as_string().substr(kOpenWithPrefixStr
.size());
263 bool Link::GetLinkType(const base::StringPiece
& rel
, Link::LinkType
* type
) {
265 for (size_t i
= 0; i
< arraysize(kLinkTypeMap
); i
++) {
266 if (rel
== kLinkTypeMap
[i
].rel
) {
267 *type
= kLinkTypeMap
[i
].type
;
272 // OPEN_WITH links have extra information at the end of the rel that is unique
273 // for each one, so we can't just check the usual map. This check is slightly
274 // redundant to provide a quick skip if it's obviously not an OPEN_WITH url.
275 if (rel
.size() >= kOpenWithPrefixSize
&&
276 StartsWithASCII(rel
.as_string(), kOpenWithPrefix
, false)) {
277 *type
= LINK_OPEN_WITH
;
281 // Let unknown link types through, just report it; if the link type is needed
282 // in the future, add it into LinkType and kLinkTypeMap.
283 DVLOG(1) << "Ignoring unknown link type for rel " << rel
;
284 *type
= LINK_UNKNOWN
;
289 void Link::RegisterJSONConverter(base::JSONValueConverter
<Link
>* converter
) {
290 converter
->RegisterCustomField
<Link::LinkType
>(kRelField
,
293 // We have to register kRelField twice because we extract two different pieces
294 // of data from the same rel field.
295 converter
->RegisterCustomField
<std::string
>(kRelField
,
298 converter
->RegisterCustomField(kHrefField
, &Link::href_
, &GetGURLFromString
);
299 converter
->RegisterStringField(kTitleField
, &Link::title_
);
300 converter
->RegisterStringField(kTypeField
, &Link::mime_type_
);
303 ////////////////////////////////////////////////////////////////////////////////
304 // ResourceLink implementation
306 ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN
) {
310 bool ResourceLink::GetFeedLinkType(
311 const base::StringPiece
& rel
, ResourceLink::ResourceLinkType
* result
) {
312 for (size_t i
= 0; i
< arraysize(kFeedLinkTypeMap
); i
++) {
313 if (rel
== kFeedLinkTypeMap
[i
].rel
) {
314 *result
= kFeedLinkTypeMap
[i
].type
;
318 DVLOG(1) << "Unknown feed link type for rel " << rel
;
323 void ResourceLink::RegisterJSONConverter(
324 base::JSONValueConverter
<ResourceLink
>* converter
) {
325 converter
->RegisterCustomField
<ResourceLink::ResourceLinkType
>(
326 kRelField
, &ResourceLink::type_
, &ResourceLink::GetFeedLinkType
);
327 converter
->RegisterCustomField(
328 kHrefField
, &ResourceLink::href_
, &GetGURLFromString
);
331 ////////////////////////////////////////////////////////////////////////////////
332 // Category implementation
334 Category::Category() : type_(CATEGORY_UNKNOWN
) {
337 // Converts category.scheme into CategoryType enum.
338 bool Category::GetCategoryTypeFromScheme(
339 const base::StringPiece
& scheme
, Category::CategoryType
* result
) {
340 for (size_t i
= 0; i
< arraysize(kCategoryTypeMap
); i
++) {
341 if (scheme
== kCategoryTypeMap
[i
].scheme
) {
342 *result
= kCategoryTypeMap
[i
].type
;
346 DVLOG(1) << "Unknown feed link type for scheme " << scheme
;
351 void Category::RegisterJSONConverter(
352 base::JSONValueConverter
<Category
>* converter
) {
353 converter
->RegisterStringField(kLabelField
, &Category::label_
);
354 converter
->RegisterCustomField
<Category::CategoryType
>(
355 kSchemeField
, &Category::type_
, &Category::GetCategoryTypeFromScheme
);
356 converter
->RegisterStringField(kTermField
, &Category::term_
);
359 const Link
* CommonMetadata::GetLinkByType(Link::LinkType type
) const {
360 for (size_t i
= 0; i
< links_
.size(); ++i
) {
361 if (links_
[i
]->type() == type
)
367 ////////////////////////////////////////////////////////////////////////////////
368 // Content implementation
374 void Content::RegisterJSONConverter(
375 base::JSONValueConverter
<Content
>* converter
) {
376 converter
->RegisterCustomField(kSrcField
, &Content::url_
, &GetGURLFromString
);
377 converter
->RegisterStringField(kTypeField
, &Content::mime_type_
);
380 ////////////////////////////////////////////////////////////////////////////////
381 // AppIcon implementation
383 AppIcon::AppIcon() : category_(AppIcon::ICON_UNKNOWN
), icon_side_length_(0) {
386 AppIcon::~AppIcon() {
390 void AppIcon::RegisterJSONConverter(
391 base::JSONValueConverter
<AppIcon
>* converter
) {
392 converter
->RegisterCustomField
<AppIcon::IconCategory
>(
393 kInstalledAppIconCategoryField
,
395 &AppIcon::GetIconCategory
);
396 converter
->RegisterCustomField
<int>(kInstalledAppIconSizeField
,
397 &AppIcon::icon_side_length_
,
399 converter
->RegisterRepeatedMessage(kLinkField
, &AppIcon::links_
);
402 GURL
AppIcon::GetIconURL() const {
403 for (size_t i
= 0; i
< links_
.size(); ++i
) {
404 if (links_
[i
]->type() == Link::LINK_ICON
)
405 return links_
[i
]->href();
411 bool AppIcon::GetIconCategory(const base::StringPiece
& category
,
412 AppIcon::IconCategory
* result
) {
413 for (size_t i
= 0; i
< arraysize(kAppIconCategoryMap
); i
++) {
414 if (category
== kAppIconCategoryMap
[i
].category_name
) {
415 *result
= kAppIconCategoryMap
[i
].category
;
419 DVLOG(1) << "Unknown icon category " << category
;
423 ////////////////////////////////////////////////////////////////////////////////
424 // CommonMetadata implementation
426 CommonMetadata::CommonMetadata() {
429 CommonMetadata::~CommonMetadata() {
433 template<typename CommonMetadataDescendant
>
434 void CommonMetadata::RegisterJSONConverter(
435 base::JSONValueConverter
<CommonMetadataDescendant
>* converter
) {
436 converter
->RegisterStringField(kETagField
, &CommonMetadata::etag_
);
437 converter
->template RegisterRepeatedMessage
<Author
>(
438 kAuthorField
, &CommonMetadata::authors_
);
439 converter
->template RegisterRepeatedMessage
<Link
>(
440 kLinkField
, &CommonMetadata::links_
);
441 converter
->template RegisterRepeatedMessage
<Category
>(
442 kCategoryField
, &CommonMetadata::categories_
);
443 converter
->template RegisterCustomField
<base::Time
>(
444 kUpdatedField
, &CommonMetadata::updated_time_
, &util::GetTimeFromString
);
447 ////////////////////////////////////////////////////////////////////////////////
448 // ResourceEntry implementation
450 ResourceEntry::ResourceEntry()
451 : kind_(ENTRY_KIND_UNKNOWN
),
458 image_rotation_(-1) {
461 ResourceEntry::~ResourceEntry() {
464 bool ResourceEntry::HasFieldPresent(const base::Value
* value
,
466 *result
= (value
!= NULL
);
470 bool ResourceEntry::ParseChangestamp(const base::Value
* value
,
478 std::string string_value
;
479 if (value
->GetAsString(&string_value
) &&
480 base::StringToInt64(string_value
, result
))
487 void ResourceEntry::RegisterJSONConverter(
488 base::JSONValueConverter
<ResourceEntry
>* converter
) {
489 // Inherit the parent registrations.
490 CommonMetadata::RegisterJSONConverter(converter
);
491 converter
->RegisterStringField(
492 kResourceIdField
, &ResourceEntry::resource_id_
);
493 converter
->RegisterStringField(kIDField
, &ResourceEntry::id_
);
494 converter
->RegisterStringField(kTitleTField
, &ResourceEntry::title_
);
495 converter
->RegisterCustomField
<base::Time
>(
496 kPublishedField
, &ResourceEntry::published_time_
,
497 &util::GetTimeFromString
);
498 converter
->RegisterCustomField
<base::Time
>(
499 kLastViewedField
, &ResourceEntry::last_viewed_time_
,
500 &util::GetTimeFromString
);
501 converter
->RegisterRepeatedMessage(
502 kFeedLinkField
, &ResourceEntry::resource_links_
);
503 converter
->RegisterNestedField(kContentField
, &ResourceEntry::content_
);
505 // File properties. If the resource type is not a normal file, then
506 // that's no problem because those feed must not have these fields
507 // themselves, which does not report errors.
508 converter
->RegisterStringField(kFileNameField
, &ResourceEntry::filename_
);
509 converter
->RegisterStringField(kMD5Field
, &ResourceEntry::file_md5_
);
510 converter
->RegisterCustomField
<int64
>(
511 kSizeField
, &ResourceEntry::file_size_
, &base::StringToInt64
);
512 converter
->RegisterStringField(
513 kSuggestedFileNameField
, &ResourceEntry::suggested_filename_
);
514 // Deleted are treated as 'trashed' items on web client side. Removed files
515 // are gone for good. We treat both cases as 'deleted' for this client.
516 converter
->RegisterCustomValueField
<bool>(
517 kDeletedField
, &ResourceEntry::deleted_
, &ResourceEntry::HasFieldPresent
);
518 converter
->RegisterCustomValueField
<bool>(
519 kRemovedField
, &ResourceEntry::removed_
, &ResourceEntry::HasFieldPresent
);
520 converter
->RegisterCustomValueField
<int64
>(
521 kChangestampField
, &ResourceEntry::changestamp_
,
522 &ResourceEntry::ParseChangestamp
);
523 // ImageMediaMetadata fields are not supported by WAPI.
526 std::string
ResourceEntry::GetHostedDocumentExtension() const {
527 for (size_t i
= 0; i
< arraysize(kEntryKindMap
); i
++) {
528 if (kEntryKindMap
[i
].kind
== kind_
) {
529 if (kEntryKindMap
[i
].extension
)
530 return std::string(kEntryKindMap
[i
].extension
);
532 return std::string();
535 return std::string();
539 DriveEntryKind
ResourceEntry::GetEntryKindFromExtension(
540 const std::string
& extension
) {
541 for (size_t i
= 0; i
< arraysize(kEntryKindMap
); ++i
) {
542 const char* document_extension
= kEntryKindMap
[i
].extension
;
543 if (document_extension
&& extension
== document_extension
)
544 return kEntryKindMap
[i
].kind
;
546 return ENTRY_KIND_UNKNOWN
;
550 int ResourceEntry::ClassifyEntryKindByFileExtension(
551 const base::FilePath
& file_path
) {
553 std::string file_extension
= base::WideToUTF8(file_path
.Extension());
555 std::string file_extension
= file_path
.Extension();
557 return ClassifyEntryKind(GetEntryKindFromExtension(file_extension
));
561 DriveEntryKind
ResourceEntry::GetEntryKindFromTerm(
562 const std::string
& term
) {
563 if (!StartsWithASCII(term
, kTermPrefix
, false)) {
564 DVLOG(1) << "Unexpected term prefix term " << term
;
565 return ENTRY_KIND_UNKNOWN
;
568 std::string type
= term
.substr(strlen(kTermPrefix
));
569 for (size_t i
= 0; i
< arraysize(kEntryKindMap
); i
++) {
570 if (type
== kEntryKindMap
[i
].entry
)
571 return kEntryKindMap
[i
].kind
;
573 DVLOG(1) << "Unknown entry type for term " << term
<< ", type " << type
;
574 return ENTRY_KIND_UNKNOWN
;
578 int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind
) {
581 // All DriveEntryKind members are listed here, so the compiler catches if a
582 // newly added member is missing here.
584 case ENTRY_KIND_UNKNOWN
:
586 case ENTRY_KIND_ITEM
:
587 case ENTRY_KIND_SITE
:
590 // Hosted Google document.
591 case ENTRY_KIND_DOCUMENT
:
592 case ENTRY_KIND_SPREADSHEET
:
593 case ENTRY_KIND_PRESENTATION
:
594 case ENTRY_KIND_DRAWING
:
595 case ENTRY_KIND_TABLE
:
596 case ENTRY_KIND_FORM
:
597 classes
= KIND_OF_GOOGLE_DOCUMENT
| KIND_OF_HOSTED_DOCUMENT
;
600 // Hosted external application document.
601 case ENTRY_KIND_EXTERNAL_APP
:
602 classes
= KIND_OF_EXTERNAL_DOCUMENT
| KIND_OF_HOSTED_DOCUMENT
;
605 // Folders, collections.
606 case ENTRY_KIND_FOLDER
:
607 classes
= KIND_OF_FOLDER
;
611 case ENTRY_KIND_FILE
:
613 classes
= KIND_OF_FILE
;
616 case ENTRY_KIND_MAX_VALUE
:
623 void ResourceEntry::FillRemainingFields() {
624 // Set |kind_| and |labels_| based on the |categories_| in the class.
625 // JSONValueConverter does not have the ability to catch an element in a list
626 // based on a predicate. Thus we need to iterate over |categories_| and
627 // find the elements to set these fields as a post-process.
628 for (size_t i
= 0; i
< categories_
.size(); ++i
) {
629 const Category
* category
= categories_
[i
];
630 if (category
->type() == Category::CATEGORY_KIND
)
631 kind_
= GetEntryKindFromTerm(category
->term());
632 else if (category
->type() == Category::CATEGORY_LABEL
)
633 labels_
.push_back(category
->label());
638 scoped_ptr
<ResourceEntry
> ResourceEntry::ExtractAndParse(
639 const base::Value
& value
) {
640 const base::DictionaryValue
* as_dict
= NULL
;
641 const base::DictionaryValue
* entry_dict
= NULL
;
642 if (value
.GetAsDictionary(&as_dict
) &&
643 as_dict
->GetDictionary(kEntryField
, &entry_dict
)) {
644 return ResourceEntry::CreateFrom(*entry_dict
);
646 return scoped_ptr
<ResourceEntry
>();
650 scoped_ptr
<ResourceEntry
> ResourceEntry::CreateFrom(const base::Value
& value
) {
651 base::JSONValueConverter
<ResourceEntry
> converter
;
652 scoped_ptr
<ResourceEntry
> entry(new ResourceEntry());
653 if (!converter
.Convert(value
, entry
.get())) {
654 DVLOG(1) << "Invalid resource entry!";
655 return scoped_ptr
<ResourceEntry
>();
658 entry
->FillRemainingFields();
663 std::string
ResourceEntry::GetEntryNodeName() {
667 ////////////////////////////////////////////////////////////////////////////////
668 // ResourceList implementation
670 ResourceList::ResourceList()
673 largest_changestamp_(0) {
676 ResourceList::~ResourceList() {
680 void ResourceList::RegisterJSONConverter(
681 base::JSONValueConverter
<ResourceList
>* converter
) {
683 CommonMetadata::RegisterJSONConverter(converter
);
684 // TODO(zelidrag): Once we figure out where these will be used, we should
685 // check for valid start_index_ and items_per_page_ values.
686 converter
->RegisterCustomField
<int>(
687 kStartIndexField
, &ResourceList::start_index_
, &base::StringToInt
);
688 converter
->RegisterCustomField
<int>(
689 kItemsPerPageField
, &ResourceList::items_per_page_
, &base::StringToInt
);
690 converter
->RegisterStringField(kTitleTField
, &ResourceList::title_
);
691 converter
->RegisterRepeatedMessage(kEntryField
, &ResourceList::entries_
);
692 converter
->RegisterCustomField
<int64
>(
693 kLargestChangestampField
, &ResourceList::largest_changestamp_
,
694 &base::StringToInt64
);
697 bool ResourceList::Parse(const base::Value
& value
) {
698 base::JSONValueConverter
<ResourceList
> converter
;
699 if (!converter
.Convert(value
, this)) {
700 DVLOG(1) << "Invalid resource list!";
704 ScopedVector
<ResourceEntry
>::iterator iter
= entries_
.begin();
705 while (iter
!= entries_
.end()) {
706 ResourceEntry
* entry
= (*iter
);
707 entry
->FillRemainingFields();
714 scoped_ptr
<ResourceList
> ResourceList::ExtractAndParse(
715 const base::Value
& value
) {
716 const base::DictionaryValue
* as_dict
= NULL
;
717 const base::DictionaryValue
* feed_dict
= NULL
;
718 if (value
.GetAsDictionary(&as_dict
) &&
719 as_dict
->GetDictionary(kFeedField
, &feed_dict
)) {
720 return ResourceList::CreateFrom(*feed_dict
);
722 return scoped_ptr
<ResourceList
>();
726 scoped_ptr
<ResourceList
> ResourceList::CreateFrom(const base::Value
& value
) {
727 scoped_ptr
<ResourceList
> feed(new ResourceList());
728 if (!feed
->Parse(value
)) {
729 DVLOG(1) << "Invalid resource list!";
730 return scoped_ptr
<ResourceList
>();
736 bool ResourceList::GetNextFeedURL(GURL
* url
) const {
738 for (size_t i
= 0; i
< links_
.size(); ++i
) {
739 if (links_
[i
]->type() == Link::LINK_NEXT
) {
740 *url
= links_
[i
]->href();
747 void ResourceList::ReleaseEntries(std::vector
<ResourceEntry
*>* entries
) {
748 entries_
.release(entries
);
751 ////////////////////////////////////////////////////////////////////////////////
752 // InstalledApp implementation
754 InstalledApp::InstalledApp() : supports_create_(false) {
757 InstalledApp::~InstalledApp() {
760 InstalledApp::IconList
InstalledApp::GetIconsForCategory(
761 AppIcon::IconCategory category
) const {
764 for (ScopedVector
<AppIcon
>::const_iterator icon_iter
= app_icons_
.begin();
765 icon_iter
!= app_icons_
.end(); ++icon_iter
) {
766 if ((*icon_iter
)->category() != category
)
768 GURL icon_url
= (*icon_iter
)->GetIconURL();
769 if (icon_url
.is_empty())
771 result
.push_back(std::make_pair((*icon_iter
)->icon_side_length(),
775 // Return a sorted list, smallest to largest.
776 std::sort(result
.begin(), result
.end(), SortBySize
);
780 GURL
InstalledApp::GetProductUrl() const {
781 for (ScopedVector
<Link
>::const_iterator it
= links_
.begin();
782 it
!= links_
.end(); ++it
) {
783 const Link
* link
= *it
;
784 if (link
->type() == Link::LINK_PRODUCT
)
791 bool InstalledApp::GetValueString(const base::Value
* value
,
792 std::string
* result
) {
793 const base::DictionaryValue
* dict
= NULL
;
794 if (!value
->GetAsDictionary(&dict
))
797 if (!dict
->GetString(kTField
, result
))
804 void InstalledApp::RegisterJSONConverter(
805 base::JSONValueConverter
<InstalledApp
>* converter
) {
806 converter
->RegisterRepeatedMessage(kInstalledAppIconField
,
807 &InstalledApp::app_icons_
);
808 converter
->RegisterStringField(kInstalledAppIdField
,
809 &InstalledApp::app_id_
);
810 converter
->RegisterStringField(kInstalledAppNameField
,
811 &InstalledApp::app_name_
);
812 converter
->RegisterStringField(kInstalledAppObjectTypeField
,
813 &InstalledApp::object_type_
);
814 converter
->RegisterCustomField
<bool>(kInstalledAppSupportsCreateField
,
815 &InstalledApp::supports_create_
,
817 converter
->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField
,
818 &InstalledApp::primary_mimetypes_
,
820 converter
->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField
,
821 &InstalledApp::secondary_mimetypes_
,
823 converter
->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField
,
824 &InstalledApp::primary_extensions_
,
826 converter
->RegisterRepeatedCustomValue(
827 kInstalledAppSecondaryFileExtensionField
,
828 &InstalledApp::secondary_extensions_
,
830 converter
->RegisterRepeatedMessage(kLinkField
, &InstalledApp::links_
);
833 ////////////////////////////////////////////////////////////////////////////////
834 // AccountMetadata implementation
836 AccountMetadata::AccountMetadata()
837 : quota_bytes_total_(0),
838 quota_bytes_used_(0),
839 largest_changestamp_(0) {
842 AccountMetadata::~AccountMetadata() {
846 void AccountMetadata::RegisterJSONConverter(
847 base::JSONValueConverter
<AccountMetadata
>* converter
) {
848 converter
->RegisterCustomField
<int64
>(
849 kQuotaBytesTotalField
,
850 &AccountMetadata::quota_bytes_total_
,
851 &base::StringToInt64
);
852 converter
->RegisterCustomField
<int64
>(
853 kQuotaBytesUsedField
,
854 &AccountMetadata::quota_bytes_used_
,
855 &base::StringToInt64
);
856 converter
->RegisterCustomField
<int64
>(
857 kLargestChangestampField
,
858 &AccountMetadata::largest_changestamp_
,
859 &base::StringToInt64
);
860 converter
->RegisterRepeatedMessage(kInstalledAppField
,
861 &AccountMetadata::installed_apps_
);
865 scoped_ptr
<AccountMetadata
> AccountMetadata::CreateFrom(
866 const base::Value
& value
) {
867 scoped_ptr
<AccountMetadata
> metadata(new AccountMetadata());
868 const base::DictionaryValue
* dictionary
= NULL
;
869 const base::Value
* entry
= NULL
;
870 if (!value
.GetAsDictionary(&dictionary
) ||
871 !dictionary
->Get(kEntryField
, &entry
) ||
872 !metadata
->Parse(*entry
)) {
873 LOG(ERROR
) << "Unable to create: Invalid account metadata feed!";
874 return scoped_ptr
<AccountMetadata
>();
877 return metadata
.Pass();
880 bool AccountMetadata::Parse(const base::Value
& value
) {
881 base::JSONValueConverter
<AccountMetadata
> converter
;
882 if (!converter
.Convert(value
, this)) {
883 LOG(ERROR
) << "Unable to parse: Invalid account metadata feed!";
889 } // namespace google_apis