Home | History | Annotate | Download | only in google_apis
      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 "chrome/browser/google_apis/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 "chrome/browser/google_apis/gdata_wapi_parser.h"
     18 #include "chrome/browser/google_apis/time_util.h"
     19 
     20 using base::Value;
     21 using base::DictionaryValue;
     22 using base::ListValue;
     23 
     24 namespace google_apis {
     25 
     26 namespace {
     27 
     28 bool CreateFileResourceFromValue(const base::Value* value,
     29                                  scoped_ptr<FileResource>* file) {
     30   *file = FileResource::CreateFrom(*value);
     31   return !!*file;
     32 }
     33 
     34 // Converts |url_string| to |result|.  Always returns true to be used
     35 // for JSONValueConverter::RegisterCustomField method.
     36 // TODO(mukai): make it return false in case of invalid |url_string|.
     37 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
     38   *result = GURL(url_string.as_string());
     39   return true;
     40 }
     41 
     42 // Converts |value| to |result|. The key of |value| is app_id, and its value
     43 // is URL to open the resource on the web app.
     44 bool GetOpenWithLinksFromDictionaryValue(
     45     const base::Value* value,
     46     std::vector<FileResource::OpenWithLink>* result) {
     47   DCHECK(value);
     48   DCHECK(result);
     49 
     50   const base::DictionaryValue* dictionary_value;
     51   if (!value->GetAsDictionary(&dictionary_value))
     52     return false;
     53 
     54   result->reserve(dictionary_value->size());
     55   for (DictionaryValue::Iterator iter(*dictionary_value);
     56        !iter.IsAtEnd(); iter.Advance()) {
     57     std::string string_value;
     58     if (!iter.value().GetAsString(&string_value))
     59       return false;
     60 
     61     FileResource::OpenWithLink open_with_link;
     62     open_with_link.app_id = iter.key();
     63     open_with_link.open_url = GURL(string_value);
     64     result->push_back(open_with_link);
     65   }
     66 
     67   return true;
     68 }
     69 
     70 // Drive v2 API JSON names.
     71 
     72 // Definition order follows the order of documentation in
     73 // https://developers.google.com/drive/v2/reference/
     74 
     75 // Common
     76 const char kKind[] = "kind";
     77 const char kId[] = "id";
     78 const char kETag[] = "etag";
     79 const char kSelfLink[] = "selfLink";
     80 const char kItems[] = "items";
     81 const char kLargestChangeId[] = "largestChangeId";
     82 
     83 // About Resource
     84 // https://developers.google.com/drive/v2/reference/about
     85 const char kAboutKind[] = "drive#about";
     86 const char kQuotaBytesTotal[] = "quotaBytesTotal";
     87 const char kQuotaBytesUsed[] = "quotaBytesUsed";
     88 const char kRootFolderId[] = "rootFolderId";
     89 
     90 // App Icon
     91 // https://developers.google.com/drive/v2/reference/apps
     92 const char kCategory[] = "category";
     93 const char kSize[] = "size";
     94 const char kIconUrl[] = "iconUrl";
     95 
     96 // Apps Resource
     97 // https://developers.google.com/drive/v2/reference/apps
     98 const char kAppKind[] = "drive#app";
     99 const char kName[] = "name";
    100 const char kObjectType[] = "objectType";
    101 const char kSupportsCreate[] = "supportsCreate";
    102 const char kSupportsImport[] = "supportsImport";
    103 const char kInstalled[] = "installed";
    104 const char kAuthorized[] = "authorized";
    105 const char kProductUrl[] = "productUrl";
    106 const char kPrimaryMimeTypes[] = "primaryMimeTypes";
    107 const char kSecondaryMimeTypes[] = "secondaryMimeTypes";
    108 const char kPrimaryFileExtensions[] = "primaryFileExtensions";
    109 const char kSecondaryFileExtensions[] = "secondaryFileExtensions";
    110 const char kIcons[] = "icons";
    111 
    112 // Apps List
    113 // https://developers.google.com/drive/v2/reference/apps/list
    114 const char kAppListKind[] = "drive#appList";
    115 
    116 // Parent Resource
    117 // https://developers.google.com/drive/v2/reference/parents
    118 const char kParentReferenceKind[] = "drive#parentReference";
    119 const char kParentLink[] = "parentLink";
    120 const char kIsRoot[] = "isRoot";
    121 
    122 // File Resource
    123 // https://developers.google.com/drive/v2/reference/files
    124 const char kFileKind[] = "drive#file";
    125 const char kTitle[] = "title";
    126 const char kMimeType[] = "mimeType";
    127 const char kCreatedDate[] = "createdDate";
    128 const char kModifiedDate[] = "modifiedDate";
    129 const char kModifiedByMeDate[] = "modifiedByMeDate";
    130 const char kLastViewedByMeDate[] = "lastViewedByMeDate";
    131 const char kSharedWithMeDate[] = "sharedWithMeDate";
    132 const char kDownloadUrl[] = "downloadUrl";
    133 const char kFileExtension[] = "fileExtension";
    134 const char kMd5Checksum[] = "md5Checksum";
    135 const char kFileSize[] = "fileSize";
    136 const char kAlternateLink[] = "alternateLink";
    137 const char kEmbedLink[] = "embedLink";
    138 const char kParents[] = "parents";
    139 const char kThumbnailLink[] = "thumbnailLink";
    140 const char kWebContentLink[] = "webContentLink";
    141 const char kOpenWithLinks[] = "openWithLinks";
    142 const char kLabels[] = "labels";
    143 // These 5 flags are defined under |labels|.
    144 const char kLabelStarred[] = "starred";
    145 const char kLabelHidden[] = "hidden";
    146 const char kLabelTrashed[] = "trashed";
    147 const char kLabelRestricted[] = "restricted";
    148 const char kLabelViewed[] = "viewed";
    149 
    150 const char kDriveFolderMimeType[] = "application/vnd.google-apps.folder";
    151 
    152 // Files List
    153 // https://developers.google.com/drive/v2/reference/files/list
    154 const char kFileListKind[] = "drive#fileList";
    155 const char kNextPageToken[] = "nextPageToken";
    156 const char kNextLink[] = "nextLink";
    157 
    158 // Change Resource
    159 // https://developers.google.com/drive/v2/reference/changes
    160 const char kChangeKind[] = "drive#change";
    161 const char kFileId[] = "fileId";
    162 const char kDeleted[] = "deleted";
    163 const char kFile[] = "file";
    164 
    165 // Changes List
    166 // https://developers.google.com/drive/v2/reference/changes/list
    167 const char kChangeListKind[] = "drive#changeList";
    168 
    169 // Google Apps MIME types:
    170 const char kGoogleDocumentMimeType[] = "application/vnd.google-apps.document";
    171 const char kGoogleDrawingMimeType[] = "application/vnd.google-apps.drawing";
    172 const char kGoogleFormMimeType[] = "application/vnd.google-apps.form";
    173 const char kGooglePresentationMimeType[] =
    174     "application/vnd.google-apps.presentation";
    175 const char kGoogleScriptMimeType[] = "application/vnd.google-apps.script";
    176 const char kGoogleSiteMimeType[] = "application/vnd.google-apps.site";
    177 const char kGoogleSpreadsheetMimeType[] =
    178     "application/vnd.google-apps.spreadsheet";
    179 const char kGoogleTableMimeType[] = "application/vnd.google-apps.table";
    180 
    181 // Maps category name to enum IconCategory.
    182 struct AppIconCategoryMap {
    183   DriveAppIcon::IconCategory category;
    184   const char* category_name;
    185 };
    186 
    187 const AppIconCategoryMap kAppIconCategoryMap[] = {
    188   { DriveAppIcon::DOCUMENT, "document" },
    189   { DriveAppIcon::APPLICATION, "application" },
    190   { DriveAppIcon::SHARED_DOCUMENT, "documentShared" },
    191 };
    192 
    193 // Checks if the JSON is expected kind.  In Drive API, JSON data structure has
    194 // |kind| property which denotes the type of the structure (e.g. "drive#file").
    195 bool IsResourceKindExpected(const base::Value& value,
    196                             const std::string& expected_kind) {
    197   const base::DictionaryValue* as_dict = NULL;
    198   std::string kind;
    199   return value.GetAsDictionary(&as_dict) &&
    200       as_dict->HasKey(kKind) &&
    201       as_dict->GetString(kKind, &kind) &&
    202       kind == expected_kind;
    203 }
    204 
    205 ScopedVector<std::string> CopyScopedVectorString(
    206     const ScopedVector<std::string>& source) {
    207   ScopedVector<std::string> result;
    208   result.reserve(source.size());
    209   for (size_t i = 0; i < source.size(); ++i) {
    210     result.push_back(new std::string(*source[i]));
    211   }
    212   return result.Pass();
    213 }
    214 
    215 }  // namespace
    216 
    217 ////////////////////////////////////////////////////////////////////////////////
    218 // AboutResource implementation
    219 
    220 AboutResource::AboutResource()
    221     : largest_change_id_(0),
    222       quota_bytes_total_(0),
    223       quota_bytes_used_(0) {}
    224 
    225 AboutResource::~AboutResource() {}
    226 
    227 // static
    228 scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) {
    229   scoped_ptr<AboutResource> resource(new AboutResource());
    230   if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) {
    231     LOG(ERROR) << "Unable to create: Invalid About resource JSON!";
    232     return scoped_ptr<AboutResource>();
    233   }
    234   return resource.Pass();
    235 }
    236 
    237 // static
    238 scoped_ptr<AboutResource> AboutResource::CreateFromAccountMetadata(
    239     const AccountMetadata& account_metadata,
    240     const std::string& root_resource_id) {
    241   scoped_ptr<AboutResource> resource(new AboutResource);
    242   resource->set_largest_change_id(account_metadata.largest_changestamp());
    243   resource->set_quota_bytes_total(account_metadata.quota_bytes_total());
    244   resource->set_quota_bytes_used(account_metadata.quota_bytes_used());
    245   resource->set_root_folder_id(root_resource_id);
    246   return resource.Pass();
    247 }
    248 
    249 // static
    250 void AboutResource::RegisterJSONConverter(
    251     base::JSONValueConverter<AboutResource>* converter) {
    252   converter->RegisterCustomField<int64>(kLargestChangeId,
    253                                         &AboutResource::largest_change_id_,
    254                                         &base::StringToInt64);
    255   converter->RegisterCustomField<int64>(kQuotaBytesTotal,
    256                                         &AboutResource::quota_bytes_total_,
    257                                         &base::StringToInt64);
    258   converter->RegisterCustomField<int64>(kQuotaBytesUsed,
    259                                         &AboutResource::quota_bytes_used_,
    260                                         &base::StringToInt64);
    261   converter->RegisterStringField(kRootFolderId,
    262                                  &AboutResource::root_folder_id_);
    263 }
    264 
    265 bool AboutResource::Parse(const base::Value& value) {
    266   base::JSONValueConverter<AboutResource> converter;
    267   if (!converter.Convert(value, this)) {
    268     LOG(ERROR) << "Unable to parse: Invalid About resource JSON!";
    269     return false;
    270   }
    271   return true;
    272 }
    273 
    274 ////////////////////////////////////////////////////////////////////////////////
    275 // DriveAppIcon implementation
    276 
    277 DriveAppIcon::DriveAppIcon() : category_(UNKNOWN), icon_side_length_(0) {}
    278 
    279 DriveAppIcon::~DriveAppIcon() {}
    280 
    281 // static
    282 void DriveAppIcon::RegisterJSONConverter(
    283     base::JSONValueConverter<DriveAppIcon>* converter) {
    284   converter->RegisterCustomField<IconCategory>(
    285       kCategory,
    286       &DriveAppIcon::category_,
    287       &DriveAppIcon::GetIconCategory);
    288   converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_);
    289   converter->RegisterCustomField<GURL>(kIconUrl,
    290                                        &DriveAppIcon::icon_url_,
    291                                        GetGURLFromString);
    292 }
    293 
    294 // static
    295 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) {
    296   scoped_ptr<DriveAppIcon> resource(new DriveAppIcon());
    297   if (!resource->Parse(value)) {
    298     LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!";
    299     return scoped_ptr<DriveAppIcon>();
    300   }
    301   return resource.Pass();
    302 }
    303 
    304 // static
    305 scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFromAppIcon(
    306     const AppIcon& app_icon) {
    307   scoped_ptr<DriveAppIcon> resource(new DriveAppIcon);
    308   switch (app_icon.category()) {
    309     case AppIcon::ICON_UNKNOWN:
    310       resource->set_category(DriveAppIcon::UNKNOWN);
    311       break;
    312     case AppIcon::ICON_DOCUMENT:
    313       resource->set_category(DriveAppIcon::DOCUMENT);
    314       break;
    315     case AppIcon::ICON_APPLICATION:
    316       resource->set_category(DriveAppIcon::APPLICATION);
    317       break;
    318     case AppIcon::ICON_SHARED_DOCUMENT:
    319       resource->set_category(DriveAppIcon::SHARED_DOCUMENT);
    320       break;
    321     default:
    322       NOTREACHED();
    323   }
    324 
    325   resource->set_icon_side_length(app_icon.icon_side_length());
    326   resource->set_icon_url(app_icon.GetIconURL());
    327   return resource.Pass();
    328 }
    329 
    330 bool DriveAppIcon::Parse(const base::Value& value) {
    331   base::JSONValueConverter<DriveAppIcon> converter;
    332   if (!converter.Convert(value, this)) {
    333     LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon";
    334     return false;
    335   }
    336   return true;
    337 }
    338 
    339 // static
    340 bool DriveAppIcon::GetIconCategory(const base::StringPiece& category,
    341                                    DriveAppIcon::IconCategory* result) {
    342   for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
    343     if (category == kAppIconCategoryMap[i].category_name) {
    344       *result = kAppIconCategoryMap[i].category;
    345       return true;
    346     }
    347   }
    348   DVLOG(1) << "Unknown icon category " << category;
    349   return false;
    350 }
    351 
    352 ////////////////////////////////////////////////////////////////////////////////
    353 // AppResource implementation
    354 
    355 AppResource::AppResource()
    356     : supports_create_(false),
    357       supports_import_(false),
    358       installed_(false),
    359       authorized_(false) {
    360 }
    361 
    362 AppResource::~AppResource() {}
    363 
    364 // static
    365 void AppResource::RegisterJSONConverter(
    366     base::JSONValueConverter<AppResource>* converter) {
    367   converter->RegisterStringField(kId, &AppResource::application_id_);
    368   converter->RegisterStringField(kName, &AppResource::name_);
    369   converter->RegisterStringField(kObjectType, &AppResource::object_type_);
    370   converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_);
    371   converter->RegisterBoolField(kSupportsImport, &AppResource::supports_import_);
    372   converter->RegisterBoolField(kInstalled, &AppResource::installed_);
    373   converter->RegisterBoolField(kAuthorized, &AppResource::authorized_);
    374   converter->RegisterCustomField<GURL>(kProductUrl,
    375                                        &AppResource::product_url_,
    376                                        GetGURLFromString);
    377   converter->RegisterRepeatedString(kPrimaryMimeTypes,
    378                                     &AppResource::primary_mimetypes_);
    379   converter->RegisterRepeatedString(kSecondaryMimeTypes,
    380                                     &AppResource::secondary_mimetypes_);
    381   converter->RegisterRepeatedString(kPrimaryFileExtensions,
    382                                     &AppResource::primary_file_extensions_);
    383   converter->RegisterRepeatedString(kSecondaryFileExtensions,
    384                                     &AppResource::secondary_file_extensions_);
    385   converter->RegisterRepeatedMessage(kIcons, &AppResource::icons_);
    386 }
    387 
    388 // static
    389 scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) {
    390   scoped_ptr<AppResource> resource(new AppResource());
    391   if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) {
    392     LOG(ERROR) << "Unable to create: Invalid AppResource JSON!";
    393     return scoped_ptr<AppResource>();
    394   }
    395   return resource.Pass();
    396 }
    397 
    398 // static
    399 scoped_ptr<AppResource> AppResource::CreateFromInstalledApp(
    400     const InstalledApp& installed_app) {
    401   scoped_ptr<AppResource> resource(new AppResource);
    402   resource->set_application_id(installed_app.app_id());
    403   resource->set_name(installed_app.app_name());
    404   resource->set_object_type(installed_app.object_type());
    405   resource->set_supports_create(installed_app.supports_create());
    406   resource->set_product_url(installed_app.GetProductUrl());
    407 
    408   {
    409     ScopedVector<std::string> primary_mimetypes(
    410         CopyScopedVectorString(installed_app.primary_mimetypes()));
    411     resource->set_primary_mimetypes(&primary_mimetypes);
    412   }
    413   {
    414     ScopedVector<std::string> secondary_mimetypes(
    415         CopyScopedVectorString(installed_app.secondary_mimetypes()));
    416     resource->set_secondary_mimetypes(&secondary_mimetypes);
    417   }
    418   {
    419     ScopedVector<std::string> primary_file_extensions(
    420         CopyScopedVectorString(installed_app.primary_extensions()));
    421     resource->set_primary_file_extensions(&primary_file_extensions);
    422   }
    423   {
    424     ScopedVector<std::string> secondary_file_extensions(
    425         CopyScopedVectorString(installed_app.secondary_extensions()));
    426     resource->set_secondary_file_extensions(&secondary_file_extensions);
    427   }
    428 
    429   {
    430     const ScopedVector<AppIcon>& app_icons = installed_app.app_icons();
    431     ScopedVector<DriveAppIcon> icons;
    432     icons.reserve(app_icons.size());
    433     for (size_t i = 0; i < app_icons.size(); ++i) {
    434       icons.push_back(DriveAppIcon::CreateFromAppIcon(*app_icons[i]).release());
    435     }
    436     resource->set_icons(&icons);
    437   }
    438 
    439   // supports_import, installed and authorized are not supported in
    440   // InstalledApp.
    441 
    442   return resource.Pass();
    443 }
    444 
    445 
    446 bool AppResource::Parse(const base::Value& value) {
    447   base::JSONValueConverter<AppResource> converter;
    448   if (!converter.Convert(value, this)) {
    449     LOG(ERROR) << "Unable to parse: Invalid AppResource";
    450     return false;
    451   }
    452   return true;
    453 }
    454 
    455 ////////////////////////////////////////////////////////////////////////////////
    456 // AppList implementation
    457 
    458 AppList::AppList() {}
    459 
    460 AppList::~AppList() {}
    461 
    462 // static
    463 void AppList::RegisterJSONConverter(
    464     base::JSONValueConverter<AppList>* converter) {
    465   converter->RegisterStringField(kETag, &AppList::etag_);
    466   converter->RegisterRepeatedMessage<AppResource>(kItems,
    467                                                    &AppList::items_);
    468 }
    469 
    470 // static
    471 scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) {
    472   scoped_ptr<AppList> resource(new AppList());
    473   if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) {
    474     LOG(ERROR) << "Unable to create: Invalid AppList JSON!";
    475     return scoped_ptr<AppList>();
    476   }
    477   return resource.Pass();
    478 }
    479 
    480 // static
    481 scoped_ptr<AppList> AppList::CreateFromAccountMetadata(
    482     const AccountMetadata& account_metadata) {
    483   scoped_ptr<AppList> resource(new AppList);
    484 
    485   const ScopedVector<InstalledApp>& installed_apps =
    486       account_metadata.installed_apps();
    487   ScopedVector<AppResource> app_resources;
    488   app_resources.reserve(installed_apps.size());
    489   for (size_t i = 0; i < installed_apps.size(); ++i) {
    490     app_resources.push_back(
    491         AppResource::CreateFromInstalledApp(*installed_apps[i]).release());
    492   }
    493   resource->set_items(&app_resources);
    494 
    495   // etag is not supported in AccountMetadata.
    496 
    497   return resource.Pass();
    498 }
    499 
    500 bool AppList::Parse(const base::Value& value) {
    501   base::JSONValueConverter<AppList> converter;
    502   if (!converter.Convert(value, this)) {
    503     LOG(ERROR) << "Unable to parse: Invalid AppList";
    504     return false;
    505   }
    506   return true;
    507 }
    508 
    509 ////////////////////////////////////////////////////////////////////////////////
    510 // ParentReference implementation
    511 
    512 ParentReference::ParentReference() : is_root_(false) {}
    513 
    514 ParentReference::~ParentReference() {}
    515 
    516 // static
    517 void ParentReference::RegisterJSONConverter(
    518     base::JSONValueConverter<ParentReference>* converter) {
    519   converter->RegisterStringField(kId, &ParentReference::file_id_);
    520   converter->RegisterCustomField<GURL>(kParentLink,
    521                                        &ParentReference::parent_link_,
    522                                        GetGURLFromString);
    523   converter->RegisterBoolField(kIsRoot, &ParentReference::is_root_);
    524 }
    525 
    526 // static
    527 scoped_ptr<ParentReference>
    528 ParentReference::CreateFrom(const base::Value& value) {
    529   scoped_ptr<ParentReference> reference(new ParentReference());
    530   if (!IsResourceKindExpected(value, kParentReferenceKind) ||
    531       !reference->Parse(value)) {
    532     LOG(ERROR) << "Unable to create: Invalid ParentRefernce JSON!";
    533     return scoped_ptr<ParentReference>();
    534   }
    535   return reference.Pass();
    536 }
    537 
    538 bool ParentReference::Parse(const base::Value& value) {
    539   base::JSONValueConverter<ParentReference> converter;
    540   if (!converter.Convert(value, this)) {
    541     LOG(ERROR) << "Unable to parse: Invalid ParentReference";
    542     return false;
    543   }
    544   return true;
    545 }
    546 
    547 ////////////////////////////////////////////////////////////////////////////////
    548 // FileResource implementation
    549 
    550 FileResource::FileResource() : file_size_(0) {}
    551 
    552 FileResource::~FileResource() {}
    553 
    554 // static
    555 void FileResource::RegisterJSONConverter(
    556     base::JSONValueConverter<FileResource>* converter) {
    557   converter->RegisterStringField(kId, &FileResource::file_id_);
    558   converter->RegisterStringField(kETag, &FileResource::etag_);
    559   converter->RegisterCustomField<GURL>(kSelfLink,
    560                                        &FileResource::self_link_,
    561                                        GetGURLFromString);
    562   converter->RegisterStringField(kTitle, &FileResource::title_);
    563   converter->RegisterStringField(kMimeType, &FileResource::mime_type_);
    564   converter->RegisterNestedField(kLabels, &FileResource::labels_);
    565   converter->RegisterCustomField<base::Time>(
    566       kCreatedDate,
    567       &FileResource::created_date_,
    568       &util::GetTimeFromString);
    569   converter->RegisterCustomField<base::Time>(
    570       kModifiedDate,
    571       &FileResource::modified_date_,
    572       &util::GetTimeFromString);
    573   converter->RegisterCustomField<base::Time>(
    574       kModifiedByMeDate,
    575       &FileResource::modified_by_me_date_,
    576       &util::GetTimeFromString);
    577   converter->RegisterCustomField<base::Time>(
    578       kLastViewedByMeDate,
    579       &FileResource::last_viewed_by_me_date_,
    580       &util::GetTimeFromString);
    581   converter->RegisterCustomField<base::Time>(
    582       kSharedWithMeDate,
    583       &FileResource::shared_with_me_date_,
    584       &util::GetTimeFromString);
    585   converter->RegisterCustomField<GURL>(kDownloadUrl,
    586                                        &FileResource::download_url_,
    587                                        GetGURLFromString);
    588   converter->RegisterStringField(kFileExtension,
    589                                  &FileResource::file_extension_);
    590   converter->RegisterStringField(kMd5Checksum, &FileResource::md5_checksum_);
    591   converter->RegisterCustomField<int64>(kFileSize,
    592                                         &FileResource::file_size_,
    593                                         &base::StringToInt64);
    594   converter->RegisterCustomField<GURL>(kAlternateLink,
    595                                        &FileResource::alternate_link_,
    596                                        GetGURLFromString);
    597   converter->RegisterCustomField<GURL>(kEmbedLink,
    598                                        &FileResource::embed_link_,
    599                                        GetGURLFromString);
    600   converter->RegisterRepeatedMessage<ParentReference>(kParents,
    601                                                       &FileResource::parents_);
    602   converter->RegisterCustomField<GURL>(kThumbnailLink,
    603                                        &FileResource::thumbnail_link_,
    604                                        GetGURLFromString);
    605   converter->RegisterCustomField<GURL>(kWebContentLink,
    606                                        &FileResource::web_content_link_,
    607                                        GetGURLFromString);
    608   converter->RegisterCustomValueField<std::vector<OpenWithLink> >(
    609       kOpenWithLinks,
    610       &FileResource::open_with_links_,
    611       GetOpenWithLinksFromDictionaryValue);
    612 }
    613 
    614 // static
    615 scoped_ptr<FileResource> FileResource::CreateFrom(const base::Value& value) {
    616   scoped_ptr<FileResource> resource(new FileResource());
    617   if (!IsResourceKindExpected(value, kFileKind) || !resource->Parse(value)) {
    618     LOG(ERROR) << "Unable to create: Invalid FileResource JSON!";
    619     return scoped_ptr<FileResource>();
    620   }
    621   return resource.Pass();
    622 }
    623 
    624 bool FileResource::IsDirectory() const {
    625   return mime_type_ == kDriveFolderMimeType;
    626 }
    627 
    628 DriveEntryKind FileResource::GetKind() const {
    629   if (mime_type() == kGoogleDocumentMimeType)
    630     return ENTRY_KIND_DOCUMENT;
    631   if (mime_type() == kGoogleSpreadsheetMimeType)
    632     return ENTRY_KIND_SPREADSHEET;
    633   if (mime_type() == kGooglePresentationMimeType)
    634     return ENTRY_KIND_PRESENTATION;
    635   if (mime_type() == kGoogleDrawingMimeType)
    636     return ENTRY_KIND_DRAWING;
    637   if (mime_type() == kGoogleTableMimeType)
    638     return ENTRY_KIND_TABLE;
    639   if (mime_type() == kDriveFolderMimeType)
    640     return ENTRY_KIND_FOLDER;
    641   if (mime_type() == "application/pdf")
    642     return ENTRY_KIND_PDF;
    643   return ENTRY_KIND_FILE;
    644 }
    645 
    646 bool FileResource::Parse(const base::Value& value) {
    647   base::JSONValueConverter<FileResource> converter;
    648   if (!converter.Convert(value, this)) {
    649     LOG(ERROR) << "Unable to parse: Invalid FileResource";
    650     return false;
    651   }
    652   return true;
    653 }
    654 
    655 ////////////////////////////////////////////////////////////////////////////////
    656 // FileList implementation
    657 
    658 FileList::FileList() {}
    659 
    660 FileList::~FileList() {}
    661 
    662 // static
    663 void FileList::RegisterJSONConverter(
    664     base::JSONValueConverter<FileList>* converter) {
    665   converter->RegisterStringField(kETag, &FileList::etag_);
    666   converter->RegisterStringField(kNextPageToken, &FileList::next_page_token_);
    667   converter->RegisterCustomField<GURL>(kNextLink,
    668                                        &FileList::next_link_,
    669                                        GetGURLFromString);
    670   converter->RegisterRepeatedMessage<FileResource>(kItems,
    671                                                    &FileList::items_);
    672 }
    673 
    674 // static
    675 bool FileList::HasFileListKind(const base::Value& value) {
    676   return IsResourceKindExpected(value, kFileListKind);
    677 }
    678 
    679 // static
    680 scoped_ptr<FileList> FileList::CreateFrom(const base::Value& value) {
    681   scoped_ptr<FileList> resource(new FileList());
    682   if (!HasFileListKind(value) || !resource->Parse(value)) {
    683     LOG(ERROR) << "Unable to create: Invalid FileList JSON!";
    684     return scoped_ptr<FileList>();
    685   }
    686   return resource.Pass();
    687 }
    688 
    689 bool FileList::Parse(const base::Value& value) {
    690   base::JSONValueConverter<FileList> converter;
    691   if (!converter.Convert(value, this)) {
    692     LOG(ERROR) << "Unable to parse: Invalid FileList";
    693     return false;
    694   }
    695   return true;
    696 }
    697 
    698 ////////////////////////////////////////////////////////////////////////////////
    699 // ChangeResource implementation
    700 
    701 ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
    702 
    703 ChangeResource::~ChangeResource() {}
    704 
    705 // static
    706 void ChangeResource::RegisterJSONConverter(
    707     base::JSONValueConverter<ChangeResource>* converter) {
    708   converter->RegisterCustomField<int64>(kId,
    709                                         &ChangeResource::change_id_,
    710                                         &base::StringToInt64);
    711   converter->RegisterStringField(kFileId, &ChangeResource::file_id_);
    712   converter->RegisterBoolField(kDeleted, &ChangeResource::deleted_);
    713   converter->RegisterCustomValueField(kFile, &ChangeResource::file_,
    714                                       &CreateFileResourceFromValue);
    715 }
    716 
    717 // static
    718 scoped_ptr<ChangeResource>
    719 ChangeResource::CreateFrom(const base::Value& value) {
    720   scoped_ptr<ChangeResource> resource(new ChangeResource());
    721   if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) {
    722     LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!";
    723     return scoped_ptr<ChangeResource>();
    724   }
    725   return resource.Pass();
    726 }
    727 
    728 bool ChangeResource::Parse(const base::Value& value) {
    729   base::JSONValueConverter<ChangeResource> converter;
    730   if (!converter.Convert(value, this)) {
    731     LOG(ERROR) << "Unable to parse: Invalid ChangeResource";
    732     return false;
    733   }
    734   return true;
    735 }
    736 
    737 ////////////////////////////////////////////////////////////////////////////////
    738 // ChangeList implementation
    739 
    740 ChangeList::ChangeList() : largest_change_id_(0) {}
    741 
    742 ChangeList::~ChangeList() {}
    743 
    744 // static
    745 void ChangeList::RegisterJSONConverter(
    746     base::JSONValueConverter<ChangeList>* converter) {
    747   converter->RegisterStringField(kETag, &ChangeList::etag_);
    748   converter->RegisterStringField(kNextPageToken, &ChangeList::next_page_token_);
    749   converter->RegisterCustomField<GURL>(kNextLink,
    750                                        &ChangeList::next_link_,
    751                                        GetGURLFromString);
    752   converter->RegisterCustomField<int64>(kLargestChangeId,
    753                                         &ChangeList::largest_change_id_,
    754                                         &base::StringToInt64);
    755   converter->RegisterRepeatedMessage<ChangeResource>(kItems,
    756                                                      &ChangeList::items_);
    757 }
    758 
    759 // static
    760 bool ChangeList::HasChangeListKind(const base::Value& value) {
    761   return IsResourceKindExpected(value, kChangeListKind);
    762 }
    763 
    764 // static
    765 scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) {
    766   scoped_ptr<ChangeList> resource(new ChangeList());
    767   if (!HasChangeListKind(value) || !resource->Parse(value)) {
    768     LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!";
    769     return scoped_ptr<ChangeList>();
    770   }
    771   return resource.Pass();
    772 }
    773 
    774 bool ChangeList::Parse(const base::Value& value) {
    775   base::JSONValueConverter<ChangeList> converter;
    776   if (!converter.Convert(value, this)) {
    777     LOG(ERROR) << "Unable to parse: Invalid ChangeList";
    778     return false;
    779   }
    780   return true;
    781 }
    782 
    783 
    784 ////////////////////////////////////////////////////////////////////////////////
    785 // FileLabels implementation
    786 
    787 FileLabels::FileLabels()
    788     : starred_(false),
    789       hidden_(false),
    790       trashed_(false),
    791       restricted_(false),
    792       viewed_(false) {}
    793 
    794 FileLabels::~FileLabels() {}
    795 
    796 // static
    797 void FileLabels::RegisterJSONConverter(
    798     base::JSONValueConverter<FileLabels>* converter) {
    799   converter->RegisterBoolField(kLabelStarred, &FileLabels::starred_);
    800   converter->RegisterBoolField(kLabelHidden, &FileLabels::hidden_);
    801   converter->RegisterBoolField(kLabelTrashed, &FileLabels::trashed_);
    802   converter->RegisterBoolField(kLabelRestricted, &FileLabels::restricted_);
    803   converter->RegisterBoolField(kLabelViewed, &FileLabels::viewed_);
    804 }
    805 
    806 // static
    807 scoped_ptr<FileLabels> FileLabels::CreateFrom(const base::Value& value) {
    808   scoped_ptr<FileLabels> resource(new FileLabels());
    809   if (!resource->Parse(value)) {
    810     LOG(ERROR) << "Unable to create: Invalid FileLabels JSON!";
    811     return scoped_ptr<FileLabels>();
    812   }
    813   return resource.Pass();
    814 }
    815 
    816 bool FileLabels::Parse(const base::Value& value) {
    817   base::JSONValueConverter<FileLabels> converter;
    818   if (!converter.Convert(value, this)) {
    819     LOG(ERROR) << "Unable to parse: Invalid FileLabels";
    820     return false;
    821   }
    822   return true;
    823 }
    824 
    825 }  // namespace google_apis
    826