1cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// Use of this source code is governed by a BSD-style license that can be 3cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// found in the LICENSE file. 4cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com 5cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include "google_apis/drive/gdata_wapi_parser.h" 6cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com 7cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include <algorithm> 8cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include <string> 9cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com 10ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/basictypes.h" 11ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/files/file_path.h" 12ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/json/json_value_converter.h" 132af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/memory/scoped_ptr.h" 142af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_number_conversions.h" 152af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_piece.h" 162af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_util.h" 172af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/utf_string_conversions.h" 182af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/values.h" 19ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "google_apis/drive/time_util.h" 20ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com 21ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::Value; 22ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::DictionaryValue; 23ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::ListValue; 241f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com 25ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comnamespace google_apis { 26ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com 27ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comnamespace { 28ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com 298f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Term values for kSchemeKind category: 308f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTermPrefix[] = "http://schemas.google.com/docs/2007#"; 318f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com 32ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com// Node names. 338f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kEntryNode[] = "entry"; 348f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com 358f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Field names. 368f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kAuthorField[] = "author"; 378f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kCategoryField[] = "category"; 38ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kChangestampField[] = "docs$changestamp.value"; 398f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kContentField[] = "content"; 40ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kDeletedField[] = "gd$deleted"; 41ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kETagField[] = "gd$etag"; 422c8177767a4b3b6c27c6ac071c8619b557472521edisonn@google.comconst char kEmailField[] = "email.$t"; 438f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kEntryField[] = "entry"; 44ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kFeedField[] = "feed"; 458f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kFeedLinkField[] = "gd$feedLink"; 46ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kFileNameField[] = "docs$filename.$t"; 478f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kHrefField[] = "href"; 48ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kIDField[] = "id.$t"; 498f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kItemsPerPageField[] = "openSearch$itemsPerPage.$t"; 50ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLabelField[] = "label"; 51ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLargestChangestampField[] = "docs$largestChangestamp.value"; 52ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLastViewedField[] = "gd$lastViewed.$t"; 53ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLinkField[] = "link"; 548f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kMD5Field[] = "docs$md5Checksum.$t"; 558f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kNameField[] = "name.$t"; 568f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kPublishedField[] = "published.$t"; 578f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kRelField[] = "rel"; 588f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kRemovedField[] = "docs$removed"; 598f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kResourceIdField[] = "gd$resourceId.$t"; 608f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kSchemeField[] = "scheme"; 618f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kSizeField[] = "docs$size.$t"; 625c70cdca5efe541b70d010e91607bf8626ea49cacommit-bot@chromium.orgconst char kSrcField[] = "src"; 638f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kStartIndexField[] = "openSearch$startIndex.$t"; 645c70cdca5efe541b70d010e91607bf8626ea49cacommit-bot@chromium.orgconst char kSuggestedFileNameField[] = "docs$suggestedFilename.$t"; 658f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTermField[] = "term"; 668f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTitleField[] = "title"; 67370a89980b2d38a6d01903b484bf404d6c48b496skia.committer@gmail.comconst char kTitleTField[] = "title.$t"; 68370a89980b2d38a6d01903b484bf404d6c48b496skia.committer@gmail.comconst char kTypeField[] = "type"; 698f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kUpdatedField[] = "updated.$t"; 708f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com 718f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Link Prefixes 728f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-"; 738f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1; 748f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com 758f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comstruct LinkTypeMap { 768f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com Link::LinkType type; 778f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com const char* rel; 788f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com}; 79ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com 80ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst LinkTypeMap kLinkTypeMap[] = { 81ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com { Link::LINK_SELF, 821f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com "self" }, 83ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com { Link::LINK_NEXT, 84e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org "next" }, 85e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org { Link::LINK_PARENT, 86e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org "http://schemas.google.com/docs/2007#parent" }, 87e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org { Link::LINK_ALTERNATE, 88e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org "alternate"}, 89e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org { Link::LINK_EDIT, 90e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "edit" }, 91e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com { Link::LINK_EDIT_MEDIA, 92e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "edit-media" }, 93e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com { Link::LINK_ALT_EDIT_MEDIA, 94e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "http://schemas.google.com/docs/2007#alt-edit-media" }, 95e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com { Link::LINK_ALT_POST, 96e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "http://schemas.google.com/docs/2007#alt-post" }, 97e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com { Link::LINK_FEED, 98e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "http://schemas.google.com/g/2005#feed"}, 99e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com { Link::LINK_POST, 100e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com "http://schemas.google.com/g/2005#post"}, 1018f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com { Link::LINK_BATCH, 1028f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com "http://schemas.google.com/g/2005#batch"}, 1038f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com { Link::LINK_THUMBNAIL, 1048f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com "http://schemas.google.com/docs/2007/thumbnail"}, 1058f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com { Link::LINK_RESUMABLE_EDIT_MEDIA, 1069b14f26d0f3a974f3dd626c8354e1db1cfcd322frobertphillips "http://schemas.google.com/g/2005#resumable-edit-media"}, 1079b14f26d0f3a974f3dd626c8354e1db1cfcd322frobertphillips { Link::LINK_RESUMABLE_CREATE_MEDIA, 108ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com "http://schemas.google.com/g/2005#resumable-create-media"}, 109ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com { Link::LINK_TABLES_FEED, 110ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, 111ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com { Link::LINK_WORKSHEET_FEED, 112cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"}, 113 { Link::LINK_EMBED, 114 "http://schemas.google.com/docs/2007#embed"}, 115 { Link::LINK_PRODUCT, 116 "http://schemas.google.com/docs/2007#product"}, 117 { Link::LINK_ICON, 118 "http://schemas.google.com/docs/2007#icon"}, 119 { Link::LINK_SHARE, 120 "http://schemas.google.com/docs/2007#share"}, 121}; 122 123struct ResourceLinkTypeMap { 124 ResourceLink::ResourceLinkType type; 125 const char* rel; 126}; 127 128const ResourceLinkTypeMap kFeedLinkTypeMap[] = { 129 { ResourceLink::FEED_LINK_ACL, 130 "http://schemas.google.com/acl/2007#accessControlList" }, 131 { ResourceLink::FEED_LINK_REVISIONS, 132 "http://schemas.google.com/docs/2007/revisions" }, 133}; 134 135struct CategoryTypeMap { 136 Category::CategoryType type; 137 const char* scheme; 138}; 139 140const CategoryTypeMap kCategoryTypeMap[] = { 141 { Category::CATEGORY_KIND, "http://schemas.google.com/g/2005#kind" }, 142 { Category::CATEGORY_LABEL, "http://schemas.google.com/g/2005/labels" }, 143}; 144 145// Converts |url_string| to |result|. Always returns true to be used 146// for JSONValueConverter::RegisterCustomField method. 147// TODO(mukai): make it return false in case of invalid |url_string|. 148bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) { 149 *result = GURL(url_string.as_string()); 150 return true; 151} 152 153} // namespace 154 155//////////////////////////////////////////////////////////////////////////////// 156// Author implementation 157 158Author::Author() { 159} 160 161// static 162void Author::RegisterJSONConverter( 163 base::JSONValueConverter<Author>* converter) { 164 converter->RegisterStringField(kNameField, &Author::name_); 165 converter->RegisterStringField(kEmailField, &Author::email_); 166} 167 168//////////////////////////////////////////////////////////////////////////////// 169// Link implementation 170 171Link::Link() : type_(Link::LINK_UNKNOWN) { 172} 173 174Link::~Link() { 175} 176 177// static 178bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) { 179 DCHECK(app_id); 180 // Fast return path if the link clearly isn't an OPEN_WITH link. 181 if (rel.size() < kOpenWithPrefixSize) { 182 app_id->clear(); 183 return true; 184 } 185 186 const std::string kOpenWithPrefixStr(kOpenWithPrefix); 187 if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) { 188 *app_id = rel.as_string().substr(kOpenWithPrefixStr.size()); 189 return true; 190 } 191 192 app_id->clear(); 193 return true; 194} 195 196// static. 197bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) { 198 DCHECK(type); 199 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) { 200 if (rel == kLinkTypeMap[i].rel) { 201 *type = kLinkTypeMap[i].type; 202 return true; 203 } 204 } 205 206 // OPEN_WITH links have extra information at the end of the rel that is unique 207 // for each one, so we can't just check the usual map. This check is slightly 208 // redundant to provide a quick skip if it's obviously not an OPEN_WITH url. 209 if (rel.size() >= kOpenWithPrefixSize && 210 StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) { 211 *type = LINK_OPEN_WITH; 212 return true; 213 } 214 215 // Let unknown link types through, just report it; if the link type is needed 216 // in the future, add it into LinkType and kLinkTypeMap. 217 DVLOG(1) << "Ignoring unknown link type for rel " << rel; 218 *type = LINK_UNKNOWN; 219 return true; 220} 221 222// static 223void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) { 224 converter->RegisterCustomField<Link::LinkType>(kRelField, 225 &Link::type_, 226 &Link::GetLinkType); 227 // We have to register kRelField twice because we extract two different pieces 228 // of data from the same rel field. 229 converter->RegisterCustomField<std::string>(kRelField, 230 &Link::app_id_, 231 &Link::GetAppID); 232 converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString); 233 converter->RegisterStringField(kTitleField, &Link::title_); 234 converter->RegisterStringField(kTypeField, &Link::mime_type_); 235} 236 237//////////////////////////////////////////////////////////////////////////////// 238// ResourceLink implementation 239 240ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN) { 241} 242 243// static. 244bool ResourceLink::GetFeedLinkType( 245 const base::StringPiece& rel, ResourceLink::ResourceLinkType* result) { 246 for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) { 247 if (rel == kFeedLinkTypeMap[i].rel) { 248 *result = kFeedLinkTypeMap[i].type; 249 return true; 250 } 251 } 252 DVLOG(1) << "Unknown feed link type for rel " << rel; 253 return false; 254} 255 256// static 257void ResourceLink::RegisterJSONConverter( 258 base::JSONValueConverter<ResourceLink>* converter) { 259 converter->RegisterCustomField<ResourceLink::ResourceLinkType>( 260 kRelField, &ResourceLink::type_, &ResourceLink::GetFeedLinkType); 261 converter->RegisterCustomField( 262 kHrefField, &ResourceLink::href_, &GetGURLFromString); 263} 264 265//////////////////////////////////////////////////////////////////////////////// 266// Category implementation 267 268Category::Category() : type_(CATEGORY_UNKNOWN) { 269} 270 271// Converts category.scheme into CategoryType enum. 272bool Category::GetCategoryTypeFromScheme( 273 const base::StringPiece& scheme, Category::CategoryType* result) { 274 for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) { 275 if (scheme == kCategoryTypeMap[i].scheme) { 276 *result = kCategoryTypeMap[i].type; 277 return true; 278 } 279 } 280 DVLOG(1) << "Unknown feed link type for scheme " << scheme; 281 return false; 282} 283 284// static 285void Category::RegisterJSONConverter( 286 base::JSONValueConverter<Category>* converter) { 287 converter->RegisterStringField(kLabelField, &Category::label_); 288 converter->RegisterCustomField<Category::CategoryType>( 289 kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme); 290 converter->RegisterStringField(kTermField, &Category::term_); 291} 292 293const Link* CommonMetadata::GetLinkByType(Link::LinkType type) const { 294 for (size_t i = 0; i < links_.size(); ++i) { 295 if (links_[i]->type() == type) 296 return links_[i]; 297 } 298 return NULL; 299} 300 301//////////////////////////////////////////////////////////////////////////////// 302// Content implementation 303 304Content::Content() { 305} 306 307// static 308void Content::RegisterJSONConverter( 309 base::JSONValueConverter<Content>* converter) { 310 converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString); 311 converter->RegisterStringField(kTypeField, &Content::mime_type_); 312} 313 314//////////////////////////////////////////////////////////////////////////////// 315// CommonMetadata implementation 316 317CommonMetadata::CommonMetadata() { 318} 319 320CommonMetadata::~CommonMetadata() { 321} 322 323// static 324template<typename CommonMetadataDescendant> 325void CommonMetadata::RegisterJSONConverter( 326 base::JSONValueConverter<CommonMetadataDescendant>* converter) { 327 converter->RegisterStringField(kETagField, &CommonMetadata::etag_); 328 converter->template RegisterRepeatedMessage<Author>( 329 kAuthorField, &CommonMetadata::authors_); 330 converter->template RegisterRepeatedMessage<Link>( 331 kLinkField, &CommonMetadata::links_); 332 converter->template RegisterRepeatedMessage<Category>( 333 kCategoryField, &CommonMetadata::categories_); 334 converter->template RegisterCustomField<base::Time>( 335 kUpdatedField, &CommonMetadata::updated_time_, &util::GetTimeFromString); 336} 337 338//////////////////////////////////////////////////////////////////////////////// 339// ResourceEntry implementation 340 341ResourceEntry::ResourceEntry() 342 : kind_(ENTRY_KIND_UNKNOWN), 343 file_size_(0), 344 deleted_(false), 345 removed_(false), 346 changestamp_(0), 347 image_width_(-1), 348 image_height_(-1), 349 image_rotation_(-1) { 350} 351 352ResourceEntry::~ResourceEntry() { 353} 354 355bool ResourceEntry::HasFieldPresent(const base::Value* value, 356 bool* result) { 357 *result = (value != NULL); 358 return true; 359} 360 361bool ResourceEntry::ParseChangestamp(const base::Value* value, 362 int64* result) { 363 DCHECK(result); 364 if (!value) { 365 *result = 0; 366 return true; 367 } 368 369 std::string string_value; 370 if (value->GetAsString(&string_value) && 371 base::StringToInt64(string_value, result)) 372 return true; 373 374 return false; 375} 376 377// static 378void ResourceEntry::RegisterJSONConverter( 379 base::JSONValueConverter<ResourceEntry>* converter) { 380 // Inherit the parent registrations. 381 CommonMetadata::RegisterJSONConverter(converter); 382 converter->RegisterStringField( 383 kResourceIdField, &ResourceEntry::resource_id_); 384 converter->RegisterStringField(kIDField, &ResourceEntry::id_); 385 converter->RegisterStringField(kTitleTField, &ResourceEntry::title_); 386 converter->RegisterCustomField<base::Time>( 387 kPublishedField, &ResourceEntry::published_time_, 388 &util::GetTimeFromString); 389 converter->RegisterCustomField<base::Time>( 390 kLastViewedField, &ResourceEntry::last_viewed_time_, 391 &util::GetTimeFromString); 392 converter->RegisterRepeatedMessage( 393 kFeedLinkField, &ResourceEntry::resource_links_); 394 converter->RegisterNestedField(kContentField, &ResourceEntry::content_); 395 396 // File properties. If the resource type is not a normal file, then 397 // that's no problem because those feed must not have these fields 398 // themselves, which does not report errors. 399 converter->RegisterStringField(kFileNameField, &ResourceEntry::filename_); 400 converter->RegisterStringField(kMD5Field, &ResourceEntry::file_md5_); 401 converter->RegisterCustomField<int64>( 402 kSizeField, &ResourceEntry::file_size_, &base::StringToInt64); 403 converter->RegisterStringField( 404 kSuggestedFileNameField, &ResourceEntry::suggested_filename_); 405 // Deleted are treated as 'trashed' items on web client side. Removed files 406 // are gone for good. We treat both cases as 'deleted' for this client. 407 converter->RegisterCustomValueField<bool>( 408 kDeletedField, &ResourceEntry::deleted_, &ResourceEntry::HasFieldPresent); 409 converter->RegisterCustomValueField<bool>( 410 kRemovedField, &ResourceEntry::removed_, &ResourceEntry::HasFieldPresent); 411 converter->RegisterCustomValueField<int64>( 412 kChangestampField, &ResourceEntry::changestamp_, 413 &ResourceEntry::ParseChangestamp); 414 // ImageMediaMetadata fields are not supported by WAPI. 415} 416 417// static 418ResourceEntry::ResourceEntryKind ResourceEntry::GetEntryKindFromTerm( 419 const std::string& term) { 420 if (!StartsWithASCII(term, kTermPrefix, false)) { 421 DVLOG(1) << "Unexpected term prefix term " << term; 422 return ENTRY_KIND_UNKNOWN; 423 } 424 425 std::string type = term.substr(strlen(kTermPrefix)); 426 if (type == "folder") 427 return ENTRY_KIND_FOLDER; 428 if (type == "file" || type == "pdf") 429 return ENTRY_KIND_FILE; 430 431 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; 432 return ENTRY_KIND_UNKNOWN; 433} 434 435void ResourceEntry::FillRemainingFields() { 436 // Set |kind_| and |labels_| based on the |categories_| in the class. 437 // JSONValueConverter does not have the ability to catch an element in a list 438 // based on a predicate. Thus we need to iterate over |categories_| and 439 // find the elements to set these fields as a post-process. 440 for (size_t i = 0; i < categories_.size(); ++i) { 441 const Category* category = categories_[i]; 442 if (category->type() == Category::CATEGORY_KIND) 443 kind_ = GetEntryKindFromTerm(category->term()); 444 else if (category->type() == Category::CATEGORY_LABEL) 445 labels_.push_back(category->label()); 446 } 447} 448 449// static 450scoped_ptr<ResourceEntry> ResourceEntry::ExtractAndParse( 451 const base::Value& value) { 452 const base::DictionaryValue* as_dict = NULL; 453 const base::DictionaryValue* entry_dict = NULL; 454 if (value.GetAsDictionary(&as_dict) && 455 as_dict->GetDictionary(kEntryField, &entry_dict)) { 456 return ResourceEntry::CreateFrom(*entry_dict); 457 } 458 return scoped_ptr<ResourceEntry>(); 459} 460 461// static 462scoped_ptr<ResourceEntry> ResourceEntry::CreateFrom(const base::Value& value) { 463 base::JSONValueConverter<ResourceEntry> converter; 464 scoped_ptr<ResourceEntry> entry(new ResourceEntry()); 465 if (!converter.Convert(value, entry.get())) { 466 DVLOG(1) << "Invalid resource entry!"; 467 return scoped_ptr<ResourceEntry>(); 468 } 469 470 entry->FillRemainingFields(); 471 return entry.Pass(); 472} 473 474// static 475std::string ResourceEntry::GetEntryNodeName() { 476 return kEntryNode; 477} 478 479//////////////////////////////////////////////////////////////////////////////// 480// ResourceList implementation 481 482ResourceList::ResourceList() 483 : start_index_(0), 484 items_per_page_(0), 485 largest_changestamp_(0) { 486} 487 488ResourceList::~ResourceList() { 489} 490 491// static 492void ResourceList::RegisterJSONConverter( 493 base::JSONValueConverter<ResourceList>* converter) { 494 // inheritance 495 CommonMetadata::RegisterJSONConverter(converter); 496 // TODO(zelidrag): Once we figure out where these will be used, we should 497 // check for valid start_index_ and items_per_page_ values. 498 converter->RegisterCustomField<int>( 499 kStartIndexField, &ResourceList::start_index_, &base::StringToInt); 500 converter->RegisterCustomField<int>( 501 kItemsPerPageField, &ResourceList::items_per_page_, &base::StringToInt); 502 converter->RegisterStringField(kTitleTField, &ResourceList::title_); 503 converter->RegisterRepeatedMessage(kEntryField, &ResourceList::entries_); 504 converter->RegisterCustomField<int64>( 505 kLargestChangestampField, &ResourceList::largest_changestamp_, 506 &base::StringToInt64); 507} 508 509bool ResourceList::Parse(const base::Value& value) { 510 base::JSONValueConverter<ResourceList> converter; 511 if (!converter.Convert(value, this)) { 512 DVLOG(1) << "Invalid resource list!"; 513 return false; 514 } 515 516 ScopedVector<ResourceEntry>::iterator iter = entries_.begin(); 517 while (iter != entries_.end()) { 518 ResourceEntry* entry = (*iter); 519 entry->FillRemainingFields(); 520 ++iter; 521 } 522 return true; 523} 524 525// static 526scoped_ptr<ResourceList> ResourceList::ExtractAndParse( 527 const base::Value& value) { 528 const base::DictionaryValue* as_dict = NULL; 529 const base::DictionaryValue* feed_dict = NULL; 530 if (value.GetAsDictionary(&as_dict) && 531 as_dict->GetDictionary(kFeedField, &feed_dict)) { 532 return ResourceList::CreateFrom(*feed_dict); 533 } 534 return scoped_ptr<ResourceList>(); 535} 536 537// static 538scoped_ptr<ResourceList> ResourceList::CreateFrom(const base::Value& value) { 539 scoped_ptr<ResourceList> feed(new ResourceList()); 540 if (!feed->Parse(value)) { 541 DVLOG(1) << "Invalid resource list!"; 542 return scoped_ptr<ResourceList>(); 543 } 544 545 return feed.Pass(); 546} 547 548bool ResourceList::GetNextFeedURL(GURL* url) const { 549 DCHECK(url); 550 for (size_t i = 0; i < links_.size(); ++i) { 551 if (links_[i]->type() == Link::LINK_NEXT) { 552 *url = links_[i]->href(); 553 return true; 554 } 555 } 556 return false; 557} 558 559void ResourceList::ReleaseEntries(std::vector<ResourceEntry*>* entries) { 560 entries_.release(entries); 561} 562 563} // namespace google_apis 564