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. 4 5 #include "google_apis/drive/drive_api_parser.h" 6 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" 15 16 namespace google_apis { 17 18 namespace { 19 20 const int64 kUnsetFileSize = -1; 21 22 bool CreateFileResourceFromValue(const base::Value* value, 23 scoped_ptr<FileResource>* file) { 24 *file = FileResource::CreateFrom(*value); 25 return !!*file; 26 } 27 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()); 33 return true; 34 } 35 36 // Converts |value| to |result|. 37 bool GetParentsFromValue(const base::Value* value, 38 std::vector<ParentReference>* result) { 39 DCHECK(value); 40 DCHECK(result); 41 42 const base::ListValue* list_value = NULL; 43 if (!value->GetAsList(&list_value)) 44 return false; 45 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])) 52 return false; 53 } 54 55 return true; 56 } 57 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) { 63 DCHECK(value); 64 DCHECK(result); 65 66 const base::DictionaryValue* dictionary_value; 67 if (!value->GetAsDictionary(&dictionary_value)) 68 return false; 69 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)) 75 return false; 76 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); 81 } 82 83 return true; 84 } 85 86 // Drive v2 API JSON names. 87 88 // Definition order follows the order of documentation in 89 // https://developers.google.com/drive/v2/reference/ 90 91 // Common 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"; 97 98 // About Resource 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"; 104 105 // App Icon 106 // https://developers.google.com/drive/v2/reference/apps 107 const char kCategory[] = "category"; 108 const char kSize[] = "size"; 109 const char kIconUrl[] = "iconUrl"; 110 111 // Apps Resource 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"; 125 126 // Apps List 127 // https://developers.google.com/drive/v2/reference/apps/list 128 const char kAppListKind[] = "drive#appList"; 129 130 // Parent Resource 131 // https://developers.google.com/drive/v2/reference/parents 132 const char kParentReferenceKind[] = "drive#parentReference"; 133 const char kParentLink[] = "parentLink"; 134 135 // File Resource 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 160 const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder"; 161 162 // Files List 163 // https://developers.google.com/drive/v2/reference/files/list 164 const char kFileListKind[] = "drive#fileList"; 165 const char kNextLink[] = "nextLink"; 166 167 // Change Resource 168 // https://developers.google.com/drive/v2/reference/changes 169 const char kChangeKind[] = "drive#change"; 170 const char kFileId[] = "fileId"; 171 const char kDeleted[] = "deleted"; 172 const char kFile[] = "file"; 173 174 // Changes List 175 // https://developers.google.com/drive/v2/reference/changes/list 176 const char kChangeListKind[] = "drive#changeList"; 177 178 // Maps category name to enum IconCategory. 179 struct AppIconCategoryMap { 180 DriveAppIcon::IconCategory category; 181 const char* category_name; 182 }; 183 184 const AppIconCategoryMap kAppIconCategoryMap[] = { 185 { DriveAppIcon::DOCUMENT, "document" }, 186 { DriveAppIcon::APPLICATION, "application" }, 187 { DriveAppIcon::SHARED_DOCUMENT, "documentShared" }, 188 }; 189 190 // Checks if the JSON is expected kind. In Drive API, JSON data structure has 191 // |kind| property which denotes the type of the structure (e.g. "drive#file"). 192 bool IsResourceKindExpected(const base::Value& value, 193 const std::string& expected_kind) { 194 const base::DictionaryValue* as_dict = NULL; 195 std::string kind; 196 return value.GetAsDictionary(&as_dict) && 197 as_dict->HasKey(kKind) && 198 as_dict->GetString(kKind, &kind) && 199 kind == expected_kind; 200 } 201 202 } // namespace 203 204 //////////////////////////////////////////////////////////////////////////////// 205 // AboutResource implementation 206 207 AboutResource::AboutResource() 208 : largest_change_id_(0), 209 quota_bytes_total_(0), 210 quota_bytes_used_(0) {} 211 212 AboutResource::~AboutResource() {} 213 214 // static 215 scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) { 216 scoped_ptr<AboutResource> resource(new AboutResource()); 217 if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) { 218 LOG(ERROR) << "Unable to create: Invalid About resource JSON!"; 219 return scoped_ptr<AboutResource>(); 220 } 221 return resource.Pass(); 222 } 223 224 // static 225 void AboutResource::RegisterJSONConverter( 226 base::JSONValueConverter<AboutResource>* converter) { 227 converter->RegisterCustomField<int64>(kLargestChangeId, 228 &AboutResource::largest_change_id_, 229 &base::StringToInt64); 230 converter->RegisterCustomField<int64>(kQuotaBytesTotal, 231 &AboutResource::quota_bytes_total_, 232 &base::StringToInt64); 233 converter->RegisterCustomField<int64>(kQuotaBytesUsed, 234 &AboutResource::quota_bytes_used_, 235 &base::StringToInt64); 236 converter->RegisterStringField(kRootFolderId, 237 &AboutResource::root_folder_id_); 238 } 239 240 bool AboutResource::Parse(const base::Value& value) { 241 base::JSONValueConverter<AboutResource> converter; 242 if (!converter.Convert(value, this)) { 243 LOG(ERROR) << "Unable to parse: Invalid About resource JSON!"; 244 return false; 245 } 246 return true; 247 } 248 249 //////////////////////////////////////////////////////////////////////////////// 250 // DriveAppIcon implementation 251 252 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN), icon_side_length_(0) {} 253 254 DriveAppIcon::~DriveAppIcon() {} 255 256 // static 257 void DriveAppIcon::RegisterJSONConverter( 258 base::JSONValueConverter<DriveAppIcon>* converter) { 259 converter->RegisterCustomField<IconCategory>( 260 kCategory, 261 &DriveAppIcon::category_, 262 &DriveAppIcon::GetIconCategory); 263 converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_); 264 converter->RegisterCustomField<GURL>(kIconUrl, 265 &DriveAppIcon::icon_url_, 266 GetGURLFromString); 267 } 268 269 // static 270 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) { 271 scoped_ptr<DriveAppIcon> resource(new DriveAppIcon()); 272 if (!resource->Parse(value)) { 273 LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!"; 274 return scoped_ptr<DriveAppIcon>(); 275 } 276 return resource.Pass(); 277 } 278 279 bool DriveAppIcon::Parse(const base::Value& value) { 280 base::JSONValueConverter<DriveAppIcon> converter; 281 if (!converter.Convert(value, this)) { 282 LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon"; 283 return false; 284 } 285 return true; 286 } 287 288 // static 289 bool DriveAppIcon::GetIconCategory(const base::StringPiece& category, 290 DriveAppIcon::IconCategory* result) { 291 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) { 292 if (category == kAppIconCategoryMap[i].category_name) { 293 *result = kAppIconCategoryMap[i].category; 294 return true; 295 } 296 } 297 DVLOG(1) << "Unknown icon category " << category; 298 return false; 299 } 300 301 //////////////////////////////////////////////////////////////////////////////// 302 // AppResource implementation 303 304 AppResource::AppResource() 305 : supports_create_(false), 306 removable_(false) { 307 } 308 309 AppResource::~AppResource() {} 310 311 // static 312 void AppResource::RegisterJSONConverter( 313 base::JSONValueConverter<AppResource>* converter) { 314 converter->RegisterStringField(kId, &AppResource::application_id_); 315 converter->RegisterStringField(kName, &AppResource::name_); 316 converter->RegisterStringField(kObjectType, &AppResource::object_type_); 317 converter->RegisterStringField(kProductId, &AppResource::product_id_); 318 converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_); 319 converter->RegisterBoolField(kRemovable, &AppResource::removable_); 320 converter->RegisterRepeatedString(kPrimaryMimeTypes, 321 &AppResource::primary_mimetypes_); 322 converter->RegisterRepeatedString(kSecondaryMimeTypes, 323 &AppResource::secondary_mimetypes_); 324 converter->RegisterRepeatedString(kPrimaryFileExtensions, 325 &AppResource::primary_file_extensions_); 326 converter->RegisterRepeatedString(kSecondaryFileExtensions, 327 &AppResource::secondary_file_extensions_); 328 converter->RegisterRepeatedMessage(kIcons, &AppResource::icons_); 329 converter->RegisterCustomField<GURL>(kCreateUrl, 330 &AppResource::create_url_, 331 GetGURLFromString); 332 } 333 334 // static 335 scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) { 336 scoped_ptr<AppResource> resource(new AppResource()); 337 if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) { 338 LOG(ERROR) << "Unable to create: Invalid AppResource JSON!"; 339 return scoped_ptr<AppResource>(); 340 } 341 return resource.Pass(); 342 } 343 344 bool AppResource::Parse(const base::Value& value) { 345 base::JSONValueConverter<AppResource> converter; 346 if (!converter.Convert(value, this)) { 347 LOG(ERROR) << "Unable to parse: Invalid AppResource"; 348 return false; 349 } 350 return true; 351 } 352 353 //////////////////////////////////////////////////////////////////////////////// 354 // AppList implementation 355 356 AppList::AppList() {} 357 358 AppList::~AppList() {} 359 360 // static 361 void AppList::RegisterJSONConverter( 362 base::JSONValueConverter<AppList>* converter) { 363 converter->RegisterStringField(kETag, &AppList::etag_); 364 converter->RegisterRepeatedMessage<AppResource>(kItems, 365 &AppList::items_); 366 } 367 368 // static 369 scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) { 370 scoped_ptr<AppList> resource(new AppList()); 371 if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) { 372 LOG(ERROR) << "Unable to create: Invalid AppList JSON!"; 373 return scoped_ptr<AppList>(); 374 } 375 return resource.Pass(); 376 } 377 378 bool AppList::Parse(const base::Value& value) { 379 base::JSONValueConverter<AppList> converter; 380 if (!converter.Convert(value, this)) { 381 LOG(ERROR) << "Unable to parse: Invalid AppList"; 382 return false; 383 } 384 return true; 385 } 386 387 //////////////////////////////////////////////////////////////////////////////// 388 // ParentReference implementation 389 390 ParentReference::ParentReference() {} 391 392 ParentReference::~ParentReference() {} 393 394 // static 395 void ParentReference::RegisterJSONConverter( 396 base::JSONValueConverter<ParentReference>* converter) { 397 converter->RegisterStringField(kId, &ParentReference::file_id_); 398 converter->RegisterCustomField<GURL>(kParentLink, 399 &ParentReference::parent_link_, 400 GetGURLFromString); 401 } 402 403 // static 404 scoped_ptr<ParentReference> 405 ParentReference::CreateFrom(const base::Value& value) { 406 scoped_ptr<ParentReference> reference(new ParentReference()); 407 if (!IsResourceKindExpected(value, kParentReferenceKind) || 408 !reference->Parse(value)) { 409 LOG(ERROR) << "Unable to create: Invalid ParentRefernce JSON!"; 410 return scoped_ptr<ParentReference>(); 411 } 412 return reference.Pass(); 413 } 414 415 bool ParentReference::Parse(const base::Value& value) { 416 base::JSONValueConverter<ParentReference> converter; 417 if (!converter.Convert(value, this)) { 418 LOG(ERROR) << "Unable to parse: Invalid ParentReference"; 419 return false; 420 } 421 return true; 422 } 423 424 //////////////////////////////////////////////////////////////////////////////// 425 // FileResource implementation 426 427 FileResource::FileResource() : shared_(false), file_size_(kUnsetFileSize) {} 428 429 FileResource::~FileResource() {} 430 431 // static 432 void FileResource::RegisterJSONConverter( 433 base::JSONValueConverter<FileResource>* converter) { 434 converter->RegisterStringField(kId, &FileResource::file_id_); 435 converter->RegisterStringField(kETag, &FileResource::etag_); 436 converter->RegisterStringField(kTitle, &FileResource::title_); 437 converter->RegisterStringField(kMimeType, &FileResource::mime_type_); 438 converter->RegisterNestedField(kLabels, &FileResource::labels_); 439 converter->RegisterNestedField(kImageMediaMetadata, 440 &FileResource::image_media_metadata_); 441 converter->RegisterCustomField<base::Time>( 442 kCreatedDate, 443 &FileResource::created_date_, 444 &util::GetTimeFromString); 445 converter->RegisterCustomField<base::Time>( 446 kModifiedDate, 447 &FileResource::modified_date_, 448 &util::GetTimeFromString); 449 converter->RegisterCustomField<base::Time>( 450 kLastViewedByMeDate, 451 &FileResource::last_viewed_by_me_date_, 452 &util::GetTimeFromString); 453 converter->RegisterCustomField<base::Time>( 454 kSharedWithMeDate, 455 &FileResource::shared_with_me_date_, 456 &util::GetTimeFromString); 457 converter->RegisterBoolField(kShared, &FileResource::shared_); 458 converter->RegisterStringField(kMd5Checksum, &FileResource::md5_checksum_); 459 converter->RegisterCustomField<int64>(kFileSize, 460 &FileResource::file_size_, 461 &base::StringToInt64); 462 converter->RegisterCustomField<GURL>(kAlternateLink, 463 &FileResource::alternate_link_, 464 GetGURLFromString); 465 converter->RegisterCustomValueField<std::vector<ParentReference> >( 466 kParents, 467 &FileResource::parents_, 468 GetParentsFromValue); 469 converter->RegisterCustomValueField<std::vector<OpenWithLink> >( 470 kOpenWithLinks, 471 &FileResource::open_with_links_, 472 GetOpenWithLinksFromDictionaryValue); 473 } 474 475 // static 476 scoped_ptr<FileResource> FileResource::CreateFrom(const base::Value& value) { 477 scoped_ptr<FileResource> resource(new FileResource()); 478 if (!IsResourceKindExpected(value, kFileKind) || !resource->Parse(value)) { 479 LOG(ERROR) << "Unable to create: Invalid FileResource JSON!"; 480 return scoped_ptr<FileResource>(); 481 } 482 return resource.Pass(); 483 } 484 485 bool FileResource::IsDirectory() const { 486 return mime_type_ == kDriveFolderMimeType; 487 } 488 489 bool FileResource::IsHostedDocument() const { 490 // Hosted documents don't have fileSize field set: 491 // https://developers.google.com/drive/v2/reference/files 492 return !IsDirectory() && file_size_ == kUnsetFileSize; 493 } 494 495 bool FileResource::Parse(const base::Value& value) { 496 base::JSONValueConverter<FileResource> converter; 497 if (!converter.Convert(value, this)) { 498 LOG(ERROR) << "Unable to parse: Invalid FileResource"; 499 return false; 500 } 501 return true; 502 } 503 504 //////////////////////////////////////////////////////////////////////////////// 505 // FileList implementation 506 507 FileList::FileList() {} 508 509 FileList::~FileList() {} 510 511 // static 512 void FileList::RegisterJSONConverter( 513 base::JSONValueConverter<FileList>* converter) { 514 converter->RegisterCustomField<GURL>(kNextLink, 515 &FileList::next_link_, 516 GetGURLFromString); 517 converter->RegisterRepeatedMessage<FileResource>(kItems, 518 &FileList::items_); 519 } 520 521 // static 522 bool FileList::HasFileListKind(const base::Value& value) { 523 return IsResourceKindExpected(value, kFileListKind); 524 } 525 526 // static 527 scoped_ptr<FileList> FileList::CreateFrom(const base::Value& value) { 528 scoped_ptr<FileList> resource(new FileList()); 529 if (!HasFileListKind(value) || !resource->Parse(value)) { 530 LOG(ERROR) << "Unable to create: Invalid FileList JSON!"; 531 return scoped_ptr<FileList>(); 532 } 533 return resource.Pass(); 534 } 535 536 bool FileList::Parse(const base::Value& value) { 537 base::JSONValueConverter<FileList> converter; 538 if (!converter.Convert(value, this)) { 539 LOG(ERROR) << "Unable to parse: Invalid FileList"; 540 return false; 541 } 542 return true; 543 } 544 545 //////////////////////////////////////////////////////////////////////////////// 546 // ChangeResource implementation 547 548 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {} 549 550 ChangeResource::~ChangeResource() {} 551 552 // static 553 void ChangeResource::RegisterJSONConverter( 554 base::JSONValueConverter<ChangeResource>* converter) { 555 converter->RegisterCustomField<int64>(kId, 556 &ChangeResource::change_id_, 557 &base::StringToInt64); 558 converter->RegisterStringField(kFileId, &ChangeResource::file_id_); 559 converter->RegisterBoolField(kDeleted, &ChangeResource::deleted_); 560 converter->RegisterCustomValueField(kFile, &ChangeResource::file_, 561 &CreateFileResourceFromValue); 562 converter->RegisterCustomField<base::Time>( 563 kModificationDate, &ChangeResource::modification_date_, 564 &util::GetTimeFromString); 565 } 566 567 // static 568 scoped_ptr<ChangeResource> 569 ChangeResource::CreateFrom(const base::Value& value) { 570 scoped_ptr<ChangeResource> resource(new ChangeResource()); 571 if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) { 572 LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!"; 573 return scoped_ptr<ChangeResource>(); 574 } 575 return resource.Pass(); 576 } 577 578 bool ChangeResource::Parse(const base::Value& value) { 579 base::JSONValueConverter<ChangeResource> converter; 580 if (!converter.Convert(value, this)) { 581 LOG(ERROR) << "Unable to parse: Invalid ChangeResource"; 582 return false; 583 } 584 return true; 585 } 586 587 //////////////////////////////////////////////////////////////////////////////// 588 // ChangeList implementation 589 590 ChangeList::ChangeList() : largest_change_id_(0) {} 591 592 ChangeList::~ChangeList() {} 593 594 // static 595 void ChangeList::RegisterJSONConverter( 596 base::JSONValueConverter<ChangeList>* converter) { 597 converter->RegisterCustomField<GURL>(kNextLink, 598 &ChangeList::next_link_, 599 GetGURLFromString); 600 converter->RegisterCustomField<int64>(kLargestChangeId, 601 &ChangeList::largest_change_id_, 602 &base::StringToInt64); 603 converter->RegisterRepeatedMessage<ChangeResource>(kItems, 604 &ChangeList::items_); 605 } 606 607 // static 608 bool ChangeList::HasChangeListKind(const base::Value& value) { 609 return IsResourceKindExpected(value, kChangeListKind); 610 } 611 612 // static 613 scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) { 614 scoped_ptr<ChangeList> resource(new ChangeList()); 615 if (!HasChangeListKind(value) || !resource->Parse(value)) { 616 LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!"; 617 return scoped_ptr<ChangeList>(); 618 } 619 return resource.Pass(); 620 } 621 622 bool ChangeList::Parse(const base::Value& value) { 623 base::JSONValueConverter<ChangeList> converter; 624 if (!converter.Convert(value, this)) { 625 LOG(ERROR) << "Unable to parse: Invalid ChangeList"; 626 return false; 627 } 628 return true; 629 } 630 631 632 //////////////////////////////////////////////////////////////////////////////// 633 // FileLabels implementation 634 635 FileLabels::FileLabels() : trashed_(false) {} 636 637 FileLabels::~FileLabels() {} 638 639 // static 640 void FileLabels::RegisterJSONConverter( 641 base::JSONValueConverter<FileLabels>* converter) { 642 converter->RegisterBoolField(kLabelTrashed, &FileLabels::trashed_); 643 } 644 645 // static 646 scoped_ptr<FileLabels> FileLabels::CreateFrom(const base::Value& value) { 647 scoped_ptr<FileLabels> resource(new FileLabels()); 648 if (!resource->Parse(value)) { 649 LOG(ERROR) << "Unable to create: Invalid FileLabels JSON!"; 650 return scoped_ptr<FileLabels>(); 651 } 652 return resource.Pass(); 653 } 654 655 bool FileLabels::Parse(const base::Value& value) { 656 base::JSONValueConverter<FileLabels> converter; 657 if (!converter.Convert(value, this)) { 658 LOG(ERROR) << "Unable to parse: Invalid FileLabels."; 659 return false; 660 } 661 return true; 662 } 663 664 //////////////////////////////////////////////////////////////////////////////// 665 // ImageMediaMetadata implementation 666 667 ImageMediaMetadata::ImageMediaMetadata() 668 : width_(-1), 669 height_(-1), 670 rotation_(-1) {} 671 672 ImageMediaMetadata::~ImageMediaMetadata() {} 673 674 // static 675 void ImageMediaMetadata::RegisterJSONConverter( 676 base::JSONValueConverter<ImageMediaMetadata>* converter) { 677 converter->RegisterIntField(kImageMediaMetadataWidth, 678 &ImageMediaMetadata::width_); 679 converter->RegisterIntField(kImageMediaMetadataHeight, 680 &ImageMediaMetadata::height_); 681 converter->RegisterIntField(kImageMediaMetadataRotation, 682 &ImageMediaMetadata::rotation_); 683 } 684 685 // static 686 scoped_ptr<ImageMediaMetadata> ImageMediaMetadata::CreateFrom( 687 const base::Value& value) { 688 scoped_ptr<ImageMediaMetadata> resource(new ImageMediaMetadata()); 689 if (!resource->Parse(value)) { 690 LOG(ERROR) << "Unable to create: Invalid ImageMediaMetadata JSON!"; 691 return scoped_ptr<ImageMediaMetadata>(); 692 } 693 return resource.Pass(); 694 } 695 696 bool ImageMediaMetadata::Parse(const base::Value& value) { 697 base::JSONValueConverter<ImageMediaMetadata> converter; 698 if (!converter.Convert(value, this)) { 699 LOG(ERROR) << "Unable to parse: Invalid ImageMediaMetadata."; 700 return false; 701 } 702 return true; 703 } 704 705 } // namespace google_apis 706