Add an extension override bubble and warning box for proxy extensions. (2nd attempt...
[chromium-blink-merge.git] / google_apis / drive / drive_api_parser.cc
blobc22070eb458e5dd2a1c8c484cdcf6bfe0fe53945
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/drive_api_parser.h"
7 #include <algorithm>
9 #include "base/basictypes.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_value_converter.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "google_apis/drive/time_util.h"
19 using base::Value;
20 using base::DictionaryValue;
21 using base::ListValue;
23 namespace google_apis {
25 namespace {
27 bool CreateFileResourceFromValue(const base::Value* value,
28 scoped_ptr<FileResource>* file) {
29 *file = FileResource::CreateFrom(*value);
30 return !!*file;
33 // Converts |url_string| to |result|. Always returns true to be used
34 // for JSONValueConverter::RegisterCustomField method.
35 // TODO(mukai): make it return false in case of invalid |url_string|.
36 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
37 *result = GURL(url_string.as_string());
38 return true;
41 // Converts |value| to |result|.
42 bool GetParentsFromValue(const base::Value* value,
43 std::vector<ParentReference>* result) {
44 DCHECK(value);
45 DCHECK(result);
47 const base::ListValue* list_value = NULL;
48 if (!value->GetAsList(&list_value))
49 return false;
51 base::JSONValueConverter<ParentReference> converter;
52 result->resize(list_value->GetSize());
53 for (size_t i = 0; i < list_value->GetSize(); ++i) {
54 const base::Value* parent_value = NULL;
55 if (!list_value->Get(i, &parent_value) ||
56 !converter.Convert(*parent_value, &(*result)[i]))
57 return false;
60 return true;
63 // Converts |value| to |result|. The key of |value| is app_id, and its value
64 // is URL to open the resource on the web app.
65 bool GetOpenWithLinksFromDictionaryValue(
66 const base::Value* value,
67 std::vector<FileResource::OpenWithLink>* result) {
68 DCHECK(value);
69 DCHECK(result);
71 const base::DictionaryValue* dictionary_value;
72 if (!value->GetAsDictionary(&dictionary_value))
73 return false;
75 result->reserve(dictionary_value->size());
76 for (DictionaryValue::Iterator iter(*dictionary_value);
77 !iter.IsAtEnd(); iter.Advance()) {
78 std::string string_value;
79 if (!iter.value().GetAsString(&string_value))
80 return false;
82 FileResource::OpenWithLink open_with_link;
83 open_with_link.app_id = iter.key();
84 open_with_link.open_url = GURL(string_value);
85 result->push_back(open_with_link);
88 return true;
91 // Drive v2 API JSON names.
93 // Definition order follows the order of documentation in
94 // https://developers.google.com/drive/v2/reference/
96 // Common
97 const char kKind[] = "kind";
98 const char kId[] = "id";
99 const char kETag[] = "etag";
100 const char kItems[] = "items";
101 const char kLargestChangeId[] = "largestChangeId";
103 // About Resource
104 // https://developers.google.com/drive/v2/reference/about
105 const char kAboutKind[] = "drive#about";
106 const char kQuotaBytesTotal[] = "quotaBytesTotal";
107 const char kQuotaBytesUsed[] = "quotaBytesUsed";
108 const char kRootFolderId[] = "rootFolderId";
110 // App Icon
111 // https://developers.google.com/drive/v2/reference/apps
112 const char kCategory[] = "category";
113 const char kSize[] = "size";
114 const char kIconUrl[] = "iconUrl";
116 // Apps Resource
117 // https://developers.google.com/drive/v2/reference/apps
118 const char kAppKind[] = "drive#app";
119 const char kName[] = "name";
120 const char kObjectType[] = "objectType";
121 const char kProductId[] = "productId";
122 const char kSupportsCreate[] = "supportsCreate";
123 const char kRemovable[] = "removable";
124 const char kPrimaryMimeTypes[] = "primaryMimeTypes";
125 const char kSecondaryMimeTypes[] = "secondaryMimeTypes";
126 const char kPrimaryFileExtensions[] = "primaryFileExtensions";
127 const char kSecondaryFileExtensions[] = "secondaryFileExtensions";
128 const char kIcons[] = "icons";
129 const char kCreateUrl[] = "createUrl";
131 // Apps List
132 // https://developers.google.com/drive/v2/reference/apps/list
133 const char kAppListKind[] = "drive#appList";
135 // Parent Resource
136 // https://developers.google.com/drive/v2/reference/parents
137 const char kParentReferenceKind[] = "drive#parentReference";
138 const char kParentLink[] = "parentLink";
140 // File Resource
141 // https://developers.google.com/drive/v2/reference/files
142 const char kFileKind[] = "drive#file";
143 const char kTitle[] = "title";
144 const char kMimeType[] = "mimeType";
145 const char kCreatedDate[] = "createdDate";
146 const char kModificationDate[] = "modificationDate";
147 const char kModifiedDate[] = "modifiedDate";
148 const char kLastViewedByMeDate[] = "lastViewedByMeDate";
149 const char kSharedWithMeDate[] = "sharedWithMeDate";
150 const char kMd5Checksum[] = "md5Checksum";
151 const char kFileSize[] = "fileSize";
152 const char kAlternateLink[] = "alternateLink";
153 const char kParents[] = "parents";
154 const char kOpenWithLinks[] = "openWithLinks";
155 const char kLabels[] = "labels";
156 const char kImageMediaMetadata[] = "imageMediaMetadata";
157 const char kShared[] = "shared";
158 // These 5 flags are defined under |labels|.
159 const char kLabelTrashed[] = "trashed";
160 // These 3 flags are defined under |imageMediaMetadata|.
161 const char kImageMediaMetadataWidth[] = "width";
162 const char kImageMediaMetadataHeight[] = "height";
163 const char kImageMediaMetadataRotation[] = "rotation";
165 const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder";
167 // Files List
168 // https://developers.google.com/drive/v2/reference/files/list
169 const char kFileListKind[] = "drive#fileList";
170 const char kNextLink[] = "nextLink";
172 // Change Resource
173 // https://developers.google.com/drive/v2/reference/changes
174 const char kChangeKind[] = "drive#change";
175 const char kFileId[] = "fileId";
176 const char kDeleted[] = "deleted";
177 const char kFile[] = "file";
179 // Changes List
180 // https://developers.google.com/drive/v2/reference/changes/list
181 const char kChangeListKind[] = "drive#changeList";
183 // Maps category name to enum IconCategory.
184 struct AppIconCategoryMap {
185 DriveAppIcon::IconCategory category;
186 const char* category_name;
189 const AppIconCategoryMap kAppIconCategoryMap[] = {
190 { DriveAppIcon::DOCUMENT, "document" },
191 { DriveAppIcon::APPLICATION, "application" },
192 { DriveAppIcon::SHARED_DOCUMENT, "documentShared" },
195 // Checks if the JSON is expected kind. In Drive API, JSON data structure has
196 // |kind| property which denotes the type of the structure (e.g. "drive#file").
197 bool IsResourceKindExpected(const base::Value& value,
198 const std::string& expected_kind) {
199 const base::DictionaryValue* as_dict = NULL;
200 std::string kind;
201 return value.GetAsDictionary(&as_dict) &&
202 as_dict->HasKey(kKind) &&
203 as_dict->GetString(kKind, &kind) &&
204 kind == expected_kind;
207 } // namespace
209 ////////////////////////////////////////////////////////////////////////////////
210 // AboutResource implementation
212 AboutResource::AboutResource()
213 : largest_change_id_(0),
214 quota_bytes_total_(0),
215 quota_bytes_used_(0) {}
217 AboutResource::~AboutResource() {}
219 // static
220 scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) {
221 scoped_ptr<AboutResource> resource(new AboutResource());
222 if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) {
223 LOG(ERROR) << "Unable to create: Invalid About resource JSON!";
224 return scoped_ptr<AboutResource>();
226 return resource.Pass();
229 // static
230 void AboutResource::RegisterJSONConverter(
231 base::JSONValueConverter<AboutResource>* converter) {
232 converter->RegisterCustomField<int64>(kLargestChangeId,
233 &AboutResource::largest_change_id_,
234 &base::StringToInt64);
235 converter->RegisterCustomField<int64>(kQuotaBytesTotal,
236 &AboutResource::quota_bytes_total_,
237 &base::StringToInt64);
238 converter->RegisterCustomField<int64>(kQuotaBytesUsed,
239 &AboutResource::quota_bytes_used_,
240 &base::StringToInt64);
241 converter->RegisterStringField(kRootFolderId,
242 &AboutResource::root_folder_id_);
245 bool AboutResource::Parse(const base::Value& value) {
246 base::JSONValueConverter<AboutResource> converter;
247 if (!converter.Convert(value, this)) {
248 LOG(ERROR) << "Unable to parse: Invalid About resource JSON!";
249 return false;
251 return true;
254 ////////////////////////////////////////////////////////////////////////////////
255 // DriveAppIcon implementation
257 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN), icon_side_length_(0) {}
259 DriveAppIcon::~DriveAppIcon() {}
261 // static
262 void DriveAppIcon::RegisterJSONConverter(
263 base::JSONValueConverter<DriveAppIcon>* converter) {
264 converter->RegisterCustomField<IconCategory>(
265 kCategory,
266 &DriveAppIcon::category_,
267 &DriveAppIcon::GetIconCategory);
268 converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_);
269 converter->RegisterCustomField<GURL>(kIconUrl,
270 &DriveAppIcon::icon_url_,
271 GetGURLFromString);
274 // static
275 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) {
276 scoped_ptr<DriveAppIcon> resource(new DriveAppIcon());
277 if (!resource->Parse(value)) {
278 LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!";
279 return scoped_ptr<DriveAppIcon>();
281 return resource.Pass();
284 bool DriveAppIcon::Parse(const base::Value& value) {
285 base::JSONValueConverter<DriveAppIcon> converter;
286 if (!converter.Convert(value, this)) {
287 LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon";
288 return false;
290 return true;
293 // static
294 bool DriveAppIcon::GetIconCategory(const base::StringPiece& category,
295 DriveAppIcon::IconCategory* result) {
296 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
297 if (category == kAppIconCategoryMap[i].category_name) {
298 *result = kAppIconCategoryMap[i].category;
299 return true;
302 DVLOG(1) << "Unknown icon category " << category;
303 return false;
306 ////////////////////////////////////////////////////////////////////////////////
307 // AppResource implementation
309 AppResource::AppResource()
310 : supports_create_(false),
311 removable_(false) {
314 AppResource::~AppResource() {}
316 // static
317 void AppResource::RegisterJSONConverter(
318 base::JSONValueConverter<AppResource>* converter) {
319 converter->RegisterStringField(kId, &AppResource::application_id_);
320 converter->RegisterStringField(kName, &AppResource::name_);
321 converter->RegisterStringField(kObjectType, &AppResource::object_type_);
322 converter->RegisterStringField(kProductId, &AppResource::product_id_);
323 converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_);
324 converter->RegisterBoolField(kRemovable, &AppResource::removable_);
325 converter->RegisterRepeatedString(kPrimaryMimeTypes,
326 &AppResource::primary_mimetypes_);
327 converter->RegisterRepeatedString(kSecondaryMimeTypes,
328 &AppResource::secondary_mimetypes_);
329 converter->RegisterRepeatedString(kPrimaryFileExtensions,
330 &AppResource::primary_file_extensions_);
331 converter->RegisterRepeatedString(kSecondaryFileExtensions,
332 &AppResource::secondary_file_extensions_);
333 converter->RegisterRepeatedMessage(kIcons, &AppResource::icons_);
334 converter->RegisterCustomField<GURL>(kCreateUrl,
335 &AppResource::create_url_,
336 GetGURLFromString);
339 // static
340 scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) {
341 scoped_ptr<AppResource> resource(new AppResource());
342 if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) {
343 LOG(ERROR) << "Unable to create: Invalid AppResource JSON!";
344 return scoped_ptr<AppResource>();
346 return resource.Pass();
349 bool AppResource::Parse(const base::Value& value) {
350 base::JSONValueConverter<AppResource> converter;
351 if (!converter.Convert(value, this)) {
352 LOG(ERROR) << "Unable to parse: Invalid AppResource";
353 return false;
355 return true;
358 ////////////////////////////////////////////////////////////////////////////////
359 // AppList implementation
361 AppList::AppList() {}
363 AppList::~AppList() {}
365 // static
366 void AppList::RegisterJSONConverter(
367 base::JSONValueConverter<AppList>* converter) {
368 converter->RegisterStringField(kETag, &AppList::etag_);
369 converter->RegisterRepeatedMessage<AppResource>(kItems,
370 &AppList::items_);
373 // static
374 scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) {
375 scoped_ptr<AppList> resource(new AppList());
376 if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) {
377 LOG(ERROR) << "Unable to create: Invalid AppList JSON!";
378 return scoped_ptr<AppList>();
380 return resource.Pass();
383 bool AppList::Parse(const base::Value& value) {
384 base::JSONValueConverter<AppList> converter;
385 if (!converter.Convert(value, this)) {
386 LOG(ERROR) << "Unable to parse: Invalid AppList";
387 return false;
389 return true;
392 ////////////////////////////////////////////////////////////////////////////////
393 // ParentReference implementation
395 ParentReference::ParentReference() {}
397 ParentReference::~ParentReference() {}
399 // static
400 void ParentReference::RegisterJSONConverter(
401 base::JSONValueConverter<ParentReference>* converter) {
402 converter->RegisterStringField(kId, &ParentReference::file_id_);
403 converter->RegisterCustomField<GURL>(kParentLink,
404 &ParentReference::parent_link_,
405 GetGURLFromString);
408 // static
409 scoped_ptr<ParentReference>
410 ParentReference::CreateFrom(const base::Value& value) {
411 scoped_ptr<ParentReference> reference(new ParentReference());
412 if (!IsResourceKindExpected(value, kParentReferenceKind) ||
413 !reference->Parse(value)) {
414 LOG(ERROR) << "Unable to create: Invalid ParentRefernce JSON!";
415 return scoped_ptr<ParentReference>();
417 return reference.Pass();
420 bool ParentReference::Parse(const base::Value& value) {
421 base::JSONValueConverter<ParentReference> converter;
422 if (!converter.Convert(value, this)) {
423 LOG(ERROR) << "Unable to parse: Invalid ParentReference";
424 return false;
426 return true;
429 ////////////////////////////////////////////////////////////////////////////////
430 // FileResource implementation
432 FileResource::FileResource() : shared_(false), file_size_(0) {}
434 FileResource::~FileResource() {}
436 // static
437 void FileResource::RegisterJSONConverter(
438 base::JSONValueConverter<FileResource>* converter) {
439 converter->RegisterStringField(kId, &FileResource::file_id_);
440 converter->RegisterStringField(kETag, &FileResource::etag_);
441 converter->RegisterStringField(kTitle, &FileResource::title_);
442 converter->RegisterStringField(kMimeType, &FileResource::mime_type_);
443 converter->RegisterNestedField(kLabels, &FileResource::labels_);
444 converter->RegisterNestedField(kImageMediaMetadata,
445 &FileResource::image_media_metadata_);
446 converter->RegisterCustomField<base::Time>(
447 kCreatedDate,
448 &FileResource::created_date_,
449 &util::GetTimeFromString);
450 converter->RegisterCustomField<base::Time>(
451 kModifiedDate,
452 &FileResource::modified_date_,
453 &util::GetTimeFromString);
454 converter->RegisterCustomField<base::Time>(
455 kLastViewedByMeDate,
456 &FileResource::last_viewed_by_me_date_,
457 &util::GetTimeFromString);
458 converter->RegisterCustomField<base::Time>(
459 kSharedWithMeDate,
460 &FileResource::shared_with_me_date_,
461 &util::GetTimeFromString);
462 converter->RegisterBoolField(kShared, &FileResource::shared_);
463 converter->RegisterStringField(kMd5Checksum, &FileResource::md5_checksum_);
464 converter->RegisterCustomField<int64>(kFileSize,
465 &FileResource::file_size_,
466 &base::StringToInt64);
467 converter->RegisterCustomField<GURL>(kAlternateLink,
468 &FileResource::alternate_link_,
469 GetGURLFromString);
470 converter->RegisterCustomValueField<std::vector<ParentReference> >(
471 kParents,
472 &FileResource::parents_,
473 GetParentsFromValue);
474 converter->RegisterCustomValueField<std::vector<OpenWithLink> >(
475 kOpenWithLinks,
476 &FileResource::open_with_links_,
477 GetOpenWithLinksFromDictionaryValue);
480 // static
481 scoped_ptr<FileResource> FileResource::CreateFrom(const base::Value& value) {
482 scoped_ptr<FileResource> resource(new FileResource());
483 if (!IsResourceKindExpected(value, kFileKind) || !resource->Parse(value)) {
484 LOG(ERROR) << "Unable to create: Invalid FileResource JSON!";
485 return scoped_ptr<FileResource>();
487 return resource.Pass();
490 bool FileResource::IsDirectory() const {
491 return mime_type_ == kDriveFolderMimeType;
494 bool FileResource::Parse(const base::Value& value) {
495 base::JSONValueConverter<FileResource> converter;
496 if (!converter.Convert(value, this)) {
497 LOG(ERROR) << "Unable to parse: Invalid FileResource";
498 return false;
500 return true;
503 ////////////////////////////////////////////////////////////////////////////////
504 // FileList implementation
506 FileList::FileList() {}
508 FileList::~FileList() {}
510 // static
511 void FileList::RegisterJSONConverter(
512 base::JSONValueConverter<FileList>* converter) {
513 converter->RegisterCustomField<GURL>(kNextLink,
514 &FileList::next_link_,
515 GetGURLFromString);
516 converter->RegisterRepeatedMessage<FileResource>(kItems,
517 &FileList::items_);
520 // static
521 bool FileList::HasFileListKind(const base::Value& value) {
522 return IsResourceKindExpected(value, kFileListKind);
525 // static
526 scoped_ptr<FileList> FileList::CreateFrom(const base::Value& value) {
527 scoped_ptr<FileList> resource(new FileList());
528 if (!HasFileListKind(value) || !resource->Parse(value)) {
529 LOG(ERROR) << "Unable to create: Invalid FileList JSON!";
530 return scoped_ptr<FileList>();
532 return resource.Pass();
535 bool FileList::Parse(const base::Value& value) {
536 base::JSONValueConverter<FileList> converter;
537 if (!converter.Convert(value, this)) {
538 LOG(ERROR) << "Unable to parse: Invalid FileList";
539 return false;
541 return true;
544 ////////////////////////////////////////////////////////////////////////////////
545 // ChangeResource implementation
547 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
549 ChangeResource::~ChangeResource() {}
551 // static
552 void ChangeResource::RegisterJSONConverter(
553 base::JSONValueConverter<ChangeResource>* converter) {
554 converter->RegisterCustomField<int64>(kId,
555 &ChangeResource::change_id_,
556 &base::StringToInt64);
557 converter->RegisterStringField(kFileId, &ChangeResource::file_id_);
558 converter->RegisterBoolField(kDeleted, &ChangeResource::deleted_);
559 converter->RegisterCustomValueField(kFile, &ChangeResource::file_,
560 &CreateFileResourceFromValue);
561 converter->RegisterCustomField<base::Time>(
562 kModificationDate, &ChangeResource::modification_date_,
563 &util::GetTimeFromString);
566 // static
567 scoped_ptr<ChangeResource>
568 ChangeResource::CreateFrom(const base::Value& value) {
569 scoped_ptr<ChangeResource> resource(new ChangeResource());
570 if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) {
571 LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!";
572 return scoped_ptr<ChangeResource>();
574 return resource.Pass();
577 bool ChangeResource::Parse(const base::Value& value) {
578 base::JSONValueConverter<ChangeResource> converter;
579 if (!converter.Convert(value, this)) {
580 LOG(ERROR) << "Unable to parse: Invalid ChangeResource";
581 return false;
583 return true;
586 ////////////////////////////////////////////////////////////////////////////////
587 // ChangeList implementation
589 ChangeList::ChangeList() : largest_change_id_(0) {}
591 ChangeList::~ChangeList() {}
593 // static
594 void ChangeList::RegisterJSONConverter(
595 base::JSONValueConverter<ChangeList>* converter) {
596 converter->RegisterCustomField<GURL>(kNextLink,
597 &ChangeList::next_link_,
598 GetGURLFromString);
599 converter->RegisterCustomField<int64>(kLargestChangeId,
600 &ChangeList::largest_change_id_,
601 &base::StringToInt64);
602 converter->RegisterRepeatedMessage<ChangeResource>(kItems,
603 &ChangeList::items_);
606 // static
607 bool ChangeList::HasChangeListKind(const base::Value& value) {
608 return IsResourceKindExpected(value, kChangeListKind);
611 // static
612 scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) {
613 scoped_ptr<ChangeList> resource(new ChangeList());
614 if (!HasChangeListKind(value) || !resource->Parse(value)) {
615 LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!";
616 return scoped_ptr<ChangeList>();
618 return resource.Pass();
621 bool ChangeList::Parse(const base::Value& value) {
622 base::JSONValueConverter<ChangeList> converter;
623 if (!converter.Convert(value, this)) {
624 LOG(ERROR) << "Unable to parse: Invalid ChangeList";
625 return false;
627 return true;
631 ////////////////////////////////////////////////////////////////////////////////
632 // FileLabels implementation
634 FileLabels::FileLabels() : trashed_(false) {}
636 FileLabels::~FileLabels() {}
638 // static
639 void FileLabels::RegisterJSONConverter(
640 base::JSONValueConverter<FileLabels>* converter) {
641 converter->RegisterBoolField(kLabelTrashed, &FileLabels::trashed_);
644 // static
645 scoped_ptr<FileLabels> FileLabels::CreateFrom(const base::Value& value) {
646 scoped_ptr<FileLabels> resource(new FileLabels());
647 if (!resource->Parse(value)) {
648 LOG(ERROR) << "Unable to create: Invalid FileLabels JSON!";
649 return scoped_ptr<FileLabels>();
651 return resource.Pass();
654 bool FileLabels::Parse(const base::Value& value) {
655 base::JSONValueConverter<FileLabels> converter;
656 if (!converter.Convert(value, this)) {
657 LOG(ERROR) << "Unable to parse: Invalid FileLabels.";
658 return false;
660 return true;
663 ////////////////////////////////////////////////////////////////////////////////
664 // ImageMediaMetadata implementation
666 ImageMediaMetadata::ImageMediaMetadata()
667 : width_(-1),
668 height_(-1),
669 rotation_(-1) {}
671 ImageMediaMetadata::~ImageMediaMetadata() {}
673 // static
674 void ImageMediaMetadata::RegisterJSONConverter(
675 base::JSONValueConverter<ImageMediaMetadata>* converter) {
676 converter->RegisterIntField(kImageMediaMetadataWidth,
677 &ImageMediaMetadata::width_);
678 converter->RegisterIntField(kImageMediaMetadataHeight,
679 &ImageMediaMetadata::height_);
680 converter->RegisterIntField(kImageMediaMetadataRotation,
681 &ImageMediaMetadata::rotation_);
684 // static
685 scoped_ptr<ImageMediaMetadata> ImageMediaMetadata::CreateFrom(
686 const base::Value& value) {
687 scoped_ptr<ImageMediaMetadata> resource(new ImageMediaMetadata());
688 if (!resource->Parse(value)) {
689 LOG(ERROR) << "Unable to create: Invalid ImageMediaMetadata JSON!";
690 return scoped_ptr<ImageMediaMetadata>();
692 return resource.Pass();
695 bool ImageMediaMetadata::Parse(const base::Value& value) {
696 base::JSONValueConverter<ImageMediaMetadata> converter;
697 if (!converter.Convert(value, this)) {
698 LOG(ERROR) << "Unable to parse: Invalid ImageMediaMetadata.";
699 return false;
701 return true;
704 } // namespace google_apis