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/gdata_wapi_parser.h" 6 7 #include <algorithm> 8 #include <string> 9 10 #include "base/basictypes.h" 11 #include "base/files/file_path.h" 12 #include "base/json/json_value_converter.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_piece.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/values.h" 19 #include "google_apis/drive/time_util.h" 20 21 using base::Value; 22 using base::DictionaryValue; 23 using base::ListValue; 24 25 namespace google_apis { 26 27 namespace { 28 29 // Term values for kSchemeKind category: 30 const char kTermPrefix[] = "http://schemas.google.com/docs/2007#"; 31 32 // Node names. 33 const char kEntryNode[] = "entry"; 34 35 // Field names. 36 const char kAuthorField[] = "author"; 37 const char kCategoryField[] = "category"; 38 const char kChangestampField[] = "docs$changestamp.value"; 39 const char kContentField[] = "content"; 40 const char kDeletedField[] = "gd$deleted"; 41 const char kETagField[] = "gd$etag"; 42 const char kEmailField[] = "email.$t"; 43 const char kEntryField[] = "entry"; 44 const char kFeedField[] = "feed"; 45 const char kFeedLinkField[] = "gd$feedLink"; 46 const char kFileNameField[] = "docs$filename.$t"; 47 const char kHrefField[] = "href"; 48 const char kIDField[] = "id.$t"; 49 const char kInstalledAppField[] = "docs$installedApp"; 50 const char kInstalledAppNameField[] = "docs$installedAppName"; 51 const char kInstalledAppIdField[] = "docs$installedAppId"; 52 const char kInstalledAppIconField[] = "docs$installedAppIcon"; 53 const char kInstalledAppIconCategoryField[] = "docs$installedAppIconCategory"; 54 const char kInstalledAppIconSizeField[] = "docs$installedAppIconSize"; 55 const char kInstalledAppObjectTypeField[] = "docs$installedAppObjectType"; 56 const char kInstalledAppPrimaryFileExtensionField[] = 57 "docs$installedAppPrimaryFileExtension"; 58 const char kInstalledAppPrimaryMimeTypeField[] = 59 "docs$installedAppPrimaryMimeType"; 60 const char kInstalledAppSecondaryFileExtensionField[] = 61 "docs$installedAppSecondaryFileExtension"; 62 const char kInstalledAppSecondaryMimeTypeField[] = 63 "docs$installedAppSecondaryMimeType"; 64 const char kInstalledAppSupportsCreateField[] = 65 "docs$installedAppSupportsCreate"; 66 const char kItemsPerPageField[] = "openSearch$itemsPerPage.$t"; 67 const char kLabelField[] = "label"; 68 const char kLargestChangestampField[] = "docs$largestChangestamp.value"; 69 const char kLastViewedField[] = "gd$lastViewed.$t"; 70 const char kLinkField[] = "link"; 71 const char kMD5Field[] = "docs$md5Checksum.$t"; 72 const char kNameField[] = "name.$t"; 73 const char kPublishedField[] = "published.$t"; 74 const char kQuotaBytesTotalField[] = "gd$quotaBytesTotal.$t"; 75 const char kQuotaBytesUsedField[] = "gd$quotaBytesUsed.$t"; 76 const char kRelField[] = "rel"; 77 const char kRemovedField[] = "docs$removed"; 78 const char kResourceIdField[] = "gd$resourceId.$t"; 79 const char kSchemeField[] = "scheme"; 80 const char kSizeField[] = "docs$size.$t"; 81 const char kSrcField[] = "src"; 82 const char kStartIndexField[] = "openSearch$startIndex.$t"; 83 const char kSuggestedFileNameField[] = "docs$suggestedFilename.$t"; 84 const char kTField[] = "$t"; 85 const char kTermField[] = "term"; 86 const char kTitleField[] = "title"; 87 const char kTitleTField[] = "title.$t"; 88 const char kTypeField[] = "type"; 89 const char kUpdatedField[] = "updated.$t"; 90 91 // Link Prefixes 92 const char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-"; 93 const size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1; 94 95 struct EntryKindMap { 96 DriveEntryKind kind; 97 const char* entry; 98 const char* extension; 99 }; 100 101 const EntryKindMap kEntryKindMap[] = { 102 { ENTRY_KIND_UNKNOWN, "unknown", NULL}, 103 { ENTRY_KIND_ITEM, "item", NULL}, 104 { ENTRY_KIND_DOCUMENT, "document", ".gdoc"}, 105 { ENTRY_KIND_SPREADSHEET, "spreadsheet", ".gsheet"}, 106 { ENTRY_KIND_PRESENTATION, "presentation", ".gslides" }, 107 { ENTRY_KIND_DRAWING, "drawing", ".gdraw"}, 108 { ENTRY_KIND_TABLE, "table", ".gtable"}, 109 { ENTRY_KIND_FORM, "form", ".gform"}, 110 { ENTRY_KIND_EXTERNAL_APP, "externalapp", ".glink"}, 111 { ENTRY_KIND_SITE, "site", NULL}, 112 { ENTRY_KIND_FOLDER, "folder", NULL}, 113 { ENTRY_KIND_FILE, "file", NULL}, 114 { ENTRY_KIND_PDF, "pdf", NULL}, 115 }; 116 COMPILE_ASSERT(arraysize(kEntryKindMap) == ENTRY_KIND_MAX_VALUE, 117 EntryKindMap_and_DriveEntryKind_are_not_in_sync); 118 119 struct LinkTypeMap { 120 Link::LinkType type; 121 const char* rel; 122 }; 123 124 const LinkTypeMap kLinkTypeMap[] = { 125 { Link::LINK_SELF, 126 "self" }, 127 { Link::LINK_NEXT, 128 "next" }, 129 { Link::LINK_PARENT, 130 "http://schemas.google.com/docs/2007#parent" }, 131 { Link::LINK_ALTERNATE, 132 "alternate"}, 133 { Link::LINK_EDIT, 134 "edit" }, 135 { Link::LINK_EDIT_MEDIA, 136 "edit-media" }, 137 { Link::LINK_ALT_EDIT_MEDIA, 138 "http://schemas.google.com/docs/2007#alt-edit-media" }, 139 { Link::LINK_ALT_POST, 140 "http://schemas.google.com/docs/2007#alt-post" }, 141 { Link::LINK_FEED, 142 "http://schemas.google.com/g/2005#feed"}, 143 { Link::LINK_POST, 144 "http://schemas.google.com/g/2005#post"}, 145 { Link::LINK_BATCH, 146 "http://schemas.google.com/g/2005#batch"}, 147 { Link::LINK_THUMBNAIL, 148 "http://schemas.google.com/docs/2007/thumbnail"}, 149 { Link::LINK_RESUMABLE_EDIT_MEDIA, 150 "http://schemas.google.com/g/2005#resumable-edit-media"}, 151 { Link::LINK_RESUMABLE_CREATE_MEDIA, 152 "http://schemas.google.com/g/2005#resumable-create-media"}, 153 { Link::LINK_TABLES_FEED, 154 "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, 155 { Link::LINK_WORKSHEET_FEED, 156 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"}, 157 { Link::LINK_EMBED, 158 "http://schemas.google.com/docs/2007#embed"}, 159 { Link::LINK_PRODUCT, 160 "http://schemas.google.com/docs/2007#product"}, 161 { Link::LINK_ICON, 162 "http://schemas.google.com/docs/2007#icon"}, 163 { Link::LINK_SHARE, 164 "http://schemas.google.com/docs/2007#share"}, 165 }; 166 167 struct ResourceLinkTypeMap { 168 ResourceLink::ResourceLinkType type; 169 const char* rel; 170 }; 171 172 const ResourceLinkTypeMap kFeedLinkTypeMap[] = { 173 { ResourceLink::FEED_LINK_ACL, 174 "http://schemas.google.com/acl/2007#accessControlList" }, 175 { ResourceLink::FEED_LINK_REVISIONS, 176 "http://schemas.google.com/docs/2007/revisions" }, 177 }; 178 179 struct CategoryTypeMap { 180 Category::CategoryType type; 181 const char* scheme; 182 }; 183 184 const CategoryTypeMap kCategoryTypeMap[] = { 185 { Category::CATEGORY_KIND, "http://schemas.google.com/g/2005#kind" }, 186 { Category::CATEGORY_LABEL, "http://schemas.google.com/g/2005/labels" }, 187 }; 188 189 struct AppIconCategoryMap { 190 AppIcon::IconCategory category; 191 const char* category_name; 192 }; 193 194 const AppIconCategoryMap kAppIconCategoryMap[] = { 195 { AppIcon::ICON_DOCUMENT, "document" }, 196 { AppIcon::ICON_APPLICATION, "application" }, 197 { AppIcon::ICON_SHARED_DOCUMENT, "documentShared" }, 198 }; 199 200 // Converts |url_string| to |result|. Always returns true to be used 201 // for JSONValueConverter::RegisterCustomField method. 202 // TODO(mukai): make it return false in case of invalid |url_string|. 203 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) { 204 *result = GURL(url_string.as_string()); 205 return true; 206 } 207 208 // Converts boolean string values like "true" into bool. 209 bool GetBoolFromString(const base::StringPiece& value, bool* result) { 210 *result = (value == "true"); 211 return true; 212 } 213 214 bool SortBySize(const InstalledApp::IconList::value_type& a, 215 const InstalledApp::IconList::value_type& b) { 216 return a.first < b.first; 217 } 218 219 } // namespace 220 221 //////////////////////////////////////////////////////////////////////////////// 222 // Author implementation 223 224 Author::Author() { 225 } 226 227 // static 228 void Author::RegisterJSONConverter( 229 base::JSONValueConverter<Author>* converter) { 230 converter->RegisterStringField(kNameField, &Author::name_); 231 converter->RegisterStringField(kEmailField, &Author::email_); 232 } 233 234 //////////////////////////////////////////////////////////////////////////////// 235 // Link implementation 236 237 Link::Link() : type_(Link::LINK_UNKNOWN) { 238 } 239 240 Link::~Link() { 241 } 242 243 // static 244 bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) { 245 DCHECK(app_id); 246 // Fast return path if the link clearly isn't an OPEN_WITH link. 247 if (rel.size() < kOpenWithPrefixSize) { 248 app_id->clear(); 249 return true; 250 } 251 252 const std::string kOpenWithPrefixStr(kOpenWithPrefix); 253 if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) { 254 *app_id = rel.as_string().substr(kOpenWithPrefixStr.size()); 255 return true; 256 } 257 258 app_id->clear(); 259 return true; 260 } 261 262 // static. 263 bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) { 264 DCHECK(type); 265 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) { 266 if (rel == kLinkTypeMap[i].rel) { 267 *type = kLinkTypeMap[i].type; 268 return true; 269 } 270 } 271 272 // OPEN_WITH links have extra information at the end of the rel that is unique 273 // for each one, so we can't just check the usual map. This check is slightly 274 // redundant to provide a quick skip if it's obviously not an OPEN_WITH url. 275 if (rel.size() >= kOpenWithPrefixSize && 276 StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) { 277 *type = LINK_OPEN_WITH; 278 return true; 279 } 280 281 // Let unknown link types through, just report it; if the link type is needed 282 // in the future, add it into LinkType and kLinkTypeMap. 283 DVLOG(1) << "Ignoring unknown link type for rel " << rel; 284 *type = LINK_UNKNOWN; 285 return true; 286 } 287 288 // static 289 void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) { 290 converter->RegisterCustomField<Link::LinkType>(kRelField, 291 &Link::type_, 292 &Link::GetLinkType); 293 // We have to register kRelField twice because we extract two different pieces 294 // of data from the same rel field. 295 converter->RegisterCustomField<std::string>(kRelField, 296 &Link::app_id_, 297 &Link::GetAppID); 298 converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString); 299 converter->RegisterStringField(kTitleField, &Link::title_); 300 converter->RegisterStringField(kTypeField, &Link::mime_type_); 301 } 302 303 //////////////////////////////////////////////////////////////////////////////// 304 // ResourceLink implementation 305 306 ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN) { 307 } 308 309 // static. 310 bool ResourceLink::GetFeedLinkType( 311 const base::StringPiece& rel, ResourceLink::ResourceLinkType* result) { 312 for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) { 313 if (rel == kFeedLinkTypeMap[i].rel) { 314 *result = kFeedLinkTypeMap[i].type; 315 return true; 316 } 317 } 318 DVLOG(1) << "Unknown feed link type for rel " << rel; 319 return false; 320 } 321 322 // static 323 void ResourceLink::RegisterJSONConverter( 324 base::JSONValueConverter<ResourceLink>* converter) { 325 converter->RegisterCustomField<ResourceLink::ResourceLinkType>( 326 kRelField, &ResourceLink::type_, &ResourceLink::GetFeedLinkType); 327 converter->RegisterCustomField( 328 kHrefField, &ResourceLink::href_, &GetGURLFromString); 329 } 330 331 //////////////////////////////////////////////////////////////////////////////// 332 // Category implementation 333 334 Category::Category() : type_(CATEGORY_UNKNOWN) { 335 } 336 337 // Converts category.scheme into CategoryType enum. 338 bool Category::GetCategoryTypeFromScheme( 339 const base::StringPiece& scheme, Category::CategoryType* result) { 340 for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) { 341 if (scheme == kCategoryTypeMap[i].scheme) { 342 *result = kCategoryTypeMap[i].type; 343 return true; 344 } 345 } 346 DVLOG(1) << "Unknown feed link type for scheme " << scheme; 347 return false; 348 } 349 350 // static 351 void Category::RegisterJSONConverter( 352 base::JSONValueConverter<Category>* converter) { 353 converter->RegisterStringField(kLabelField, &Category::label_); 354 converter->RegisterCustomField<Category::CategoryType>( 355 kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme); 356 converter->RegisterStringField(kTermField, &Category::term_); 357 } 358 359 const Link* CommonMetadata::GetLinkByType(Link::LinkType type) const { 360 for (size_t i = 0; i < links_.size(); ++i) { 361 if (links_[i]->type() == type) 362 return links_[i]; 363 } 364 return NULL; 365 } 366 367 //////////////////////////////////////////////////////////////////////////////// 368 // Content implementation 369 370 Content::Content() { 371 } 372 373 // static 374 void Content::RegisterJSONConverter( 375 base::JSONValueConverter<Content>* converter) { 376 converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString); 377 converter->RegisterStringField(kTypeField, &Content::mime_type_); 378 } 379 380 //////////////////////////////////////////////////////////////////////////////// 381 // AppIcon implementation 382 383 AppIcon::AppIcon() : category_(AppIcon::ICON_UNKNOWN), icon_side_length_(0) { 384 } 385 386 AppIcon::~AppIcon() { 387 } 388 389 // static 390 void AppIcon::RegisterJSONConverter( 391 base::JSONValueConverter<AppIcon>* converter) { 392 converter->RegisterCustomField<AppIcon::IconCategory>( 393 kInstalledAppIconCategoryField, 394 &AppIcon::category_, 395 &AppIcon::GetIconCategory); 396 converter->RegisterCustomField<int>(kInstalledAppIconSizeField, 397 &AppIcon::icon_side_length_, 398 base::StringToInt); 399 converter->RegisterRepeatedMessage(kLinkField, &AppIcon::links_); 400 } 401 402 GURL AppIcon::GetIconURL() const { 403 for (size_t i = 0; i < links_.size(); ++i) { 404 if (links_[i]->type() == Link::LINK_ICON) 405 return links_[i]->href(); 406 } 407 return GURL(); 408 } 409 410 // static 411 bool AppIcon::GetIconCategory(const base::StringPiece& category, 412 AppIcon::IconCategory* result) { 413 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) { 414 if (category == kAppIconCategoryMap[i].category_name) { 415 *result = kAppIconCategoryMap[i].category; 416 return true; 417 } 418 } 419 DVLOG(1) << "Unknown icon category " << category; 420 return false; 421 } 422 423 //////////////////////////////////////////////////////////////////////////////// 424 // CommonMetadata implementation 425 426 CommonMetadata::CommonMetadata() { 427 } 428 429 CommonMetadata::~CommonMetadata() { 430 } 431 432 // static 433 template<typename CommonMetadataDescendant> 434 void CommonMetadata::RegisterJSONConverter( 435 base::JSONValueConverter<CommonMetadataDescendant>* converter) { 436 converter->RegisterStringField(kETagField, &CommonMetadata::etag_); 437 converter->template RegisterRepeatedMessage<Author>( 438 kAuthorField, &CommonMetadata::authors_); 439 converter->template RegisterRepeatedMessage<Link>( 440 kLinkField, &CommonMetadata::links_); 441 converter->template RegisterRepeatedMessage<Category>( 442 kCategoryField, &CommonMetadata::categories_); 443 converter->template RegisterCustomField<base::Time>( 444 kUpdatedField, &CommonMetadata::updated_time_, &util::GetTimeFromString); 445 } 446 447 //////////////////////////////////////////////////////////////////////////////// 448 // ResourceEntry implementation 449 450 ResourceEntry::ResourceEntry() 451 : kind_(ENTRY_KIND_UNKNOWN), 452 file_size_(0), 453 deleted_(false), 454 removed_(false), 455 changestamp_(0), 456 image_width_(-1), 457 image_height_(-1), 458 image_rotation_(-1) { 459 } 460 461 ResourceEntry::~ResourceEntry() { 462 } 463 464 bool ResourceEntry::HasFieldPresent(const base::Value* value, 465 bool* result) { 466 *result = (value != NULL); 467 return true; 468 } 469 470 bool ResourceEntry::ParseChangestamp(const base::Value* value, 471 int64* result) { 472 DCHECK(result); 473 if (!value) { 474 *result = 0; 475 return true; 476 } 477 478 std::string string_value; 479 if (value->GetAsString(&string_value) && 480 base::StringToInt64(string_value, result)) 481 return true; 482 483 return false; 484 } 485 486 // static 487 void ResourceEntry::RegisterJSONConverter( 488 base::JSONValueConverter<ResourceEntry>* converter) { 489 // Inherit the parent registrations. 490 CommonMetadata::RegisterJSONConverter(converter); 491 converter->RegisterStringField( 492 kResourceIdField, &ResourceEntry::resource_id_); 493 converter->RegisterStringField(kIDField, &ResourceEntry::id_); 494 converter->RegisterStringField(kTitleTField, &ResourceEntry::title_); 495 converter->RegisterCustomField<base::Time>( 496 kPublishedField, &ResourceEntry::published_time_, 497 &util::GetTimeFromString); 498 converter->RegisterCustomField<base::Time>( 499 kLastViewedField, &ResourceEntry::last_viewed_time_, 500 &util::GetTimeFromString); 501 converter->RegisterRepeatedMessage( 502 kFeedLinkField, &ResourceEntry::resource_links_); 503 converter->RegisterNestedField(kContentField, &ResourceEntry::content_); 504 505 // File properties. If the resource type is not a normal file, then 506 // that's no problem because those feed must not have these fields 507 // themselves, which does not report errors. 508 converter->RegisterStringField(kFileNameField, &ResourceEntry::filename_); 509 converter->RegisterStringField(kMD5Field, &ResourceEntry::file_md5_); 510 converter->RegisterCustomField<int64>( 511 kSizeField, &ResourceEntry::file_size_, &base::StringToInt64); 512 converter->RegisterStringField( 513 kSuggestedFileNameField, &ResourceEntry::suggested_filename_); 514 // Deleted are treated as 'trashed' items on web client side. Removed files 515 // are gone for good. We treat both cases as 'deleted' for this client. 516 converter->RegisterCustomValueField<bool>( 517 kDeletedField, &ResourceEntry::deleted_, &ResourceEntry::HasFieldPresent); 518 converter->RegisterCustomValueField<bool>( 519 kRemovedField, &ResourceEntry::removed_, &ResourceEntry::HasFieldPresent); 520 converter->RegisterCustomValueField<int64>( 521 kChangestampField, &ResourceEntry::changestamp_, 522 &ResourceEntry::ParseChangestamp); 523 // ImageMediaMetadata fields are not supported by WAPI. 524 } 525 526 std::string ResourceEntry::GetHostedDocumentExtension() const { 527 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { 528 if (kEntryKindMap[i].kind == kind_) { 529 if (kEntryKindMap[i].extension) 530 return std::string(kEntryKindMap[i].extension); 531 else 532 return std::string(); 533 } 534 } 535 return std::string(); 536 } 537 538 // static 539 int ResourceEntry::ClassifyEntryKindByFileExtension( 540 const base::FilePath& file_path) { 541 #if defined(OS_WIN) 542 std::string file_extension = WideToUTF8(file_path.Extension()); 543 #else 544 std::string file_extension = file_path.Extension(); 545 #endif 546 for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) { 547 const char* document_extension = kEntryKindMap[i].extension; 548 if (document_extension && file_extension == document_extension) 549 return ClassifyEntryKind(kEntryKindMap[i].kind); 550 } 551 return 0; 552 } 553 554 // static 555 DriveEntryKind ResourceEntry::GetEntryKindFromTerm( 556 const std::string& term) { 557 if (!StartsWithASCII(term, kTermPrefix, false)) { 558 DVLOG(1) << "Unexpected term prefix term " << term; 559 return ENTRY_KIND_UNKNOWN; 560 } 561 562 std::string type = term.substr(strlen(kTermPrefix)); 563 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { 564 if (type == kEntryKindMap[i].entry) 565 return kEntryKindMap[i].kind; 566 } 567 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; 568 return ENTRY_KIND_UNKNOWN; 569 } 570 571 // static 572 int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind) { 573 int classes = 0; 574 575 // All DriveEntryKind members are listed here, so the compiler catches if a 576 // newly added member is missing here. 577 switch (kind) { 578 case ENTRY_KIND_UNKNOWN: 579 // Special entries. 580 case ENTRY_KIND_ITEM: 581 case ENTRY_KIND_SITE: 582 break; 583 584 // Hosted Google document. 585 case ENTRY_KIND_DOCUMENT: 586 case ENTRY_KIND_SPREADSHEET: 587 case ENTRY_KIND_PRESENTATION: 588 case ENTRY_KIND_DRAWING: 589 case ENTRY_KIND_TABLE: 590 case ENTRY_KIND_FORM: 591 classes = KIND_OF_GOOGLE_DOCUMENT | KIND_OF_HOSTED_DOCUMENT; 592 break; 593 594 // Hosted external application document. 595 case ENTRY_KIND_EXTERNAL_APP: 596 classes = KIND_OF_EXTERNAL_DOCUMENT | KIND_OF_HOSTED_DOCUMENT; 597 break; 598 599 // Folders, collections. 600 case ENTRY_KIND_FOLDER: 601 classes = KIND_OF_FOLDER; 602 break; 603 604 // Regular files. 605 case ENTRY_KIND_FILE: 606 case ENTRY_KIND_PDF: 607 classes = KIND_OF_FILE; 608 break; 609 610 case ENTRY_KIND_MAX_VALUE: 611 NOTREACHED(); 612 } 613 614 return classes; 615 } 616 617 void ResourceEntry::FillRemainingFields() { 618 // Set |kind_| and |labels_| based on the |categories_| in the class. 619 // JSONValueConverter does not have the ability to catch an element in a list 620 // based on a predicate. Thus we need to iterate over |categories_| and 621 // find the elements to set these fields as a post-process. 622 for (size_t i = 0; i < categories_.size(); ++i) { 623 const Category* category = categories_[i]; 624 if (category->type() == Category::CATEGORY_KIND) 625 kind_ = GetEntryKindFromTerm(category->term()); 626 else if (category->type() == Category::CATEGORY_LABEL) 627 labels_.push_back(category->label()); 628 } 629 } 630 631 // static 632 scoped_ptr<ResourceEntry> ResourceEntry::ExtractAndParse( 633 const base::Value& value) { 634 const base::DictionaryValue* as_dict = NULL; 635 const base::DictionaryValue* entry_dict = NULL; 636 if (value.GetAsDictionary(&as_dict) && 637 as_dict->GetDictionary(kEntryField, &entry_dict)) { 638 return ResourceEntry::CreateFrom(*entry_dict); 639 } 640 return scoped_ptr<ResourceEntry>(); 641 } 642 643 // static 644 scoped_ptr<ResourceEntry> ResourceEntry::CreateFrom(const base::Value& value) { 645 base::JSONValueConverter<ResourceEntry> converter; 646 scoped_ptr<ResourceEntry> entry(new ResourceEntry()); 647 if (!converter.Convert(value, entry.get())) { 648 DVLOG(1) << "Invalid resource entry!"; 649 return scoped_ptr<ResourceEntry>(); 650 } 651 652 entry->FillRemainingFields(); 653 return entry.Pass(); 654 } 655 656 // static 657 std::string ResourceEntry::GetEntryNodeName() { 658 return kEntryNode; 659 } 660 661 //////////////////////////////////////////////////////////////////////////////// 662 // ResourceList implementation 663 664 ResourceList::ResourceList() 665 : start_index_(0), 666 items_per_page_(0), 667 largest_changestamp_(0) { 668 } 669 670 ResourceList::~ResourceList() { 671 } 672 673 // static 674 void ResourceList::RegisterJSONConverter( 675 base::JSONValueConverter<ResourceList>* converter) { 676 // inheritance 677 CommonMetadata::RegisterJSONConverter(converter); 678 // TODO(zelidrag): Once we figure out where these will be used, we should 679 // check for valid start_index_ and items_per_page_ values. 680 converter->RegisterCustomField<int>( 681 kStartIndexField, &ResourceList::start_index_, &base::StringToInt); 682 converter->RegisterCustomField<int>( 683 kItemsPerPageField, &ResourceList::items_per_page_, &base::StringToInt); 684 converter->RegisterStringField(kTitleTField, &ResourceList::title_); 685 converter->RegisterRepeatedMessage(kEntryField, &ResourceList::entries_); 686 converter->RegisterCustomField<int64>( 687 kLargestChangestampField, &ResourceList::largest_changestamp_, 688 &base::StringToInt64); 689 } 690 691 bool ResourceList::Parse(const base::Value& value) { 692 base::JSONValueConverter<ResourceList> converter; 693 if (!converter.Convert(value, this)) { 694 DVLOG(1) << "Invalid resource list!"; 695 return false; 696 } 697 698 ScopedVector<ResourceEntry>::iterator iter = entries_.begin(); 699 while (iter != entries_.end()) { 700 ResourceEntry* entry = (*iter); 701 entry->FillRemainingFields(); 702 ++iter; 703 } 704 return true; 705 } 706 707 // static 708 scoped_ptr<ResourceList> ResourceList::ExtractAndParse( 709 const base::Value& value) { 710 const base::DictionaryValue* as_dict = NULL; 711 const base::DictionaryValue* feed_dict = NULL; 712 if (value.GetAsDictionary(&as_dict) && 713 as_dict->GetDictionary(kFeedField, &feed_dict)) { 714 return ResourceList::CreateFrom(*feed_dict); 715 } 716 return scoped_ptr<ResourceList>(); 717 } 718 719 // static 720 scoped_ptr<ResourceList> ResourceList::CreateFrom(const base::Value& value) { 721 scoped_ptr<ResourceList> feed(new ResourceList()); 722 if (!feed->Parse(value)) { 723 DVLOG(1) << "Invalid resource list!"; 724 return scoped_ptr<ResourceList>(); 725 } 726 727 return feed.Pass(); 728 } 729 730 bool ResourceList::GetNextFeedURL(GURL* url) const { 731 DCHECK(url); 732 for (size_t i = 0; i < links_.size(); ++i) { 733 if (links_[i]->type() == Link::LINK_NEXT) { 734 *url = links_[i]->href(); 735 return true; 736 } 737 } 738 return false; 739 } 740 741 void ResourceList::ReleaseEntries(std::vector<ResourceEntry*>* entries) { 742 entries_.release(entries); 743 } 744 745 //////////////////////////////////////////////////////////////////////////////// 746 // InstalledApp implementation 747 748 InstalledApp::InstalledApp() : supports_create_(false) { 749 } 750 751 InstalledApp::~InstalledApp() { 752 } 753 754 InstalledApp::IconList InstalledApp::GetIconsForCategory( 755 AppIcon::IconCategory category) const { 756 IconList result; 757 758 for (ScopedVector<AppIcon>::const_iterator icon_iter = app_icons_.begin(); 759 icon_iter != app_icons_.end(); ++icon_iter) { 760 if ((*icon_iter)->category() != category) 761 continue; 762 GURL icon_url = (*icon_iter)->GetIconURL(); 763 if (icon_url.is_empty()) 764 continue; 765 result.push_back(std::make_pair((*icon_iter)->icon_side_length(), 766 icon_url)); 767 } 768 769 // Return a sorted list, smallest to largest. 770 std::sort(result.begin(), result.end(), SortBySize); 771 return result; 772 } 773 774 GURL InstalledApp::GetProductUrl() const { 775 for (ScopedVector<Link>::const_iterator it = links_.begin(); 776 it != links_.end(); ++it) { 777 const Link* link = *it; 778 if (link->type() == Link::LINK_PRODUCT) 779 return link->href(); 780 } 781 return GURL(); 782 } 783 784 // static 785 bool InstalledApp::GetValueString(const base::Value* value, 786 std::string* result) { 787 const base::DictionaryValue* dict = NULL; 788 if (!value->GetAsDictionary(&dict)) 789 return false; 790 791 if (!dict->GetString(kTField, result)) 792 return false; 793 794 return true; 795 } 796 797 // static 798 void InstalledApp::RegisterJSONConverter( 799 base::JSONValueConverter<InstalledApp>* converter) { 800 converter->RegisterRepeatedMessage(kInstalledAppIconField, 801 &InstalledApp::app_icons_); 802 converter->RegisterStringField(kInstalledAppIdField, 803 &InstalledApp::app_id_); 804 converter->RegisterStringField(kInstalledAppNameField, 805 &InstalledApp::app_name_); 806 converter->RegisterStringField(kInstalledAppObjectTypeField, 807 &InstalledApp::object_type_); 808 converter->RegisterCustomField<bool>(kInstalledAppSupportsCreateField, 809 &InstalledApp::supports_create_, 810 &GetBoolFromString); 811 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField, 812 &InstalledApp::primary_mimetypes_, 813 &GetValueString); 814 converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField, 815 &InstalledApp::secondary_mimetypes_, 816 &GetValueString); 817 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField, 818 &InstalledApp::primary_extensions_, 819 &GetValueString); 820 converter->RegisterRepeatedCustomValue( 821 kInstalledAppSecondaryFileExtensionField, 822 &InstalledApp::secondary_extensions_, 823 &GetValueString); 824 converter->RegisterRepeatedMessage(kLinkField, &InstalledApp::links_); 825 } 826 827 //////////////////////////////////////////////////////////////////////////////// 828 // AccountMetadata implementation 829 830 AccountMetadata::AccountMetadata() 831 : quota_bytes_total_(0), 832 quota_bytes_used_(0), 833 largest_changestamp_(0) { 834 } 835 836 AccountMetadata::~AccountMetadata() { 837 } 838 839 // static 840 void AccountMetadata::RegisterJSONConverter( 841 base::JSONValueConverter<AccountMetadata>* converter) { 842 converter->RegisterCustomField<int64>( 843 kQuotaBytesTotalField, 844 &AccountMetadata::quota_bytes_total_, 845 &base::StringToInt64); 846 converter->RegisterCustomField<int64>( 847 kQuotaBytesUsedField, 848 &AccountMetadata::quota_bytes_used_, 849 &base::StringToInt64); 850 converter->RegisterCustomField<int64>( 851 kLargestChangestampField, 852 &AccountMetadata::largest_changestamp_, 853 &base::StringToInt64); 854 converter->RegisterRepeatedMessage(kInstalledAppField, 855 &AccountMetadata::installed_apps_); 856 } 857 858 // static 859 scoped_ptr<AccountMetadata> AccountMetadata::CreateFrom( 860 const base::Value& value) { 861 scoped_ptr<AccountMetadata> metadata(new AccountMetadata()); 862 const base::DictionaryValue* dictionary = NULL; 863 const base::Value* entry = NULL; 864 if (!value.GetAsDictionary(&dictionary) || 865 !dictionary->Get(kEntryField, &entry) || 866 !metadata->Parse(*entry)) { 867 LOG(ERROR) << "Unable to create: Invalid account metadata feed!"; 868 return scoped_ptr<AccountMetadata>(); 869 } 870 871 return metadata.Pass(); 872 } 873 874 bool AccountMetadata::Parse(const base::Value& value) { 875 base::JSONValueConverter<AccountMetadata> converter; 876 if (!converter.Convert(value, this)) { 877 LOG(ERROR) << "Unable to parse: Invalid account metadata feed!"; 878 return false; 879 } 880 return true; 881 } 882 883 } // namespace google_apis 884