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 kQuotaBytesUsed
[] = "quotaBytesUsed";
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_(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
>(kQuotaBytesUsed
,
236 &AboutResource::quota_bytes_used_
,
237 &base::StringToInt64
);
238 converter
->RegisterStringField(kRootFolderId
,
239 &AboutResource::root_folder_id_
);
242 bool AboutResource::Parse(const base::Value
& value
) {
243 base::JSONValueConverter
<AboutResource
> converter
;
244 if (!converter
.Convert(value
, this)) {
245 LOG(ERROR
) << "Unable to parse: Invalid About resource JSON!";
251 ////////////////////////////////////////////////////////////////////////////////
252 // DriveAppIcon implementation
254 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN
), icon_side_length_(0) {}
256 DriveAppIcon::~DriveAppIcon() {}
259 void DriveAppIcon::RegisterJSONConverter(
260 base::JSONValueConverter
<DriveAppIcon
>* converter
) {
261 converter
->RegisterCustomField
<IconCategory
>(
263 &DriveAppIcon::category_
,
264 &DriveAppIcon::GetIconCategory
);
265 converter
->RegisterIntField(kSize
, &DriveAppIcon::icon_side_length_
);
266 converter
->RegisterCustomField
<GURL
>(kIconUrl
,
267 &DriveAppIcon::icon_url_
,
272 scoped_ptr
<DriveAppIcon
> DriveAppIcon::CreateFrom(const base::Value
& value
) {
273 scoped_ptr
<DriveAppIcon
> resource(new DriveAppIcon());
274 if (!resource
->Parse(value
)) {
275 LOG(ERROR
) << "Unable to create: Invalid DriveAppIcon JSON!";
276 return scoped_ptr
<DriveAppIcon
>();
278 return resource
.Pass();
281 bool DriveAppIcon::Parse(const base::Value
& value
) {
282 base::JSONValueConverter
<DriveAppIcon
> converter
;
283 if (!converter
.Convert(value
, this)) {
284 LOG(ERROR
) << "Unable to parse: Invalid DriveAppIcon";
291 bool DriveAppIcon::GetIconCategory(const base::StringPiece
& category
,
292 DriveAppIcon::IconCategory
* result
) {
293 for (size_t i
= 0; i
< arraysize(kAppIconCategoryMap
); i
++) {
294 if (category
== kAppIconCategoryMap
[i
].category_name
) {
295 *result
= kAppIconCategoryMap
[i
].category
;
299 DVLOG(1) << "Unknown icon category " << category
;
303 ////////////////////////////////////////////////////////////////////////////////
304 // AppResource implementation
306 AppResource::AppResource()
307 : supports_create_(false),
311 AppResource::~AppResource() {}
314 void AppResource::RegisterJSONConverter(
315 base::JSONValueConverter
<AppResource
>* converter
) {
316 converter
->RegisterStringField(kId
, &AppResource::application_id_
);
317 converter
->RegisterStringField(kName
, &AppResource::name_
);
318 converter
->RegisterStringField(kObjectType
, &AppResource::object_type_
);
319 converter
->RegisterStringField(kProductId
, &AppResource::product_id_
);
320 converter
->RegisterBoolField(kSupportsCreate
, &AppResource::supports_create_
);
321 converter
->RegisterBoolField(kRemovable
, &AppResource::removable_
);
322 converter
->RegisterRepeatedString(kPrimaryMimeTypes
,
323 &AppResource::primary_mimetypes_
);
324 converter
->RegisterRepeatedString(kSecondaryMimeTypes
,
325 &AppResource::secondary_mimetypes_
);
326 converter
->RegisterRepeatedString(kPrimaryFileExtensions
,
327 &AppResource::primary_file_extensions_
);
328 converter
->RegisterRepeatedString(kSecondaryFileExtensions
,
329 &AppResource::secondary_file_extensions_
);
330 converter
->RegisterRepeatedMessage(kIcons
, &AppResource::icons_
);
331 converter
->RegisterCustomField
<GURL
>(kCreateUrl
,
332 &AppResource::create_url_
,
337 scoped_ptr
<AppResource
> AppResource::CreateFrom(const base::Value
& value
) {
338 scoped_ptr
<AppResource
> resource(new AppResource());
339 if (!IsResourceKindExpected(value
, kAppKind
) || !resource
->Parse(value
)) {
340 LOG(ERROR
) << "Unable to create: Invalid AppResource JSON!";
341 return scoped_ptr
<AppResource
>();
343 return resource
.Pass();
346 bool AppResource::Parse(const base::Value
& value
) {
347 base::JSONValueConverter
<AppResource
> converter
;
348 if (!converter
.Convert(value
, this)) {
349 LOG(ERROR
) << "Unable to parse: Invalid AppResource";
355 ////////////////////////////////////////////////////////////////////////////////
356 // AppList implementation
358 AppList::AppList() {}
360 AppList::~AppList() {}
363 void AppList::RegisterJSONConverter(
364 base::JSONValueConverter
<AppList
>* converter
) {
365 converter
->RegisterStringField(kETag
, &AppList::etag_
);
366 converter
->RegisterRepeatedMessage
<AppResource
>(kItems
,
371 scoped_ptr
<AppList
> AppList::CreateFrom(const base::Value
& value
) {
372 scoped_ptr
<AppList
> resource(new AppList());
373 if (!IsResourceKindExpected(value
, kAppListKind
) || !resource
->Parse(value
)) {
374 LOG(ERROR
) << "Unable to create: Invalid AppList JSON!";
375 return scoped_ptr
<AppList
>();
377 return resource
.Pass();
380 bool AppList::Parse(const base::Value
& value
) {
381 base::JSONValueConverter
<AppList
> converter
;
382 if (!converter
.Convert(value
, this)) {
383 LOG(ERROR
) << "Unable to parse: Invalid AppList";
389 ////////////////////////////////////////////////////////////////////////////////
390 // ParentReference implementation
392 ParentReference::ParentReference() {}
394 ParentReference::~ParentReference() {}
397 void ParentReference::RegisterJSONConverter(
398 base::JSONValueConverter
<ParentReference
>* converter
) {
399 converter
->RegisterStringField(kId
, &ParentReference::file_id_
);
400 converter
->RegisterCustomField
<GURL
>(kParentLink
,
401 &ParentReference::parent_link_
,
406 scoped_ptr
<ParentReference
>
407 ParentReference::CreateFrom(const base::Value
& value
) {
408 scoped_ptr
<ParentReference
> reference(new ParentReference());
409 if (!IsResourceKindExpected(value
, kParentReferenceKind
) ||
410 !reference
->Parse(value
)) {
411 LOG(ERROR
) << "Unable to create: Invalid ParentRefernce JSON!";
412 return scoped_ptr
<ParentReference
>();
414 return reference
.Pass();
417 bool ParentReference::Parse(const base::Value
& value
) {
418 base::JSONValueConverter
<ParentReference
> converter
;
419 if (!converter
.Convert(value
, this)) {
420 LOG(ERROR
) << "Unable to parse: Invalid ParentReference";
426 ////////////////////////////////////////////////////////////////////////////////
427 // FileResource implementation
429 FileResource::FileResource() : shared_(false), file_size_(kUnsetFileSize
) {}
431 FileResource::~FileResource() {}
434 void FileResource::RegisterJSONConverter(
435 base::JSONValueConverter
<FileResource
>* converter
) {
436 converter
->RegisterStringField(kId
, &FileResource::file_id_
);
437 converter
->RegisterStringField(kETag
, &FileResource::etag_
);
438 converter
->RegisterStringField(kTitle
, &FileResource::title_
);
439 converter
->RegisterStringField(kMimeType
, &FileResource::mime_type_
);
440 converter
->RegisterNestedField(kLabels
, &FileResource::labels_
);
441 converter
->RegisterNestedField(kImageMediaMetadata
,
442 &FileResource::image_media_metadata_
);
443 converter
->RegisterCustomField
<base::Time
>(
445 &FileResource::created_date_
,
446 &util::GetTimeFromString
);
447 converter
->RegisterCustomField
<base::Time
>(
449 &FileResource::modified_date_
,
450 &util::GetTimeFromString
);
451 converter
->RegisterCustomField
<base::Time
>(
453 &FileResource::last_viewed_by_me_date_
,
454 &util::GetTimeFromString
);
455 converter
->RegisterCustomField
<base::Time
>(
457 &FileResource::shared_with_me_date_
,
458 &util::GetTimeFromString
);
459 converter
->RegisterBoolField(kShared
, &FileResource::shared_
);
460 converter
->RegisterStringField(kMd5Checksum
, &FileResource::md5_checksum_
);
461 converter
->RegisterCustomField
<int64
>(kFileSize
,
462 &FileResource::file_size_
,
463 &base::StringToInt64
);
464 converter
->RegisterCustomField
<GURL
>(kAlternateLink
,
465 &FileResource::alternate_link_
,
467 converter
->RegisterCustomField
<GURL
>(kShareLink
,
468 &FileResource::share_link_
,
470 converter
->RegisterCustomValueField
<std::vector
<ParentReference
> >(
472 &FileResource::parents_
,
473 GetParentsFromValue
);
474 converter
->RegisterCustomValueField
<std::vector
<OpenWithLink
> >(
476 &FileResource::open_with_links_
,
477 GetOpenWithLinksFromDictionaryValue
);
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::IsHostedDocument() const {
495 // Hosted documents don't have fileSize field set:
496 // https://developers.google.com/drive/v2/reference/files
497 return !IsDirectory() && file_size_
== kUnsetFileSize
;
500 bool FileResource::Parse(const base::Value
& value
) {
501 base::JSONValueConverter
<FileResource
> converter
;
502 if (!converter
.Convert(value
, this)) {
503 LOG(ERROR
) << "Unable to parse: Invalid FileResource";
509 ////////////////////////////////////////////////////////////////////////////////
510 // FileList implementation
512 FileList::FileList() {}
514 FileList::~FileList() {}
517 void FileList::RegisterJSONConverter(
518 base::JSONValueConverter
<FileList
>* converter
) {
519 converter
->RegisterCustomField
<GURL
>(kNextLink
,
520 &FileList::next_link_
,
522 converter
->RegisterRepeatedMessage
<FileResource
>(kItems
,
527 bool FileList::HasFileListKind(const base::Value
& value
) {
528 return IsResourceKindExpected(value
, kFileListKind
);
532 scoped_ptr
<FileList
> FileList::CreateFrom(const base::Value
& value
) {
533 scoped_ptr
<FileList
> resource(new FileList());
534 if (!HasFileListKind(value
) || !resource
->Parse(value
)) {
535 LOG(ERROR
) << "Unable to create: Invalid FileList JSON!";
536 return scoped_ptr
<FileList
>();
538 return resource
.Pass();
541 bool FileList::Parse(const base::Value
& value
) {
542 base::JSONValueConverter
<FileList
> converter
;
543 if (!converter
.Convert(value
, this)) {
544 LOG(ERROR
) << "Unable to parse: Invalid FileList";
550 ////////////////////////////////////////////////////////////////////////////////
551 // ChangeResource implementation
553 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
555 ChangeResource::~ChangeResource() {}
558 void ChangeResource::RegisterJSONConverter(
559 base::JSONValueConverter
<ChangeResource
>* converter
) {
560 converter
->RegisterCustomField
<int64
>(kId
,
561 &ChangeResource::change_id_
,
562 &base::StringToInt64
);
563 converter
->RegisterStringField(kFileId
, &ChangeResource::file_id_
);
564 converter
->RegisterBoolField(kDeleted
, &ChangeResource::deleted_
);
565 converter
->RegisterCustomValueField(kFile
, &ChangeResource::file_
,
566 &CreateFileResourceFromValue
);
567 converter
->RegisterCustomField
<base::Time
>(
568 kModificationDate
, &ChangeResource::modification_date_
,
569 &util::GetTimeFromString
);
573 scoped_ptr
<ChangeResource
>
574 ChangeResource::CreateFrom(const base::Value
& value
) {
575 scoped_ptr
<ChangeResource
> resource(new ChangeResource());
576 if (!IsResourceKindExpected(value
, kChangeKind
) || !resource
->Parse(value
)) {
577 LOG(ERROR
) << "Unable to create: Invalid ChangeResource JSON!";
578 return scoped_ptr
<ChangeResource
>();
580 return resource
.Pass();
583 bool ChangeResource::Parse(const base::Value
& value
) {
584 base::JSONValueConverter
<ChangeResource
> converter
;
585 if (!converter
.Convert(value
, this)) {
586 LOG(ERROR
) << "Unable to parse: Invalid ChangeResource";
592 ////////////////////////////////////////////////////////////////////////////////
593 // ChangeList implementation
595 ChangeList::ChangeList() : largest_change_id_(0) {}
597 ChangeList::~ChangeList() {}
600 void ChangeList::RegisterJSONConverter(
601 base::JSONValueConverter
<ChangeList
>* converter
) {
602 converter
->RegisterCustomField
<GURL
>(kNextLink
,
603 &ChangeList::next_link_
,
605 converter
->RegisterCustomField
<int64
>(kLargestChangeId
,
606 &ChangeList::largest_change_id_
,
607 &base::StringToInt64
);
608 converter
->RegisterRepeatedMessage
<ChangeResource
>(kItems
,
609 &ChangeList::items_
);
613 bool ChangeList::HasChangeListKind(const base::Value
& value
) {
614 return IsResourceKindExpected(value
, kChangeListKind
);
618 scoped_ptr
<ChangeList
> ChangeList::CreateFrom(const base::Value
& value
) {
619 scoped_ptr
<ChangeList
> resource(new ChangeList());
620 if (!HasChangeListKind(value
) || !resource
->Parse(value
)) {
621 LOG(ERROR
) << "Unable to create: Invalid ChangeList JSON!";
622 return scoped_ptr
<ChangeList
>();
624 return resource
.Pass();
627 bool ChangeList::Parse(const base::Value
& value
) {
628 base::JSONValueConverter
<ChangeList
> converter
;
629 if (!converter
.Convert(value
, this)) {
630 LOG(ERROR
) << "Unable to parse: Invalid ChangeList";
637 ////////////////////////////////////////////////////////////////////////////////
638 // FileLabels implementation
640 FileLabels::FileLabels() : trashed_(false) {}
642 FileLabels::~FileLabels() {}
645 void FileLabels::RegisterJSONConverter(
646 base::JSONValueConverter
<FileLabels
>* converter
) {
647 converter
->RegisterBoolField(kLabelTrashed
, &FileLabels::trashed_
);
651 scoped_ptr
<FileLabels
> FileLabels::CreateFrom(const base::Value
& value
) {
652 scoped_ptr
<FileLabels
> resource(new FileLabels());
653 if (!resource
->Parse(value
)) {
654 LOG(ERROR
) << "Unable to create: Invalid FileLabels JSON!";
655 return scoped_ptr
<FileLabels
>();
657 return resource
.Pass();
660 bool FileLabels::Parse(const base::Value
& value
) {
661 base::JSONValueConverter
<FileLabels
> converter
;
662 if (!converter
.Convert(value
, this)) {
663 LOG(ERROR
) << "Unable to parse: Invalid FileLabels.";
669 ////////////////////////////////////////////////////////////////////////////////
670 // ImageMediaMetadata implementation
672 ImageMediaMetadata::ImageMediaMetadata()
677 ImageMediaMetadata::~ImageMediaMetadata() {}
680 void ImageMediaMetadata::RegisterJSONConverter(
681 base::JSONValueConverter
<ImageMediaMetadata
>* converter
) {
682 converter
->RegisterIntField(kImageMediaMetadataWidth
,
683 &ImageMediaMetadata::width_
);
684 converter
->RegisterIntField(kImageMediaMetadataHeight
,
685 &ImageMediaMetadata::height_
);
686 converter
->RegisterIntField(kImageMediaMetadataRotation
,
687 &ImageMediaMetadata::rotation_
);
691 scoped_ptr
<ImageMediaMetadata
> ImageMediaMetadata::CreateFrom(
692 const base::Value
& value
) {
693 scoped_ptr
<ImageMediaMetadata
> resource(new ImageMediaMetadata());
694 if (!resource
->Parse(value
)) {
695 LOG(ERROR
) << "Unable to create: Invalid ImageMediaMetadata JSON!";
696 return scoped_ptr
<ImageMediaMetadata
>();
698 return resource
.Pass();
701 bool ImageMediaMetadata::Parse(const base::Value
& value
) {
702 base::JSONValueConverter
<ImageMediaMetadata
> converter
;
703 if (!converter
.Convert(value
, this)) {
704 LOG(ERROR
) << "Unable to parse: Invalid ImageMediaMetadata.";
710 } // namespace google_apis