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 <algorithm>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/files/file_path.h"
     11 #include "base/json/json_value_converter.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_piece.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/values.h"
     17 #include "google_apis/drive/time_util.h"
     18 
     19 using base::Value;
     20 using base::DictionaryValue;
     21 using base::ListValue;
     22 
     23 namespace google_apis {
     24 
     25 namespace {
     26 
     27 bool CreateFileResourceFromValue(const base::Value* value,
     28                                  scoped_ptr<FileResource>* file) {
     29   *file = FileResource::CreateFrom(*value);
     30   return !!*file;
     31 }
     32 
     33 // Converts |url_string| to |result|.  Always returns true to be used
     34 // for JSONValueConverter::RegisterCustomField method.
     35 // TODO(mukai): make it return false in case of invalid |url_string|.
     36 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
     37   *result = GURL(url_string.as_string());
     38   return true;
     39 }
     40 
     41 // Converts |value| to |result|. The key of |value| is app_id, and its value
     42 // is URL to open the resource on the web app.
     43 bool GetOpenWithLinksFromDictionaryValue(
     44     const base::Value* value,
     45     std::vector<FileResource::OpenWithLink>* result) {
     46   DCHECK(value);
     47   DCHECK(result);
     48 
     49   const base::DictionaryValue* dictionary_value;
     50   if (!value->GetAsDictionary(&dictionary_value))
     51     return false;
     52 
     53   result->reserve(dictionary_value->size());
     54   for (DictionaryValue::Iterator iter(*dictionary_value);
     55        !iter.IsAtEnd(); iter.Advance()) {
     56     std::string string_value;
     57     if (!iter.value().GetAsString(&string_value))
     58       return false;
     59 
     60     FileResource::OpenWithLink open_with_link;
     61     open_with_link.app_id = iter.key();
     62     open_with_link.open_url = GURL(string_value);
     63     result->push_back(open_with_link);
     64   }
     65 
     66   return true;
     67 }
     68 
     69 // Drive v2 API JSON names.
     70 
     71 // Definition order follows the order of documentation in
     72 // https://developers.google.com/drive/v2/reference/
     73 
     74 // Common
     75 const char kKind[] = "kind";
     76 const char kId[] = "id";
     77 const char kETag[] = "etag";
     78 const char kSelfLink[] = "selfLink";
     79 const char kItems[] = "items";
     80 const char kLargestChangeId[] = "largestChangeId";
     81 
     82 // About Resource
     83 // https://developers.google.com/drive/v2/reference/about
     84 const char kAboutKind[] = "drive#about";
     85 const char kQuotaBytesTotal[] = "quotaBytesTotal";
     86 const char kQuotaBytesUsed[] = "quotaBytesUsed";
     87 const char kRootFolderId[] = "rootFolderId";
     88 
     89 // App Icon
     90 // https://developers.google.com/drive/v2/reference/apps
     91 const char kCategory[] = "category";
     92 const char kSize[] = "size";
     93 const char kIconUrl[] = "iconUrl";
     94 
     95 // Apps Resource
     96 // https://developers.google.com/drive/v2/reference/apps
     97 const char kAppKind[] = "drive#app";
     98 const char kName[] = "name";
     99 const char kObjectType[] = "objectType";
    100 const char kSupportsCreate[] = "supportsCreate";
    101 const char kSupportsImport[] = "supportsImport";
    102 const char kInstalled[] = "installed";
    103 const char kAuthorized[] = "authorized";
    104 const char kProductUrl[] = "productUrl";
    105 const char kPrimaryMimeTypes[] = "primaryMimeTypes";
    106 const char kSecondaryMimeTypes[] = "secondaryMimeTypes";
    107 const char kPrimaryFileExtensions[] = "primaryFileExtensions";
    108 const char kSecondaryFileExtensions[] = "secondaryFileExtensions";
    109 const char kIcons[] = "icons";
    110 
    111 // Apps List
    112 // https://developers.google.com/drive/v2/reference/apps/list
    113 const char kAppListKind[] = "drive#appList";
    114 
    115 // Parent Resource
    116 // https://developers.google.com/drive/v2/reference/parents
    117 const char kParentReferenceKind[] = "drive#parentReference";
    118 const char kParentLink[] = "parentLink";
    119 const char kIsRoot[] = "isRoot";
    120 
    121 // File Resource
    122 // https://developers.google.com/drive/v2/reference/files
    123 const char kFileKind[] = "drive#file";
    124 const char kTitle[] = "title";
    125 const char kMimeType[] = "mimeType";
    126 const char kCreatedDate[] = "createdDate";
    127 const char kModifiedDate[] = "modifiedDate";
    128 const char kModifiedByMeDate[] = "modifiedByMeDate";
    129 const char kLastViewedByMeDate[] = "lastViewedByMeDate";
    130 const char kSharedWithMeDate[] = "sharedWithMeDate";
    131 const char kDownloadUrl[] = "downloadUrl";
    132 const char kFileExtension[] = "fileExtension";
    133 const char kMd5Checksum[] = "md5Checksum";
    134 const char kFileSize[] = "fileSize";
    135 const char kAlternateLink[] = "alternateLink";
    136 const char kEmbedLink[] = "embedLink";
    137 const char kParents[] = "parents";
    138 const char kThumbnailLink[] = "thumbnailLink";
    139 const char kWebContentLink[] = "webContentLink";
    140 const char kOpenWithLinks[] = "openWithLinks";
    141 const char kLabels[] = "labels";
    142 const char kImageMediaMetadata[] = "imageMediaMetadata";
    143 const char kShared[] = "shared";
    144 // These 5 flags are defined under |labels|.
    145 const char kLabelStarred[] = "starred";
    146 const char kLabelHidden[] = "hidden";
    147 const char kLabelTrashed[] = "trashed";
    148 const char kLabelRestricted[] = "restricted";
    149 const char kLabelViewed[] = "viewed";
    150 // These 3 flags are defined under |imageMediaMetadata|.
    151 const char kImageMediaMetadataWidth[] = "width";
    152 const char kImageMediaMetadataHeight[] = "height";
    153 const char kImageMediaMetadataRotation[] = "rotation";
    154 
    155 const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder";
    156 
    157 // Files List
    158 // https://developers.google.com/drive/v2/reference/files/list
    159 const char kFileListKind[] = "drive#fileList";
    160 const char kNextPageToken[] = "nextPageToken";
    161 const char kNextLink[] = "nextLink";
    162 
    163 // Change Resource
    164 // https://developers.google.com/drive/v2/reference/changes
    165 const char kChangeKind[] = "drive#change";
    166 const char kFileId[] = "fileId";
    167 const char kDeleted[] = "deleted";
    168 const char kFile[] = "file";
    169 
    170 // Changes List
    171 // https://developers.google.com/drive/v2/reference/changes/list
    172 const char kChangeListKind[] = "drive#changeList";
    173 
    174 // Maps category name to enum IconCategory.
    175 struct AppIconCategoryMap {
    176   DriveAppIcon::IconCategory category;
    177   const char* category_name;
    178 };
    179 
    180 const AppIconCategoryMap kAppIconCategoryMap[] = {
    181   { DriveAppIcon::DOCUMENT, "document" },
    182   { DriveAppIcon::APPLICATION, "application" },
    183   { DriveAppIcon::SHARED_DOCUMENT, "documentShared" },
    184 };
    185 
    186 // Checks if the JSON is expected kind.  In Drive API, JSON data structure has
    187 // |kind| property which denotes the type of the structure (e.g. "drive#file").
    188 bool IsResourceKindExpected(const base::Value& value,
    189                             const std::string& expected_kind) {
    190   const base::DictionaryValue* as_dict = NULL;
    191   std::string kind;
    192   return value.GetAsDictionary(&as_dict) &&
    193       as_dict->HasKey(kKind) &&
    194       as_dict->GetString(kKind, &kind) &&
    195       kind == expected_kind;
    196 }
    197 
    198 }  // namespace
    199 
    200 ////////////////////////////////////////////////////////////////////////////////
    201 // AboutResource implementation
    202 
    203 AboutResource::AboutResource()
    204     : largest_change_id_(0),
    205       quota_bytes_total_(0),
    206       quota_bytes_used_(0) {}
    207 
    208 AboutResource::~AboutResource() {}
    209 
    210 // static
    211 scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) {
    212   scoped_ptr<AboutResource> resource(new AboutResource());
    213   if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) {
    214     LOG(ERROR) << "Unable to create: Invalid About resource JSON!";
    215     return scoped_ptr<AboutResource>();
    216   }
    217   return resource.Pass();
    218 }
    219 
    220 // static
    221 void AboutResource::RegisterJSONConverter(
    222     base::JSONValueConverter<AboutResource>* converter) {
    223   converter->RegisterCustomField<int64>(kLargestChangeId,
    224                                         &AboutResource::largest_change_id_,
    225                                         &base::StringToInt64);
    226   converter->RegisterCustomField<int64>(kQuotaBytesTotal,
    227                                         &AboutResource::quota_bytes_total_,
    228                                         &base::StringToInt64);
    229   converter->RegisterCustomField<int64>(kQuotaBytesUsed,
    230                                         &AboutResource::quota_bytes_used_,
    231                                         &base::StringToInt64);
    232   converter->RegisterStringField(kRootFolderId,
    233                                  &AboutResource::root_folder_id_);
    234 }
    235 
    236 bool AboutResource::Parse(const base::Value& value) {
    237   base::JSONValueConverter<AboutResource> converter;
    238   if (!converter.Convert(value, this)) {
    239     LOG(ERROR) << "Unable to parse: Invalid About resource JSON!";
    240     return false;
    241   }
    242   return true;
    243 }
    244 
    245 ////////////////////////////////////////////////////////////////////////////////
    246 // DriveAppIcon implementation
    247 
    248 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN), icon_side_length_(0) {}
    249 
    250 DriveAppIcon::~DriveAppIcon() {}
    251 
    252 // static
    253 void DriveAppIcon::RegisterJSONConverter(
    254     base::JSONValueConverter<DriveAppIcon>* converter) {
    255   converter->RegisterCustomField<IconCategory>(
    256       kCategory,
    257       &DriveAppIcon::category_,
    258       &DriveAppIcon::GetIconCategory);
    259   converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_);
    260   converter->RegisterCustomField<GURL>(kIconUrl,
    261                                        &DriveAppIcon::icon_url_,
    262                                        GetGURLFromString);
    263 }
    264 
    265 // static
    266 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) {
    267   scoped_ptr<DriveAppIcon> resource(new DriveAppIcon());
    268   if (!resource->Parse(value)) {
    269     LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!";
    270     return scoped_ptr<DriveAppIcon>();
    271   }
    272   return resource.Pass();
    273 }
    274 
    275 bool DriveAppIcon::Parse(const base::Value& value) {
    276   base::JSONValueConverter<DriveAppIcon> converter;
    277   if (!converter.Convert(value, this)) {
    278     LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon";
    279     return false;
    280   }
    281   return true;
    282 }
    283 
    284 // static
    285 bool DriveAppIcon::GetIconCategory(const base::StringPiece& category,
    286                                    DriveAppIcon::IconCategory* result) {
    287   for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
    288     if (category == kAppIconCategoryMap[i].category_name) {
    289       *result = kAppIconCategoryMap[i].category;
    290       return true;
    291     }
    292   }
    293   DVLOG(1) << "Unknown icon category " << category;
    294   return false;
    295 }
    296 
    297 ////////////////////////////////////////////////////////////////////////////////
    298 // AppResource implementation
    299 
    300 AppResource::AppResource()
    301     : supports_create_(false),
    302       supports_import_(false),
    303       installed_(false),
    304       authorized_(false) {
    305 }
    306 
    307 AppResource::~AppResource() {}
    308 
    309 // static
    310 void AppResource::RegisterJSONConverter(
    311     base::JSONValueConverter<AppResource>* converter) {
    312   converter->RegisterStringField(kId, &AppResource::application_id_);
    313   converter->RegisterStringField(kName, &AppResource::name_);
    314   converter->RegisterStringField(kObjectType, &AppResource::object_type_);
    315   converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_);
    316   converter->RegisterBoolField(kSupportsImport, &AppResource::supports_import_);
    317   converter->RegisterBoolField(kInstalled, &AppResource::installed_);
    318   converter->RegisterBoolField(kAuthorized, &AppResource::authorized_);
    319   converter->RegisterCustomField<GURL>(kProductUrl,
    320                                        &AppResource::product_url_,
    321                                        GetGURLFromString);
    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 }
    332 
    333 // static
    334 scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) {
    335   scoped_ptr<AppResource> resource(new AppResource());
    336   if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) {
    337     LOG(ERROR) << "Unable to create: Invalid AppResource JSON!";
    338     return scoped_ptr<AppResource>();
    339   }
    340   return resource.Pass();
    341 }
    342 
    343 bool AppResource::Parse(const base::Value& value) {
    344   base::JSONValueConverter<AppResource> converter;
    345   if (!converter.Convert(value, this)) {
    346     LOG(ERROR) << "Unable to parse: Invalid AppResource";
    347     return false;
    348   }
    349   return true;
    350 }
    351 
    352 ////////////////////////////////////////////////////////////////////////////////
    353 // AppList implementation
    354 
    355 AppList::AppList() {}
    356 
    357 AppList::~AppList() {}
    358 
    359 // static
    360 void AppList::RegisterJSONConverter(
    361     base::JSONValueConverter<AppList>* converter) {
    362   converter->RegisterStringField(kETag, &AppList::etag_);
    363   converter->RegisterRepeatedMessage<AppResource>(kItems,
    364                                                    &AppList::items_);
    365 }
    366 
    367 // static
    368 scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) {
    369   scoped_ptr<AppList> resource(new AppList());
    370   if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) {
    371     LOG(ERROR) << "Unable to create: Invalid AppList JSON!";
    372     return scoped_ptr<AppList>();
    373   }
    374   return resource.Pass();
    375 }
    376 
    377 bool AppList::Parse(const base::Value& value) {
    378   base::JSONValueConverter<AppList> converter;
    379   if (!converter.Convert(value, this)) {
    380     LOG(ERROR) << "Unable to parse: Invalid AppList";
    381     return false;
    382   }
    383   return true;
    384 }
    385 
    386 ////////////////////////////////////////////////////////////////////////////////
    387 // ParentReference implementation
    388 
    389 ParentReference::ParentReference() : is_root_(false) {}
    390 
    391 ParentReference::~ParentReference() {}
    392 
    393 // static
    394 void ParentReference::RegisterJSONConverter(
    395     base::JSONValueConverter<ParentReference>* converter) {
    396   converter->RegisterStringField(kId, &ParentReference::file_id_);
    397   converter->RegisterCustomField<GURL>(kParentLink,
    398                                        &ParentReference::parent_link_,
    399                                        GetGURLFromString);
    400   converter->RegisterBoolField(kIsRoot, &ParentReference::is_root_);
    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_(0) {}
    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->RegisterCustomField<GURL>(kSelfLink,
    437                                        &FileResource::self_link_,
    438                                        GetGURLFromString);
    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>(
    445       kCreatedDate,
    446       &FileResource::created_date_,
    447       &util::GetTimeFromString);
    448   converter->RegisterCustomField<base::Time>(
    449       kModifiedDate,
    450       &FileResource::modified_date_,
    451       &util::GetTimeFromString);
    452   converter->RegisterCustomField<base::Time>(
    453       kModifiedByMeDate,
    454       &FileResource::modified_by_me_date_,
    455       &util::GetTimeFromString);
    456   converter->RegisterCustomField<base::Time>(
    457       kLastViewedByMeDate,
    458       &FileResource::last_viewed_by_me_date_,
    459       &util::GetTimeFromString);
    460   converter->RegisterCustomField<base::Time>(
    461       kSharedWithMeDate,
    462       &FileResource::shared_with_me_date_,
    463       &util::GetTimeFromString);
    464   converter->RegisterBoolField(kShared, &FileResource::shared_);
    465   converter->RegisterCustomField<GURL>(kDownloadUrl,
    466                                        &FileResource::download_url_,
    467                                        GetGURLFromString);
    468   converter->RegisterStringField(kFileExtension,
    469                                  &FileResource::file_extension_);
    470   converter->RegisterStringField(kMd5Checksum, &FileResource::md5_checksum_);
    471   converter->RegisterCustomField<int64>(kFileSize,
    472                                         &FileResource::file_size_,
    473                                         &base::StringToInt64);
    474   converter->RegisterCustomField<GURL>(kAlternateLink,
    475                                        &FileResource::alternate_link_,
    476                                        GetGURLFromString);
    477   converter->RegisterCustomField<GURL>(kEmbedLink,
    478                                        &FileResource::embed_link_,
    479                                        GetGURLFromString);
    480   converter->RegisterRepeatedMessage<ParentReference>(kParents,
    481                                                       &FileResource::parents_);
    482   converter->RegisterCustomField<GURL>(kThumbnailLink,
    483                                        &FileResource::thumbnail_link_,
    484                                        GetGURLFromString);
    485   converter->RegisterCustomField<GURL>(kWebContentLink,
    486                                        &FileResource::web_content_link_,
    487                                        GetGURLFromString);
    488   converter->RegisterCustomValueField<std::vector<OpenWithLink> >(
    489       kOpenWithLinks,
    490       &FileResource::open_with_links_,
    491       GetOpenWithLinksFromDictionaryValue);
    492 }
    493 
    494 // static
    495 scoped_ptr<FileResource> FileResource::CreateFrom(const base::Value& value) {
    496   scoped_ptr<FileResource> resource(new FileResource());
    497   if (!IsResourceKindExpected(value, kFileKind) || !resource->Parse(value)) {
    498     LOG(ERROR) << "Unable to create: Invalid FileResource JSON!";
    499     return scoped_ptr<FileResource>();
    500   }
    501   return resource.Pass();
    502 }
    503 
    504 bool FileResource::IsDirectory() const {
    505   return mime_type_ == kDriveFolderMimeType;
    506 }
    507 
    508 bool FileResource::Parse(const base::Value& value) {
    509   base::JSONValueConverter<FileResource> converter;
    510   if (!converter.Convert(value, this)) {
    511     LOG(ERROR) << "Unable to parse: Invalid FileResource";
    512     return false;
    513   }
    514   return true;
    515 }
    516 
    517 ////////////////////////////////////////////////////////////////////////////////
    518 // FileList implementation
    519 
    520 FileList::FileList() {}
    521 
    522 FileList::~FileList() {}
    523 
    524 // static
    525 void FileList::RegisterJSONConverter(
    526     base::JSONValueConverter<FileList>* converter) {
    527   converter->RegisterStringField(kETag, &FileList::etag_);
    528   converter->RegisterStringField(kNextPageToken, &FileList::next_page_token_);
    529   converter->RegisterCustomField<GURL>(kNextLink,
    530                                        &FileList::next_link_,
    531                                        GetGURLFromString);
    532   converter->RegisterRepeatedMessage<FileResource>(kItems,
    533                                                    &FileList::items_);
    534 }
    535 
    536 // static
    537 bool FileList::HasFileListKind(const base::Value& value) {
    538   return IsResourceKindExpected(value, kFileListKind);
    539 }
    540 
    541 // static
    542 scoped_ptr<FileList> FileList::CreateFrom(const base::Value& value) {
    543   scoped_ptr<FileList> resource(new FileList());
    544   if (!HasFileListKind(value) || !resource->Parse(value)) {
    545     LOG(ERROR) << "Unable to create: Invalid FileList JSON!";
    546     return scoped_ptr<FileList>();
    547   }
    548   return resource.Pass();
    549 }
    550 
    551 bool FileList::Parse(const base::Value& value) {
    552   base::JSONValueConverter<FileList> converter;
    553   if (!converter.Convert(value, this)) {
    554     LOG(ERROR) << "Unable to parse: Invalid FileList";
    555     return false;
    556   }
    557   return true;
    558 }
    559 
    560 ////////////////////////////////////////////////////////////////////////////////
    561 // ChangeResource implementation
    562 
    563 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
    564 
    565 ChangeResource::~ChangeResource() {}
    566 
    567 // static
    568 void ChangeResource::RegisterJSONConverter(
    569     base::JSONValueConverter<ChangeResource>* converter) {
    570   converter->RegisterCustomField<int64>(kId,
    571                                         &ChangeResource::change_id_,
    572                                         &base::StringToInt64);
    573   converter->RegisterStringField(kFileId, &ChangeResource::file_id_);
    574   converter->RegisterBoolField(kDeleted, &ChangeResource::deleted_);
    575   converter->RegisterCustomValueField(kFile, &ChangeResource::file_,
    576                                       &CreateFileResourceFromValue);
    577 }
    578 
    579 // static
    580 scoped_ptr<ChangeResource>
    581 ChangeResource::CreateFrom(const base::Value& value) {
    582   scoped_ptr<ChangeResource> resource(new ChangeResource());
    583   if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) {
    584     LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!";
    585     return scoped_ptr<ChangeResource>();
    586   }
    587   return resource.Pass();
    588 }
    589 
    590 bool ChangeResource::Parse(const base::Value& value) {
    591   base::JSONValueConverter<ChangeResource> converter;
    592   if (!converter.Convert(value, this)) {
    593     LOG(ERROR) << "Unable to parse: Invalid ChangeResource";
    594     return false;
    595   }
    596   return true;
    597 }
    598 
    599 ////////////////////////////////////////////////////////////////////////////////
    600 // ChangeList implementation
    601 
    602 ChangeList::ChangeList() : largest_change_id_(0) {}
    603 
    604 ChangeList::~ChangeList() {}
    605 
    606 // static
    607 void ChangeList::RegisterJSONConverter(
    608     base::JSONValueConverter<ChangeList>* converter) {
    609   converter->RegisterStringField(kETag, &ChangeList::etag_);
    610   converter->RegisterStringField(kNextPageToken, &ChangeList::next_page_token_);
    611   converter->RegisterCustomField<GURL>(kNextLink,
    612                                        &ChangeList::next_link_,
    613                                        GetGURLFromString);
    614   converter->RegisterCustomField<int64>(kLargestChangeId,
    615                                         &ChangeList::largest_change_id_,
    616                                         &base::StringToInt64);
    617   converter->RegisterRepeatedMessage<ChangeResource>(kItems,
    618                                                      &ChangeList::items_);
    619 }
    620 
    621 // static
    622 bool ChangeList::HasChangeListKind(const base::Value& value) {
    623   return IsResourceKindExpected(value, kChangeListKind);
    624 }
    625 
    626 // static
    627 scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) {
    628   scoped_ptr<ChangeList> resource(new ChangeList());
    629   if (!HasChangeListKind(value) || !resource->Parse(value)) {
    630     LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!";
    631     return scoped_ptr<ChangeList>();
    632   }
    633   return resource.Pass();
    634 }
    635 
    636 bool ChangeList::Parse(const base::Value& value) {
    637   base::JSONValueConverter<ChangeList> converter;
    638   if (!converter.Convert(value, this)) {
    639     LOG(ERROR) << "Unable to parse: Invalid ChangeList";
    640     return false;
    641   }
    642   return true;
    643 }
    644 
    645 
    646 ////////////////////////////////////////////////////////////////////////////////
    647 // FileLabels implementation
    648 
    649 FileLabels::FileLabels()
    650     : starred_(false),
    651       hidden_(false),
    652       trashed_(false),
    653       restricted_(false),
    654       viewed_(false) {}
    655 
    656 FileLabels::~FileLabels() {}
    657 
    658 // static
    659 void FileLabels::RegisterJSONConverter(
    660     base::JSONValueConverter<FileLabels>* converter) {
    661   converter->RegisterBoolField(kLabelStarred, &FileLabels::starred_);
    662   converter->RegisterBoolField(kLabelHidden, &FileLabels::hidden_);
    663   converter->RegisterBoolField(kLabelTrashed, &FileLabels::trashed_);
    664   converter->RegisterBoolField(kLabelRestricted, &FileLabels::restricted_);
    665   converter->RegisterBoolField(kLabelViewed, &FileLabels::viewed_);
    666 }
    667 
    668 // static
    669 scoped_ptr<FileLabels> FileLabels::CreateFrom(const base::Value& value) {
    670   scoped_ptr<FileLabels> resource(new FileLabels());
    671   if (!resource->Parse(value)) {
    672     LOG(ERROR) << "Unable to create: Invalid FileLabels JSON!";
    673     return scoped_ptr<FileLabels>();
    674   }
    675   return resource.Pass();
    676 }
    677 
    678 bool FileLabels::Parse(const base::Value& value) {
    679   base::JSONValueConverter<FileLabels> converter;
    680   if (!converter.Convert(value, this)) {
    681     LOG(ERROR) << "Unable to parse: Invalid FileLabels.";
    682     return false;
    683   }
    684   return true;
    685 }
    686 
    687 ////////////////////////////////////////////////////////////////////////////////
    688 // ImageMediaMetadata implementation
    689 
    690 ImageMediaMetadata::ImageMediaMetadata()
    691     : width_(-1),
    692       height_(-1),
    693       rotation_(-1) {}
    694 
    695 ImageMediaMetadata::~ImageMediaMetadata() {}
    696 
    697 // static
    698 void ImageMediaMetadata::RegisterJSONConverter(
    699     base::JSONValueConverter<ImageMediaMetadata>* converter) {
    700   converter->RegisterIntField(kImageMediaMetadataWidth,
    701                               &ImageMediaMetadata::width_);
    702   converter->RegisterIntField(kImageMediaMetadataHeight,
    703                               &ImageMediaMetadata::height_);
    704   converter->RegisterIntField(kImageMediaMetadataRotation,
    705                               &ImageMediaMetadata::rotation_);
    706 }
    707 
    708 // static
    709 scoped_ptr<ImageMediaMetadata> ImageMediaMetadata::CreateFrom(
    710     const base::Value& value) {
    711   scoped_ptr<ImageMediaMetadata> resource(new ImageMediaMetadata());
    712   if (!resource->Parse(value)) {
    713     LOG(ERROR) << "Unable to create: Invalid ImageMediaMetadata JSON!";
    714     return scoped_ptr<ImageMediaMetadata>();
    715   }
    716   return resource.Pass();
    717 }
    718 
    719 bool ImageMediaMetadata::Parse(const base::Value& value) {
    720   return true;
    721   base::JSONValueConverter<ImageMediaMetadata> converter;
    722   if (!converter.Convert(value, this)) {
    723     LOG(ERROR) << "Unable to parse: Invalid ImageMediaMetadata.";
    724     return false;
    725   }
    726   return true;
    727 }
    728 
    729 }  // namespace google_apis
    730