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 "base/basictypes.h"
8 #include "base/json/json_value_converter.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "google_apis/drive/time_util.h"
16 namespace google_apis
{
20 const int64 kUnsetFileSize
= -1;
22 bool CreateFileResourceFromValue(const base::Value
* value
,
23 scoped_ptr
<FileResource
>* file
) {
24 *file
= FileResource::CreateFrom(*value
);
28 // Converts |url_string| to |result|. Always returns true to be used
29 // for JSONValueConverter::RegisterCustomField method.
30 // TODO(mukai): make it return false in case of invalid |url_string|.
31 bool GetGURLFromString(const base::StringPiece
& url_string
, GURL
* result
) {
32 *result
= GURL(url_string
.as_string());
36 // Converts |value| to |result|.
37 bool GetParentsFromValue(const base::Value
* value
,
38 std::vector
<ParentReference
>* result
) {
42 const base::ListValue
* list_value
= NULL
;
43 if (!value
->GetAsList(&list_value
))
46 base::JSONValueConverter
<ParentReference
> converter
;
47 result
->resize(list_value
->GetSize());
48 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
49 const base::Value
* parent_value
= NULL
;
50 if (!list_value
->Get(i
, &parent_value
) ||
51 !converter
.Convert(*parent_value
, &(*result
)[i
]))
58 // Converts |value| to |result|. The key of |value| is app_id, and its value
59 // is URL to open the resource on the web app.
60 bool GetOpenWithLinksFromDictionaryValue(
61 const base::Value
* value
,
62 std::vector
<FileResource::OpenWithLink
>* result
) {
66 const base::DictionaryValue
* dictionary_value
;
67 if (!value
->GetAsDictionary(&dictionary_value
))
70 result
->reserve(dictionary_value
->size());
71 for (base::DictionaryValue::Iterator
iter(*dictionary_value
);
72 !iter
.IsAtEnd(); iter
.Advance()) {
73 std::string string_value
;
74 if (!iter
.value().GetAsString(&string_value
))
77 FileResource::OpenWithLink open_with_link
;
78 open_with_link
.app_id
= iter
.key();
79 open_with_link
.open_url
= GURL(string_value
);
80 result
->push_back(open_with_link
);
86 // Drive v2 API JSON names.
88 // Definition order follows the order of documentation in
89 // https://developers.google.com/drive/v2/reference/
92 const char kKind
[] = "kind";
93 const char kId
[] = "id";
94 const char kETag
[] = "etag";
95 const char kItems
[] = "items";
96 const char kLargestChangeId
[] = "largestChangeId";
99 // https://developers.google.com/drive/v2/reference/about
100 const char kAboutKind
[] = "drive#about";
101 const char kQuotaBytesTotal
[] = "quotaBytesTotal";
102 const char kQuotaBytesUsedAggregate
[] = "quotaBytesUsedAggregate";
103 const char kRootFolderId
[] = "rootFolderId";
106 // https://developers.google.com/drive/v2/reference/apps
107 const char kCategory
[] = "category";
108 const char kSize
[] = "size";
109 const char kIconUrl
[] = "iconUrl";
112 // https://developers.google.com/drive/v2/reference/apps
113 const char kAppKind
[] = "drive#app";
114 const char kName
[] = "name";
115 const char kObjectType
[] = "objectType";
116 const char kProductId
[] = "productId";
117 const char kSupportsCreate
[] = "supportsCreate";
118 const char kRemovable
[] = "removable";
119 const char kPrimaryMimeTypes
[] = "primaryMimeTypes";
120 const char kSecondaryMimeTypes
[] = "secondaryMimeTypes";
121 const char kPrimaryFileExtensions
[] = "primaryFileExtensions";
122 const char kSecondaryFileExtensions
[] = "secondaryFileExtensions";
123 const char kIcons
[] = "icons";
124 const char kCreateUrl
[] = "createUrl";
127 // https://developers.google.com/drive/v2/reference/apps/list
128 const char kAppListKind
[] = "drive#appList";
131 // https://developers.google.com/drive/v2/reference/parents
132 const char kParentReferenceKind
[] = "drive#parentReference";
133 const char kParentLink
[] = "parentLink";
136 // https://developers.google.com/drive/v2/reference/files
137 const char kFileKind
[] = "drive#file";
138 const char kTitle
[] = "title";
139 const char kMimeType
[] = "mimeType";
140 const char kCreatedDate
[] = "createdDate";
141 const char kModificationDate
[] = "modificationDate";
142 const char kModifiedDate
[] = "modifiedDate";
143 const char kLastViewedByMeDate
[] = "lastViewedByMeDate";
144 const char kSharedWithMeDate
[] = "sharedWithMeDate";
145 const char kMd5Checksum
[] = "md5Checksum";
146 const char kFileSize
[] = "fileSize";
147 const char kAlternateLink
[] = "alternateLink";
148 const char kParents
[] = "parents";
149 const char kOpenWithLinks
[] = "openWithLinks";
150 const char kLabels
[] = "labels";
151 const char kImageMediaMetadata
[] = "imageMediaMetadata";
152 const char kShared
[] = "shared";
153 // These 5 flags are defined under |labels|.
154 const char kLabelTrashed
[] = "trashed";
155 // These 3 flags are defined under |imageMediaMetadata|.
156 const char kImageMediaMetadataWidth
[] = "width";
157 const char kImageMediaMetadataHeight
[] = "height";
158 const char kImageMediaMetadataRotation
[] = "rotation";
159 // URL to the share dialog UI, which is provided only in v2internal.
160 const char kShareLink
[] = "shareLink";
162 const char kDriveFolderMimeType
[] = "application/vnd.google-apps.folder";
165 // https://developers.google.com/drive/v2/reference/files/list
166 const char kFileListKind
[] = "drive#fileList";
167 const char kNextLink
[] = "nextLink";
170 // https://developers.google.com/drive/v2/reference/changes
171 const char kChangeKind
[] = "drive#change";
172 const char kFileId
[] = "fileId";
173 const char kDeleted
[] = "deleted";
174 const char kFile
[] = "file";
177 // https://developers.google.com/drive/v2/reference/changes/list
178 const char kChangeListKind
[] = "drive#changeList";
180 // Maps category name to enum IconCategory.
181 struct AppIconCategoryMap
{
182 DriveAppIcon::IconCategory category
;
183 const char* category_name
;
186 const AppIconCategoryMap kAppIconCategoryMap
[] = {
187 { DriveAppIcon::DOCUMENT
, "document" },
188 { DriveAppIcon::APPLICATION
, "application" },
189 { DriveAppIcon::SHARED_DOCUMENT
, "documentShared" },
192 // Checks if the JSON is expected kind. In Drive API, JSON data structure has
193 // |kind| property which denotes the type of the structure (e.g. "drive#file").
194 bool IsResourceKindExpected(const base::Value
& value
,
195 const std::string
& expected_kind
) {
196 const base::DictionaryValue
* as_dict
= NULL
;
198 return value
.GetAsDictionary(&as_dict
) &&
199 as_dict
->HasKey(kKind
) &&
200 as_dict
->GetString(kKind
, &kind
) &&
201 kind
== expected_kind
;
206 ////////////////////////////////////////////////////////////////////////////////
207 // AboutResource implementation
209 AboutResource::AboutResource()
210 : largest_change_id_(0),
211 quota_bytes_total_(0),
212 quota_bytes_used_aggregate_(0) {}
214 AboutResource::~AboutResource() {}
217 scoped_ptr
<AboutResource
> AboutResource::CreateFrom(const base::Value
& value
) {
218 scoped_ptr
<AboutResource
> resource(new AboutResource());
219 if (!IsResourceKindExpected(value
, kAboutKind
) || !resource
->Parse(value
)) {
220 LOG(ERROR
) << "Unable to create: Invalid About resource JSON!";
221 return scoped_ptr
<AboutResource
>();
223 return resource
.Pass();
227 void AboutResource::RegisterJSONConverter(
228 base::JSONValueConverter
<AboutResource
>* converter
) {
229 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
230 &AboutResource::largest_change_id_
,
231 &base::StringToInt64
);
232 converter
->RegisterCustomField
<int64
>(kQuotaBytesTotal
,
233 &AboutResource::quota_bytes_total_
,
234 &base::StringToInt64
);
235 converter
->RegisterCustomField
<int64
>(
236 kQuotaBytesUsedAggregate
,
237 &AboutResource::quota_bytes_used_aggregate_
,
238 &base::StringToInt64
);
239 converter
->RegisterStringField(kRootFolderId
,
240 &AboutResource::root_folder_id_
);
243 bool AboutResource::Parse(const base::Value
& value
) {
244 base::JSONValueConverter
<AboutResource
> converter
;
245 if (!converter
.Convert(value
, this)) {
246 LOG(ERROR
) << "Unable to parse: Invalid About resource JSON!";
252 ////////////////////////////////////////////////////////////////////////////////
253 // DriveAppIcon implementation
255 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN
), icon_side_length_(0) {}
257 DriveAppIcon::~DriveAppIcon() {}
260 void DriveAppIcon::RegisterJSONConverter(
261 base::JSONValueConverter
<DriveAppIcon
>* converter
) {
262 converter
->RegisterCustomField
<IconCategory
>(
264 &DriveAppIcon::category_
,
265 &DriveAppIcon::GetIconCategory
);
266 converter
->RegisterIntField(kSize
, &DriveAppIcon::icon_side_length_
);
267 converter
->RegisterCustomField
<GURL
>(kIconUrl
,
268 &DriveAppIcon::icon_url_
,
273 scoped_ptr
<DriveAppIcon
> DriveAppIcon::CreateFrom(const base::Value
& value
) {
274 scoped_ptr
<DriveAppIcon
> resource(new DriveAppIcon());
275 if (!resource
->Parse(value
)) {
276 LOG(ERROR
) << "Unable to create: Invalid DriveAppIcon JSON!";
277 return scoped_ptr
<DriveAppIcon
>();
279 return resource
.Pass();
282 bool DriveAppIcon::Parse(const base::Value
& value
) {
283 base::JSONValueConverter
<DriveAppIcon
> converter
;
284 if (!converter
.Convert(value
, this)) {
285 LOG(ERROR
) << "Unable to parse: Invalid DriveAppIcon";
292 bool DriveAppIcon::GetIconCategory(const base::StringPiece
& category
,
293 DriveAppIcon::IconCategory
* result
) {
294 for (size_t i
= 0; i
< arraysize(kAppIconCategoryMap
); i
++) {
295 if (category
== kAppIconCategoryMap
[i
].category_name
) {
296 *result
= kAppIconCategoryMap
[i
].category
;
300 DVLOG(1) << "Unknown icon category " << category
;
304 ////////////////////////////////////////////////////////////////////////////////
305 // AppResource implementation
307 AppResource::AppResource()
308 : supports_create_(false),
312 AppResource::~AppResource() {}
315 void AppResource::RegisterJSONConverter(
316 base::JSONValueConverter
<AppResource
>* converter
) {
317 converter
->RegisterStringField(kId
, &AppResource::application_id_
);
318 converter
->RegisterStringField(kName
, &AppResource::name_
);
319 converter
->RegisterStringField(kObjectType
, &AppResource::object_type_
);
320 converter
->RegisterStringField(kProductId
, &AppResource::product_id_
);
321 converter
->RegisterBoolField(kSupportsCreate
, &AppResource::supports_create_
);
322 converter
->RegisterBoolField(kRemovable
, &AppResource::removable_
);
323 converter
->RegisterRepeatedString(kPrimaryMimeTypes
,
324 &AppResource::primary_mimetypes_
);
325 converter
->RegisterRepeatedString(kSecondaryMimeTypes
,
326 &AppResource::secondary_mimetypes_
);
327 converter
->RegisterRepeatedString(kPrimaryFileExtensions
,
328 &AppResource::primary_file_extensions_
);
329 converter
->RegisterRepeatedString(kSecondaryFileExtensions
,
330 &AppResource::secondary_file_extensions_
);
331 converter
->RegisterRepeatedMessage(kIcons
, &AppResource::icons_
);
332 converter
->RegisterCustomField
<GURL
>(kCreateUrl
,
333 &AppResource::create_url_
,
338 scoped_ptr
<AppResource
> AppResource::CreateFrom(const base::Value
& value
) {
339 scoped_ptr
<AppResource
> resource(new AppResource());
340 if (!IsResourceKindExpected(value
, kAppKind
) || !resource
->Parse(value
)) {
341 LOG(ERROR
) << "Unable to create: Invalid AppResource JSON!";
342 return scoped_ptr
<AppResource
>();
344 return resource
.Pass();
347 bool AppResource::Parse(const base::Value
& value
) {
348 base::JSONValueConverter
<AppResource
> converter
;
349 if (!converter
.Convert(value
, this)) {
350 LOG(ERROR
) << "Unable to parse: Invalid AppResource";
356 ////////////////////////////////////////////////////////////////////////////////
357 // AppList implementation
359 AppList::AppList() {}
361 AppList::~AppList() {}
364 void AppList::RegisterJSONConverter(
365 base::JSONValueConverter
<AppList
>* converter
) {
366 converter
->RegisterStringField(kETag
, &AppList::etag_
);
367 converter
->RegisterRepeatedMessage
<AppResource
>(kItems
,
372 scoped_ptr
<AppList
> AppList::CreateFrom(const base::Value
& value
) {
373 scoped_ptr
<AppList
> resource(new AppList());
374 if (!IsResourceKindExpected(value
, kAppListKind
) || !resource
->Parse(value
)) {
375 LOG(ERROR
) << "Unable to create: Invalid AppList JSON!";
376 return scoped_ptr
<AppList
>();
378 return resource
.Pass();
381 bool AppList::Parse(const base::Value
& value
) {
382 base::JSONValueConverter
<AppList
> converter
;
383 if (!converter
.Convert(value
, this)) {
384 LOG(ERROR
) << "Unable to parse: Invalid AppList";
390 ////////////////////////////////////////////////////////////////////////////////
391 // ParentReference implementation
393 ParentReference::ParentReference() {}
395 ParentReference::~ParentReference() {}
398 void ParentReference::RegisterJSONConverter(
399 base::JSONValueConverter
<ParentReference
>* converter
) {
400 converter
->RegisterStringField(kId
, &ParentReference::file_id_
);
401 converter
->RegisterCustomField
<GURL
>(kParentLink
,
402 &ParentReference::parent_link_
,
407 scoped_ptr
<ParentReference
>
408 ParentReference::CreateFrom(const base::Value
& value
) {
409 scoped_ptr
<ParentReference
> reference(new ParentReference());
410 if (!IsResourceKindExpected(value
, kParentReferenceKind
) ||
411 !reference
->Parse(value
)) {
412 LOG(ERROR
) << "Unable to create: Invalid ParentRefernce JSON!";
413 return scoped_ptr
<ParentReference
>();
415 return reference
.Pass();
418 bool ParentReference::Parse(const base::Value
& value
) {
419 base::JSONValueConverter
<ParentReference
> converter
;
420 if (!converter
.Convert(value
, this)) {
421 LOG(ERROR
) << "Unable to parse: Invalid ParentReference";
427 ////////////////////////////////////////////////////////////////////////////////
428 // FileResource implementation
430 FileResource::FileResource() : shared_(false), file_size_(kUnsetFileSize
) {}
432 FileResource::~FileResource() {}
435 void FileResource::RegisterJSONConverter(
436 base::JSONValueConverter
<FileResource
>* converter
) {
437 converter
->RegisterStringField(kId
, &FileResource::file_id_
);
438 converter
->RegisterStringField(kETag
, &FileResource::etag_
);
439 converter
->RegisterStringField(kTitle
, &FileResource::title_
);
440 converter
->RegisterStringField(kMimeType
, &FileResource::mime_type_
);
441 converter
->RegisterNestedField(kLabels
, &FileResource::labels_
);
442 converter
->RegisterNestedField(kImageMediaMetadata
,
443 &FileResource::image_media_metadata_
);
444 converter
->RegisterCustomField
<base::Time
>(
446 &FileResource::created_date_
,
447 &util::GetTimeFromString
);
448 converter
->RegisterCustomField
<base::Time
>(
450 &FileResource::modified_date_
,
451 &util::GetTimeFromString
);
452 converter
->RegisterCustomField
<base::Time
>(
454 &FileResource::last_viewed_by_me_date_
,
455 &util::GetTimeFromString
);
456 converter
->RegisterCustomField
<base::Time
>(
458 &FileResource::shared_with_me_date_
,
459 &util::GetTimeFromString
);
460 converter
->RegisterBoolField(kShared
, &FileResource::shared_
);
461 converter
->RegisterStringField(kMd5Checksum
, &FileResource::md5_checksum_
);
462 converter
->RegisterCustomField
<int64
>(kFileSize
,
463 &FileResource::file_size_
,
464 &base::StringToInt64
);
465 converter
->RegisterCustomField
<GURL
>(kAlternateLink
,
466 &FileResource::alternate_link_
,
468 converter
->RegisterCustomField
<GURL
>(kShareLink
,
469 &FileResource::share_link_
,
471 converter
->RegisterCustomValueField
<std::vector
<ParentReference
> >(
473 &FileResource::parents_
,
474 GetParentsFromValue
);
475 converter
->RegisterCustomValueField
<std::vector
<OpenWithLink
> >(
477 &FileResource::open_with_links_
,
478 GetOpenWithLinksFromDictionaryValue
);
482 scoped_ptr
<FileResource
> FileResource::CreateFrom(const base::Value
& value
) {
483 scoped_ptr
<FileResource
> resource(new FileResource());
484 if (!IsResourceKindExpected(value
, kFileKind
) || !resource
->Parse(value
)) {
485 LOG(ERROR
) << "Unable to create: Invalid FileResource JSON!";
486 return scoped_ptr
<FileResource
>();
488 return resource
.Pass();
491 bool FileResource::IsDirectory() const {
492 return mime_type_
== kDriveFolderMimeType
;
495 bool FileResource::IsHostedDocument() const {
496 // Hosted documents don't have fileSize field set:
497 // https://developers.google.com/drive/v2/reference/files
498 return !IsDirectory() && file_size_
== kUnsetFileSize
;
501 bool FileResource::Parse(const base::Value
& value
) {
502 base::JSONValueConverter
<FileResource
> converter
;
503 if (!converter
.Convert(value
, this)) {
504 LOG(ERROR
) << "Unable to parse: Invalid FileResource";
510 ////////////////////////////////////////////////////////////////////////////////
511 // FileList implementation
513 FileList::FileList() {}
515 FileList::~FileList() {}
518 void FileList::RegisterJSONConverter(
519 base::JSONValueConverter
<FileList
>* converter
) {
520 converter
->RegisterCustomField
<GURL
>(kNextLink
,
521 &FileList::next_link_
,
523 converter
->RegisterRepeatedMessage
<FileResource
>(kItems
,
528 bool FileList::HasFileListKind(const base::Value
& value
) {
529 return IsResourceKindExpected(value
, kFileListKind
);
533 scoped_ptr
<FileList
> FileList::CreateFrom(const base::Value
& value
) {
534 scoped_ptr
<FileList
> resource(new FileList());
535 if (!HasFileListKind(value
) || !resource
->Parse(value
)) {
536 LOG(ERROR
) << "Unable to create: Invalid FileList JSON!";
537 return scoped_ptr
<FileList
>();
539 return resource
.Pass();
542 bool FileList::Parse(const base::Value
& value
) {
543 base::JSONValueConverter
<FileList
> converter
;
544 if (!converter
.Convert(value
, this)) {
545 LOG(ERROR
) << "Unable to parse: Invalid FileList";
551 ////////////////////////////////////////////////////////////////////////////////
552 // ChangeResource implementation
554 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
556 ChangeResource::~ChangeResource() {}
559 void ChangeResource::RegisterJSONConverter(
560 base::JSONValueConverter
<ChangeResource
>* converter
) {
561 converter
->RegisterCustomField
<int64
>(kId
,
562 &ChangeResource::change_id_
,
563 &base::StringToInt64
);
564 converter
->RegisterStringField(kFileId
, &ChangeResource::file_id_
);
565 converter
->RegisterBoolField(kDeleted
, &ChangeResource::deleted_
);
566 converter
->RegisterCustomValueField(kFile
, &ChangeResource::file_
,
567 &CreateFileResourceFromValue
);
568 converter
->RegisterCustomField
<base::Time
>(
569 kModificationDate
, &ChangeResource::modification_date_
,
570 &util::GetTimeFromString
);
574 scoped_ptr
<ChangeResource
>
575 ChangeResource::CreateFrom(const base::Value
& value
) {
576 scoped_ptr
<ChangeResource
> resource(new ChangeResource());
577 if (!IsResourceKindExpected(value
, kChangeKind
) || !resource
->Parse(value
)) {
578 LOG(ERROR
) << "Unable to create: Invalid ChangeResource JSON!";
579 return scoped_ptr
<ChangeResource
>();
581 return resource
.Pass();
584 bool ChangeResource::Parse(const base::Value
& value
) {
585 base::JSONValueConverter
<ChangeResource
> converter
;
586 if (!converter
.Convert(value
, this)) {
587 LOG(ERROR
) << "Unable to parse: Invalid ChangeResource";
593 ////////////////////////////////////////////////////////////////////////////////
594 // ChangeList implementation
596 ChangeList::ChangeList() : largest_change_id_(0) {}
598 ChangeList::~ChangeList() {}
601 void ChangeList::RegisterJSONConverter(
602 base::JSONValueConverter
<ChangeList
>* converter
) {
603 converter
->RegisterCustomField
<GURL
>(kNextLink
,
604 &ChangeList::next_link_
,
606 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
607 &ChangeList::largest_change_id_
,
608 &base::StringToInt64
);
609 converter
->RegisterRepeatedMessage
<ChangeResource
>(kItems
,
610 &ChangeList::items_
);
614 bool ChangeList::HasChangeListKind(const base::Value
& value
) {
615 return IsResourceKindExpected(value
, kChangeListKind
);
619 scoped_ptr
<ChangeList
> ChangeList::CreateFrom(const base::Value
& value
) {
620 scoped_ptr
<ChangeList
> resource(new ChangeList());
621 if (!HasChangeListKind(value
) || !resource
->Parse(value
)) {
622 LOG(ERROR
) << "Unable to create: Invalid ChangeList JSON!";
623 return scoped_ptr
<ChangeList
>();
625 return resource
.Pass();
628 bool ChangeList::Parse(const base::Value
& value
) {
629 base::JSONValueConverter
<ChangeList
> converter
;
630 if (!converter
.Convert(value
, this)) {
631 LOG(ERROR
) << "Unable to parse: Invalid ChangeList";
638 ////////////////////////////////////////////////////////////////////////////////
639 // FileLabels implementation
641 FileLabels::FileLabels() : trashed_(false) {}
643 FileLabels::~FileLabels() {}
646 void FileLabels::RegisterJSONConverter(
647 base::JSONValueConverter
<FileLabels
>* converter
) {
648 converter
->RegisterBoolField(kLabelTrashed
, &FileLabels::trashed_
);
652 scoped_ptr
<FileLabels
> FileLabels::CreateFrom(const base::Value
& value
) {
653 scoped_ptr
<FileLabels
> resource(new FileLabels());
654 if (!resource
->Parse(value
)) {
655 LOG(ERROR
) << "Unable to create: Invalid FileLabels JSON!";
656 return scoped_ptr
<FileLabels
>();
658 return resource
.Pass();
661 bool FileLabels::Parse(const base::Value
& value
) {
662 base::JSONValueConverter
<FileLabels
> converter
;
663 if (!converter
.Convert(value
, this)) {
664 LOG(ERROR
) << "Unable to parse: Invalid FileLabels.";
670 ////////////////////////////////////////////////////////////////////////////////
671 // ImageMediaMetadata implementation
673 ImageMediaMetadata::ImageMediaMetadata()
678 ImageMediaMetadata::~ImageMediaMetadata() {}
681 void ImageMediaMetadata::RegisterJSONConverter(
682 base::JSONValueConverter
<ImageMediaMetadata
>* converter
) {
683 converter
->RegisterIntField(kImageMediaMetadataWidth
,
684 &ImageMediaMetadata::width_
);
685 converter
->RegisterIntField(kImageMediaMetadataHeight
,
686 &ImageMediaMetadata::height_
);
687 converter
->RegisterIntField(kImageMediaMetadataRotation
,
688 &ImageMediaMetadata::rotation_
);
692 scoped_ptr
<ImageMediaMetadata
> ImageMediaMetadata::CreateFrom(
693 const base::Value
& value
) {
694 scoped_ptr
<ImageMediaMetadata
> resource(new ImageMediaMetadata());
695 if (!resource
->Parse(value
)) {
696 LOG(ERROR
) << "Unable to create: Invalid ImageMediaMetadata JSON!";
697 return scoped_ptr
<ImageMediaMetadata
>();
699 return resource
.Pass();
702 bool ImageMediaMetadata::Parse(const base::Value
& value
) {
703 base::JSONValueConverter
<ImageMediaMetadata
> converter
;
704 if (!converter
.Convert(value
, this)) {
705 LOG(ERROR
) << "Unable to parse: Invalid ImageMediaMetadata.";
711 } // namespace google_apis