Remove unused script android_webview/build/install_binary.
[chromium-blink-merge.git] / google_apis / drive / gdata_wapi_parser.cc
blob2f3c97847a16254864334acb6c384a7da9f0016b
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"
7 #include <algorithm>
8 #include <string>
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"
21 using base::Value;
22 using base::DictionaryValue;
23 using base::ListValue;
25 namespace google_apis {
27 namespace {
29 // Term values for kSchemeKind category:
30 const char kTermPrefix[] = "http://schemas.google.com/docs/2007#";
32 // Node names.
33 const char kEntryNode[] = "entry";
35 // Field names.
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";
91 // Link Prefixes
92 const char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-";
93 const size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1;
95 struct EntryKindMap {
96 DriveEntryKind kind;
97 const char* entry;
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);
119 struct LinkTypeMap {
120 Link::LinkType type;
121 const char* rel;
124 const LinkTypeMap kLinkTypeMap[] = {
125 { Link::LINK_SELF,
126 "self" },
127 { Link::LINK_NEXT,
128 "next" },
129 { Link::LINK_PARENT,
130 "http://schemas.google.com/docs/2007#parent" },
131 { Link::LINK_ALTERNATE,
132 "alternate"},
133 { Link::LINK_EDIT,
134 "edit" },
135 { Link::LINK_EDIT_MEDIA,
136 "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" },
141 { Link::LINK_FEED,
142 "http://schemas.google.com/g/2005#feed"},
143 { Link::LINK_POST,
144 "http://schemas.google.com/g/2005#post"},
145 { Link::LINK_BATCH,
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"},
157 { Link::LINK_EMBED,
158 "http://schemas.google.com/docs/2007#embed"},
159 { Link::LINK_PRODUCT,
160 "http://schemas.google.com/docs/2007#product"},
161 { Link::LINK_ICON,
162 "http://schemas.google.com/docs/2007#icon"},
163 { Link::LINK_SHARE,
164 "http://schemas.google.com/docs/2007#share"},
167 struct ResourceLinkTypeMap {
168 ResourceLink::ResourceLinkType type;
169 const char* rel;
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;
181 const char* scheme;
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());
205 return true;
208 // Converts boolean string values like "true" into bool.
209 bool GetBoolFromString(const base::StringPiece& value, bool* result) {
210 *result = (value == "true");
211 return true;
214 bool SortBySize(const InstalledApp::IconList::value_type& a,
215 const InstalledApp::IconList::value_type& b) {
216 return a.first < b.first;
219 } // namespace
221 ////////////////////////////////////////////////////////////////////////////////
222 // Author implementation
224 Author::Author() {
227 // static
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) {
240 Link::~Link() {
243 // static
244 bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) {
245 DCHECK(app_id);
246 // Fast return path if the link clearly isn't an OPEN_WITH link.
247 if (rel.size() < kOpenWithPrefixSize) {
248 app_id->clear();
249 return true;
252 const std::string kOpenWithPrefixStr(kOpenWithPrefix);
253 if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) {
254 *app_id = rel.as_string().substr(kOpenWithPrefixStr.size());
255 return true;
258 app_id->clear();
259 return true;
262 // static.
263 bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) {
264 DCHECK(type);
265 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) {
266 if (rel == kLinkTypeMap[i].rel) {
267 *type = kLinkTypeMap[i].type;
268 return true;
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;
278 return true;
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;
285 return true;
288 // static
289 void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) {
290 converter->RegisterCustomField<Link::LinkType>(kRelField,
291 &Link::type_,
292 &Link::GetLinkType);
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,
296 &Link::app_id_,
297 &Link::GetAppID);
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) {
309 // static.
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;
315 return true;
318 DVLOG(1) << "Unknown feed link type for rel " << rel;
319 return false;
322 // static
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;
343 return true;
346 DVLOG(1) << "Unknown feed link type for scheme " << scheme;
347 return false;
350 // static
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)
362 return links_[i];
364 return NULL;
367 ////////////////////////////////////////////////////////////////////////////////
368 // Content implementation
370 Content::Content() {
373 // static
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() {
389 // static
390 void AppIcon::RegisterJSONConverter(
391 base::JSONValueConverter<AppIcon>* converter) {
392 converter->RegisterCustomField<AppIcon::IconCategory>(
393 kInstalledAppIconCategoryField,
394 &AppIcon::category_,
395 &AppIcon::GetIconCategory);
396 converter->RegisterCustomField<int>(kInstalledAppIconSizeField,
397 &AppIcon::icon_side_length_,
398 base::StringToInt);
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();
407 return GURL();
410 // static
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;
416 return true;
419 DVLOG(1) << "Unknown icon category " << category;
420 return false;
423 ////////////////////////////////////////////////////////////////////////////////
424 // CommonMetadata implementation
426 CommonMetadata::CommonMetadata() {
429 CommonMetadata::~CommonMetadata() {
432 // static
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),
452 file_size_(0),
453 deleted_(false),
454 removed_(false),
455 changestamp_(0),
456 image_width_(-1),
457 image_height_(-1),
458 image_rotation_(-1) {
461 ResourceEntry::~ResourceEntry() {
464 bool ResourceEntry::HasFieldPresent(const base::Value* value,
465 bool* result) {
466 *result = (value != NULL);
467 return true;
470 bool ResourceEntry::ParseChangestamp(const base::Value* value,
471 int64* result) {
472 DCHECK(result);
473 if (!value) {
474 *result = 0;
475 return true;
478 std::string string_value;
479 if (value->GetAsString(&string_value) &&
480 base::StringToInt64(string_value, result))
481 return true;
483 return false;
486 // static
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);
531 else
532 return std::string();
535 return std::string();
538 // static
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;
549 // static
550 int ResourceEntry::ClassifyEntryKindByFileExtension(
551 const base::FilePath& file_path) {
552 #if defined(OS_WIN)
553 std::string file_extension = base::WideToUTF8(file_path.Extension());
554 #else
555 std::string file_extension = file_path.Extension();
556 #endif
557 return ClassifyEntryKind(GetEntryKindFromExtension(file_extension));
560 // static
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;
577 // static
578 int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind) {
579 int classes = 0;
581 // All DriveEntryKind members are listed here, so the compiler catches if a
582 // newly added member is missing here.
583 switch (kind) {
584 case ENTRY_KIND_UNKNOWN:
585 // Special entries.
586 case ENTRY_KIND_ITEM:
587 case ENTRY_KIND_SITE:
588 break;
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;
598 break;
600 // Hosted external application document.
601 case ENTRY_KIND_EXTERNAL_APP:
602 classes = KIND_OF_EXTERNAL_DOCUMENT | KIND_OF_HOSTED_DOCUMENT;
603 break;
605 // Folders, collections.
606 case ENTRY_KIND_FOLDER:
607 classes = KIND_OF_FOLDER;
608 break;
610 // Regular files.
611 case ENTRY_KIND_FILE:
612 case ENTRY_KIND_PDF:
613 classes = KIND_OF_FILE;
614 break;
616 case ENTRY_KIND_MAX_VALUE:
617 NOTREACHED();
620 return classes;
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());
637 // static
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>();
649 // static
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();
659 return entry.Pass();
662 // static
663 std::string ResourceEntry::GetEntryNodeName() {
664 return kEntryNode;
667 ////////////////////////////////////////////////////////////////////////////////
668 // ResourceList implementation
670 ResourceList::ResourceList()
671 : start_index_(0),
672 items_per_page_(0),
673 largest_changestamp_(0) {
676 ResourceList::~ResourceList() {
679 // static
680 void ResourceList::RegisterJSONConverter(
681 base::JSONValueConverter<ResourceList>* converter) {
682 // inheritance
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!";
701 return false;
704 ScopedVector<ResourceEntry>::iterator iter = entries_.begin();
705 while (iter != entries_.end()) {
706 ResourceEntry* entry = (*iter);
707 entry->FillRemainingFields();
708 ++iter;
710 return true;
713 // static
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>();
725 // static
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>();
733 return feed.Pass();
736 bool ResourceList::GetNextFeedURL(GURL* url) const {
737 DCHECK(url);
738 for (size_t i = 0; i < links_.size(); ++i) {
739 if (links_[i]->type() == Link::LINK_NEXT) {
740 *url = links_[i]->href();
741 return true;
744 return false;
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 {
762 IconList result;
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)
767 continue;
768 GURL icon_url = (*icon_iter)->GetIconURL();
769 if (icon_url.is_empty())
770 continue;
771 result.push_back(std::make_pair((*icon_iter)->icon_side_length(),
772 icon_url));
775 // Return a sorted list, smallest to largest.
776 std::sort(result.begin(), result.end(), SortBySize);
777 return result;
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)
785 return link->href();
787 return GURL();
790 // static
791 bool InstalledApp::GetValueString(const base::Value* value,
792 std::string* result) {
793 const base::DictionaryValue* dict = NULL;
794 if (!value->GetAsDictionary(&dict))
795 return false;
797 if (!dict->GetString(kTField, result))
798 return false;
800 return true;
803 // static
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_,
816 &GetBoolFromString);
817 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField,
818 &InstalledApp::primary_mimetypes_,
819 &GetValueString);
820 converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField,
821 &InstalledApp::secondary_mimetypes_,
822 &GetValueString);
823 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField,
824 &InstalledApp::primary_extensions_,
825 &GetValueString);
826 converter->RegisterRepeatedCustomValue(
827 kInstalledAppSecondaryFileExtensionField,
828 &InstalledApp::secondary_extensions_,
829 &GetValueString);
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() {
845 // static
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_);
864 // static
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!";
884 return false;
886 return true;
889 } // namespace google_apis