Home | History | Annotate | Download | only in drive
      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