| OLD | NEW |
| (Empty) |
| 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/chromeos/gdata/gdata_parser.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/file_path.h" | |
| 11 #include "base/json/json_value_converter.h" | |
| 12 #include "base/memory/scoped_ptr.h" | |
| 13 #include "base/string_number_conversions.h" | |
| 14 #include "base/string_piece.h" | |
| 15 #include "base/string_util.h" | |
| 16 #include "base/utf_string_conversions.h" | |
| 17 #include "base/values.h" | |
| 18 #include "third_party/libxml/chromium/libxml_utils.h" | |
| 19 | |
| 20 using base::Value; | |
| 21 using base::DictionaryValue; | |
| 22 using base::ListValue; | |
| 23 | |
| 24 namespace gdata { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // Term values for kSchemeKind category: | |
| 29 const char kSchemeKind[] = "http://schemas.google.com/g/2005#kind"; | |
| 30 const char kTermPrefix[] = "http://schemas.google.com/docs/2007#"; | |
| 31 const char kFileTerm[] = "file"; | |
| 32 const char kFolderTerm[] = "folder"; | |
| 33 const char kItemTerm[] = "item"; | |
| 34 const char kPdfTerm[] = "pdf"; | |
| 35 const char kDocumentTerm[] = "document"; | |
| 36 const char kSpreadSheetTerm[] = "spreadsheet"; | |
| 37 const char kPresentationTerm[] = "presentation"; | |
| 38 | |
| 39 const char kSchemeLabels[] = "http://schemas.google.com/g/2005/labels"; | |
| 40 | |
| 41 // Node names. | |
| 42 const char kAuthorNode[] = "author"; | |
| 43 const char kCategoryNode[] = "category"; | |
| 44 const char kContentNode[] = "content"; | |
| 45 const char kEditedNode[] = "edited"; | |
| 46 const char kEmailNode[] = "email"; | |
| 47 const char kEntryNode[] = "entry"; | |
| 48 const char kFeedLinkNode[] = "feedLink"; | |
| 49 const char kFilenameNode[] = "filename"; | |
| 50 const char kIDNode[] = "id"; | |
| 51 const char kLastModifiedByNode[] = "lastModifiedBy"; | |
| 52 const char kLinkNode[] = "link"; | |
| 53 const char kMd5ChecksumNode[] = "md5Checksum"; | |
| 54 const char kModifiedByMeDateNode[] = "modifiedByMeDate"; | |
| 55 const char kNameNode[] = "name"; | |
| 56 const char kPublishedNode[] = "published"; | |
| 57 const char kQuotaBytesUsedNode[] = "quotaBytesUsed"; | |
| 58 const char kResourceIdNode[] = "resourceId"; | |
| 59 const char kSizeNode[] = "size"; | |
| 60 const char kSuggestedFilenameNode[] = "suggestedFilename"; | |
| 61 const char kTitleNode[] = "title"; | |
| 62 const char kUpdatedNode[] = "updated"; | |
| 63 const char kWritersCanInviteNode[] = "writersCanInvite"; | |
| 64 | |
| 65 // Field names. | |
| 66 const char kAuthorField[] = "author"; | |
| 67 const char kCategoryField[] = "category"; | |
| 68 const char kContentField[] = "content"; | |
| 69 const char kDeletedField[] = "gd$deleted"; | |
| 70 const char kETagField[] = "gd$etag"; | |
| 71 const char kEmailField[] = "email.$t"; | |
| 72 const char kEntryField[] = "entry"; | |
| 73 const char kFeedField[] = "feed"; | |
| 74 const char kFeedLinkField[] = "gd$feedLink"; | |
| 75 const char kFileNameField[] = "docs$filename.$t"; | |
| 76 const char kHrefField[] = "href"; | |
| 77 const char kIDField[] = "id.$t"; | |
| 78 const char kInstalledAppField[] = "docs$installedApp"; | |
| 79 const char kInstalledAppNameField[] = "docs$installedAppName"; | |
| 80 const char kInstalledAppIdField[] = "docs$installedAppId"; | |
| 81 const char kInstalledAppIconField[] = "docs$installedAppIcon"; | |
| 82 const char kInstalledAppIconCategoryField[] = "docs$installedAppIconCategory"; | |
| 83 const char kInstalledAppIconSizeField[] = "docs$installedAppIconSize"; | |
| 84 const char kInstalledAppObjectTypeField[] = "docs$installedAppObjectType"; | |
| 85 const char kInstalledAppPrimaryFileExtensionField[] = | |
| 86 "docs$installedAppPrimaryFileExtension"; | |
| 87 const char kInstalledAppPrimaryMimeTypeField[] = | |
| 88 "docs$installedAppPrimaryMimeType"; | |
| 89 const char kInstalledAppSecondaryFileExtensionField[] = | |
| 90 "docs$installedAppSecondaryFileExtension"; | |
| 91 const char kInstalledAppSecondaryMimeTypeField[] = | |
| 92 "docs$installedAppSecondaryMimeType"; | |
| 93 const char kInstalledAppSupportsCreateField[] = | |
| 94 "docs$installedAppSupportsCreate"; | |
| 95 const char kItemsPerPageField[] = "openSearch$itemsPerPage.$t"; | |
| 96 const char kLabelField[] = "label"; | |
| 97 const char kLargestChangestampField[] = "docs$largestChangestamp.value"; | |
| 98 const char kLinkField[] = "link"; | |
| 99 const char kMD5Field[] = "docs$md5Checksum.$t"; | |
| 100 const char kNameField[] = "name.$t"; | |
| 101 const char kPublishedField[] = "published.$t"; | |
| 102 const char kQuotaBytesTotalField[] = "gd$quotaBytesTotal.$t"; | |
| 103 const char kQuotaBytesUsedField[] = "gd$quotaBytesUsed.$t"; | |
| 104 const char kRelField[] = "rel"; | |
| 105 const char kRemovedField[] = "gd$removed"; | |
| 106 const char kResourceIdField[] = "gd$resourceId.$t"; | |
| 107 const char kSchemeField[] = "scheme"; | |
| 108 const char kSizeField[] = "docs$size.$t"; | |
| 109 const char kSrcField[] = "src"; | |
| 110 const char kStartIndexField[] = "openSearch$startIndex.$t"; | |
| 111 const char kSuggestedFileNameField[] = "docs$suggestedFilename.$t"; | |
| 112 const char kTField[] = "$t"; | |
| 113 const char kTermField[] = "term"; | |
| 114 const char kTitleField[] = "title"; | |
| 115 const char kTitleTField[] = "title.$t"; | |
| 116 const char kTypeField[] = "type"; | |
| 117 const char kUpdatedField[] = "updated.$t"; | |
| 118 | |
| 119 // Attribute names. | |
| 120 // Attributes are not namespace-blind as node names in XmlReader. | |
| 121 const char kETagAttr[] = "gd:etag"; | |
| 122 const char kEmailAttr[] = "email"; | |
| 123 const char kHrefAttr[] = "href"; | |
| 124 const char kLabelAttr[] = "label"; | |
| 125 const char kNameAttr[] = "name"; | |
| 126 const char kRelAttr[] = "rel"; | |
| 127 const char kSchemeAttr[] = "scheme"; | |
| 128 const char kSrcAttr[] = "src"; | |
| 129 const char kTermAttr[] = "term"; | |
| 130 const char kTypeAttr[] = "type"; | |
| 131 const char kValueAttr[] = "value"; | |
| 132 | |
| 133 // Link Prefixes | |
| 134 const char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-"; | |
| 135 const size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1; | |
| 136 | |
| 137 struct EntryKindMap { | |
| 138 DocumentEntry::EntryKind kind; | |
| 139 const char* entry; | |
| 140 const char* extension; | |
| 141 }; | |
| 142 | |
| 143 const EntryKindMap kEntryKindMap[] = { | |
| 144 { DocumentEntry::ITEM, "item", NULL}, | |
| 145 { DocumentEntry::DOCUMENT, "document", ".gdoc"}, | |
| 146 { DocumentEntry::SPREADSHEET, "spreadsheet", ".gsheet"}, | |
| 147 { DocumentEntry::PRESENTATION, "presentation", ".gslides" }, | |
| 148 { DocumentEntry::DRAWING, "drawing", ".gdraw"}, | |
| 149 { DocumentEntry::TABLE, "table", ".gtable"}, | |
| 150 { DocumentEntry::EXTERNAL_APP, "externalapp", ".glink"}, | |
| 151 { DocumentEntry::SITE, "site", NULL}, | |
| 152 { DocumentEntry::FOLDER, "folder", NULL}, | |
| 153 { DocumentEntry::FILE, "file", NULL}, | |
| 154 { DocumentEntry::PDF, "pdf", NULL}, | |
| 155 }; | |
| 156 | |
| 157 struct LinkTypeMap { | |
| 158 Link::LinkType type; | |
| 159 const char* rel; | |
| 160 }; | |
| 161 | |
| 162 const LinkTypeMap kLinkTypeMap[] = { | |
| 163 { Link::SELF, | |
| 164 "self" }, | |
| 165 { Link::NEXT, | |
| 166 "next" }, | |
| 167 { Link::PARENT, | |
| 168 "http://schemas.google.com/docs/2007#parent" }, | |
| 169 { Link::ALTERNATE, | |
| 170 "alternate"}, | |
| 171 { Link::EDIT, | |
| 172 "edit" }, | |
| 173 { Link::EDIT_MEDIA, | |
| 174 "edit-media" }, | |
| 175 { Link::ALT_EDIT_MEDIA, | |
| 176 "http://schemas.google.com/docs/2007#alt-edit-media" }, | |
| 177 { Link::ALT_POST, | |
| 178 "http://schemas.google.com/docs/2007#alt-post" }, | |
| 179 { Link::FEED, | |
| 180 "http://schemas.google.com/g/2005#feed"}, | |
| 181 { Link::POST, | |
| 182 "http://schemas.google.com/g/2005#post"}, | |
| 183 { Link::BATCH, | |
| 184 "http://schemas.google.com/g/2005#batch"}, | |
| 185 { Link::THUMBNAIL, | |
| 186 "http://schemas.google.com/docs/2007/thumbnail"}, | |
| 187 { Link::RESUMABLE_EDIT_MEDIA, | |
| 188 "http://schemas.google.com/g/2005#resumable-edit-media"}, | |
| 189 { Link::RESUMABLE_CREATE_MEDIA, | |
| 190 "http://schemas.google.com/g/2005#resumable-create-media"}, | |
| 191 { Link::TABLES_FEED, | |
| 192 "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, | |
| 193 { Link::WORKSHEET_FEED, | |
| 194 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"}, | |
| 195 { Link::EMBED, | |
| 196 "http://schemas.google.com/docs/2007#embed"}, | |
| 197 { Link::PRODUCT, | |
| 198 "http://schemas.google.com/docs/2007#product"}, | |
| 199 { Link::ICON, | |
| 200 "http://schemas.google.com/docs/2007#icon"}, | |
| 201 }; | |
| 202 | |
| 203 struct FeedLinkTypeMap { | |
| 204 FeedLink::FeedLinkType type; | |
| 205 const char* rel; | |
| 206 }; | |
| 207 | |
| 208 const FeedLinkTypeMap kFeedLinkTypeMap[] = { | |
| 209 { FeedLink::ACL, "http://schemas.google.com/acl/2007#accessControlList" }, | |
| 210 { FeedLink::REVISIONS, "http://schemas.google.com/docs/2007/revisions" }, | |
| 211 }; | |
| 212 | |
| 213 struct CategoryTypeMap { | |
| 214 Category::CategoryType type; | |
| 215 const char* scheme; | |
| 216 }; | |
| 217 | |
| 218 const CategoryTypeMap kCategoryTypeMap[] = { | |
| 219 { Category::KIND, "http://schemas.google.com/g/2005#kind" }, | |
| 220 { Category::LABEL, "http://schemas.google.com/g/2005/labels" }, | |
| 221 }; | |
| 222 | |
| 223 struct AppIconCategoryMap { | |
| 224 AppIcon::IconCategory category; | |
| 225 const char* category_name; | |
| 226 }; | |
| 227 | |
| 228 const AppIconCategoryMap kAppIconCategoryMap[] = { | |
| 229 { AppIcon::DOCUMENT, "document" }, | |
| 230 { AppIcon::APPLICATION, "application" }, | |
| 231 { AppIcon::SHARED_DOCUMENT, "documentShared" }, | |
| 232 }; | |
| 233 | |
| 234 // Converts |url_string| to |result|. Always returns true to be used | |
| 235 // for JSONValueConverter::RegisterCustomField method. | |
| 236 // TODO(mukai): make it return false in case of invalid |url_string|. | |
| 237 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) { | |
| 238 *result = GURL(url_string.as_string()); | |
| 239 return true; | |
| 240 } | |
| 241 | |
| 242 // Converts boolean string values like "true" into bool. | |
| 243 bool GetBoolFromString(const base::StringPiece& value, bool* result) { | |
| 244 *result = (value == "true"); | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 bool SortBySize(const InstalledApp::IconList::value_type& a, | |
| 249 const InstalledApp::IconList::value_type& b) { | |
| 250 return a.first < b.first; | |
| 251 } | |
| 252 | |
| 253 } // namespace | |
| 254 | |
| 255 //////////////////////////////////////////////////////////////////////////////// | |
| 256 // Author implementation | |
| 257 | |
| 258 Author::Author() { | |
| 259 } | |
| 260 | |
| 261 // static | |
| 262 void Author::RegisterJSONConverter( | |
| 263 base::JSONValueConverter<Author>* converter) { | |
| 264 converter->RegisterStringField(kNameField, &Author::name_); | |
| 265 converter->RegisterStringField(kEmailField, &Author::email_); | |
| 266 } | |
| 267 | |
| 268 Author* Author::CreateFromXml(XmlReader* xml_reader) { | |
| 269 if (xml_reader->NodeName() != kAuthorNode) | |
| 270 return NULL; | |
| 271 | |
| 272 if (!xml_reader->Read()) | |
| 273 return NULL; | |
| 274 | |
| 275 const int depth = xml_reader->Depth(); | |
| 276 Author* author = new Author(); | |
| 277 bool skip_read = false; | |
| 278 do { | |
| 279 skip_read = false; | |
| 280 DVLOG(1) << "Parsing author node " << xml_reader->NodeName() | |
| 281 << ", depth = " << depth; | |
| 282 if (xml_reader->NodeName() == kNameNode) { | |
| 283 std::string name; | |
| 284 if (xml_reader->ReadElementContent(&name)) | |
| 285 author->name_ = UTF8ToUTF16(name); | |
| 286 skip_read = true; | |
| 287 } else if (xml_reader->NodeName() == kEmailNode) { | |
| 288 xml_reader->ReadElementContent(&author->email_); | |
| 289 skip_read = true; | |
| 290 } | |
| 291 } while (depth == xml_reader->Depth() && (skip_read || xml_reader->Next())); | |
| 292 return author; | |
| 293 } | |
| 294 | |
| 295 //////////////////////////////////////////////////////////////////////////////// | |
| 296 // Link implementation | |
| 297 | |
| 298 Link::Link() : type_(Link::UNKNOWN) { | |
| 299 } | |
| 300 | |
| 301 Link::~Link() { | |
| 302 } | |
| 303 | |
| 304 // static | |
| 305 bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) { | |
| 306 DCHECK(app_id); | |
| 307 // Fast return path if the link clearly isn't an OPEN_WITH link. | |
| 308 if (rel.size() < kOpenWithPrefixSize) { | |
| 309 app_id->clear(); | |
| 310 return true; | |
| 311 } | |
| 312 | |
| 313 const std::string kOpenWithPrefixStr(kOpenWithPrefix); | |
| 314 if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) { | |
| 315 *app_id = rel.as_string().substr(kOpenWithPrefixStr.size()); | |
| 316 return true; | |
| 317 } | |
| 318 | |
| 319 app_id->clear(); | |
| 320 return true; | |
| 321 } | |
| 322 | |
| 323 // static. | |
| 324 bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) { | |
| 325 DCHECK(type); | |
| 326 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) { | |
| 327 if (rel == kLinkTypeMap[i].rel) { | |
| 328 *type = kLinkTypeMap[i].type; | |
| 329 return true; | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 // OPEN_WITH links have extra information at the end of the rel that is unique | |
| 334 // for each one, so we can't just check the usual map. This check is slightly | |
| 335 // redundant to provide a quick skip if it's obviously not an OPEN_WITH url. | |
| 336 if (rel.size() >= kOpenWithPrefixSize && | |
| 337 StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) { | |
| 338 *type = OPEN_WITH; | |
| 339 return true; | |
| 340 } | |
| 341 | |
| 342 // Let unknown link types through, just report it; if the link type is needed | |
| 343 // in the future, add it into LinkType and kLinkTypeMap. | |
| 344 DVLOG(1) << "Ignoring unknown link type for rel " << rel; | |
| 345 *type = UNKNOWN; | |
| 346 return true; | |
| 347 } | |
| 348 | |
| 349 // static | |
| 350 void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) { | |
| 351 converter->RegisterCustomField<Link::LinkType>(kRelField, | |
| 352 &Link::type_, | |
| 353 &Link::GetLinkType); | |
| 354 // We have to register kRelField twice because we extract two different pieces | |
| 355 // of data from the same rel field. | |
| 356 converter->RegisterCustomField<std::string>(kRelField, | |
| 357 &Link::app_id_, | |
| 358 &Link::GetAppID); | |
| 359 converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString); | |
| 360 converter->RegisterStringField(kTitleField, &Link::title_); | |
| 361 converter->RegisterStringField(kTypeField, &Link::mime_type_); | |
| 362 } | |
| 363 | |
| 364 // static. | |
| 365 Link* Link::CreateFromXml(XmlReader* xml_reader) { | |
| 366 if (xml_reader->NodeName() != kLinkNode) | |
| 367 return NULL; | |
| 368 | |
| 369 Link* link = new Link(); | |
| 370 xml_reader->NodeAttribute(kTypeAttr, &link->mime_type_); | |
| 371 | |
| 372 std::string href; | |
| 373 if (xml_reader->NodeAttribute(kHrefAttr, &href)) | |
| 374 link->href_ = GURL(href); | |
| 375 | |
| 376 std::string rel; | |
| 377 if (xml_reader->NodeAttribute(kRelAttr, &rel)) { | |
| 378 GetLinkType(rel, &link->type_); | |
| 379 if (link->type_ == OPEN_WITH) | |
| 380 GetAppID(rel, &link->app_id_); | |
| 381 } | |
| 382 | |
| 383 return link; | |
| 384 } | |
| 385 | |
| 386 //////////////////////////////////////////////////////////////////////////////// | |
| 387 // FeedLink implementation | |
| 388 | |
| 389 FeedLink::FeedLink() : type_(FeedLink::UNKNOWN) { | |
| 390 } | |
| 391 | |
| 392 // static. | |
| 393 bool FeedLink::GetFeedLinkType( | |
| 394 const base::StringPiece& rel, FeedLink::FeedLinkType* result) { | |
| 395 for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) { | |
| 396 if (rel == kFeedLinkTypeMap[i].rel) { | |
| 397 *result = kFeedLinkTypeMap[i].type; | |
| 398 return true; | |
| 399 } | |
| 400 } | |
| 401 DVLOG(1) << "Unknown feed link type for rel " << rel; | |
| 402 return false; | |
| 403 } | |
| 404 | |
| 405 // static | |
| 406 void FeedLink::RegisterJSONConverter( | |
| 407 base::JSONValueConverter<FeedLink>* converter) { | |
| 408 converter->RegisterCustomField<FeedLink::FeedLinkType>( | |
| 409 kRelField, &FeedLink::type_, &FeedLink::GetFeedLinkType); | |
| 410 converter->RegisterCustomField( | |
| 411 kHrefField, &FeedLink::href_, &GetGURLFromString); | |
| 412 } | |
| 413 | |
| 414 // static | |
| 415 FeedLink* FeedLink::CreateFromXml(XmlReader* xml_reader) { | |
| 416 if (xml_reader->NodeName() != kFeedLinkNode) | |
| 417 return NULL; | |
| 418 | |
| 419 FeedLink* link = new FeedLink(); | |
| 420 std::string href; | |
| 421 if (xml_reader->NodeAttribute(kHrefAttr, &href)) | |
| 422 link->href_ = GURL(href); | |
| 423 | |
| 424 std::string rel; | |
| 425 if (xml_reader->NodeAttribute(kRelAttr, &rel)) | |
| 426 GetFeedLinkType(rel, &link->type_); | |
| 427 | |
| 428 return link; | |
| 429 } | |
| 430 | |
| 431 //////////////////////////////////////////////////////////////////////////////// | |
| 432 // Category implementation | |
| 433 | |
| 434 Category::Category() : type_(UNKNOWN) { | |
| 435 } | |
| 436 | |
| 437 // Converts category.scheme into CategoryType enum. | |
| 438 bool Category::GetCategoryTypeFromScheme( | |
| 439 const base::StringPiece& scheme, Category::CategoryType* result) { | |
| 440 for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) { | |
| 441 if (scheme == kCategoryTypeMap[i].scheme) { | |
| 442 *result = kCategoryTypeMap[i].type; | |
| 443 return true; | |
| 444 } | |
| 445 } | |
| 446 DVLOG(1) << "Unknown feed link type for scheme " << scheme; | |
| 447 return false; | |
| 448 } | |
| 449 | |
| 450 // static | |
| 451 void Category::RegisterJSONConverter( | |
| 452 base::JSONValueConverter<Category>* converter) { | |
| 453 converter->RegisterStringField(kLabelField, &Category::label_); | |
| 454 converter->RegisterCustomField<Category::CategoryType>( | |
| 455 kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme); | |
| 456 converter->RegisterStringField(kTermField, &Category::term_); | |
| 457 } | |
| 458 | |
| 459 // static | |
| 460 Category* Category::CreateFromXml(XmlReader* xml_reader) { | |
| 461 if (xml_reader->NodeName() != kCategoryNode) | |
| 462 return NULL; | |
| 463 | |
| 464 Category* category = new Category(); | |
| 465 xml_reader->NodeAttribute(kTermAttr, &category->term_); | |
| 466 | |
| 467 std::string scheme; | |
| 468 if (xml_reader->NodeAttribute(kSchemeAttr, &scheme)) | |
| 469 GetCategoryTypeFromScheme(scheme, &category->type_); | |
| 470 | |
| 471 std::string label; | |
| 472 if (xml_reader->NodeAttribute(kLabelAttr, &label)) | |
| 473 category->label_ = UTF8ToUTF16(label); | |
| 474 | |
| 475 return category; | |
| 476 } | |
| 477 | |
| 478 const Link* FeedEntry::GetLinkByType(Link::LinkType type) const { | |
| 479 for (size_t i = 0; i < links_.size(); ++i) { | |
| 480 if (links_[i]->type() == type) | |
| 481 return links_[i]; | |
| 482 } | |
| 483 return NULL; | |
| 484 } | |
| 485 | |
| 486 //////////////////////////////////////////////////////////////////////////////// | |
| 487 // Content implementation | |
| 488 | |
| 489 Content::Content() { | |
| 490 } | |
| 491 | |
| 492 // static | |
| 493 void Content::RegisterJSONConverter( | |
| 494 base::JSONValueConverter<Content>* converter) { | |
| 495 converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString); | |
| 496 converter->RegisterStringField(kTypeField, &Content::mime_type_); | |
| 497 } | |
| 498 | |
| 499 Content* Content::CreateFromXml(XmlReader* xml_reader) { | |
| 500 if (xml_reader->NodeName() != kContentNode) | |
| 501 return NULL; | |
| 502 | |
| 503 Content* content = new Content(); | |
| 504 std::string src; | |
| 505 if (xml_reader->NodeAttribute(kSrcAttr, &src)) | |
| 506 content->url_ = GURL(src); | |
| 507 | |
| 508 xml_reader->NodeAttribute(kTypeAttr, &content->mime_type_); | |
| 509 return content; | |
| 510 } | |
| 511 | |
| 512 | |
| 513 //////////////////////////////////////////////////////////////////////////////// | |
| 514 // AppIcon implementation | |
| 515 | |
| 516 AppIcon::AppIcon() : category_(AppIcon::UNKNOWN), icon_side_length_(0) { | |
| 517 } | |
| 518 | |
| 519 AppIcon::~AppIcon() { | |
| 520 } | |
| 521 | |
| 522 // static | |
| 523 void AppIcon::RegisterJSONConverter( | |
| 524 base::JSONValueConverter<AppIcon>* converter) { | |
| 525 converter->RegisterCustomField<AppIcon::IconCategory>( | |
| 526 kInstalledAppIconCategoryField, | |
| 527 &AppIcon::category_, | |
| 528 &AppIcon::GetIconCategory); | |
| 529 converter->RegisterCustomField<int>(kInstalledAppIconSizeField, | |
| 530 &AppIcon::icon_side_length_, | |
| 531 base::StringToInt); | |
| 532 converter->RegisterRepeatedMessage(kLinkField, &AppIcon::links_); | |
| 533 } | |
| 534 | |
| 535 GURL AppIcon::GetIconURL() const { | |
| 536 for (size_t i = 0; i < links_.size(); ++i) { | |
| 537 if (links_[i]->type() == Link::ICON) | |
| 538 return links_[i]->href(); | |
| 539 } | |
| 540 return GURL(); | |
| 541 } | |
| 542 | |
| 543 // static | |
| 544 bool AppIcon::GetIconCategory(const base::StringPiece& category, | |
| 545 AppIcon::IconCategory* result) { | |
| 546 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) { | |
| 547 if (category == kAppIconCategoryMap[i].category_name) { | |
| 548 *result = kAppIconCategoryMap[i].category; | |
| 549 return true; | |
| 550 } | |
| 551 } | |
| 552 DVLOG(1) << "Unknown icon category " << category; | |
| 553 return false; | |
| 554 } | |
| 555 | |
| 556 //////////////////////////////////////////////////////////////////////////////// | |
| 557 // FeedEntry implementation | |
| 558 | |
| 559 FeedEntry::FeedEntry() { | |
| 560 } | |
| 561 | |
| 562 FeedEntry::~FeedEntry() { | |
| 563 } | |
| 564 | |
| 565 // static | |
| 566 void FeedEntry::RegisterJSONConverter( | |
| 567 base::JSONValueConverter<FeedEntry>* converter) { | |
| 568 converter->RegisterStringField(kETagField, &FeedEntry::etag_); | |
| 569 converter->RegisterRepeatedMessage(kAuthorField, &FeedEntry::authors_); | |
| 570 converter->RegisterRepeatedMessage(kLinkField, &FeedEntry::links_); | |
| 571 converter->RegisterRepeatedMessage(kCategoryField, &FeedEntry::categories_); | |
| 572 converter->RegisterCustomField<base::Time>( | |
| 573 kUpdatedField, | |
| 574 &FeedEntry::updated_time_, | |
| 575 &FeedEntry::GetTimeFromString); | |
| 576 } | |
| 577 | |
| 578 // static | |
| 579 bool FeedEntry::GetTimeFromString(const base::StringPiece& raw_value, | |
| 580 base::Time* time) { | |
| 581 const char kTimeParsingDelimiters[] = "-:.TZ"; | |
| 582 std::vector<base::StringPiece> parts; | |
| 583 if (Tokenize(raw_value, kTimeParsingDelimiters, &parts) != 7) | |
| 584 return false; | |
| 585 | |
| 586 base::Time::Exploded exploded; | |
| 587 if (!base::StringToInt(parts[0], &exploded.year) || | |
| 588 !base::StringToInt(parts[1], &exploded.month) || | |
| 589 !base::StringToInt(parts[2], &exploded.day_of_month) || | |
| 590 !base::StringToInt(parts[3], &exploded.hour) || | |
| 591 !base::StringToInt(parts[4], &exploded.minute) || | |
| 592 !base::StringToInt(parts[5], &exploded.second) || | |
| 593 !base::StringToInt(parts[6], &exploded.millisecond)) { | |
| 594 return false; | |
| 595 } | |
| 596 | |
| 597 exploded.day_of_week = 0; | |
| 598 if (!exploded.HasValidValues()) | |
| 599 return false; | |
| 600 | |
| 601 *time = base::Time::FromLocalExploded(exploded); | |
| 602 return true; | |
| 603 } | |
| 604 | |
| 605 //////////////////////////////////////////////////////////////////////////////// | |
| 606 // DocumentEntry implementation | |
| 607 | |
| 608 DocumentEntry::DocumentEntry() | |
| 609 : kind_(DocumentEntry::UNKNOWN), | |
| 610 file_size_(0), | |
| 611 deleted_(false), | |
| 612 removed_(false) { | |
| 613 } | |
| 614 | |
| 615 DocumentEntry::~DocumentEntry() { | |
| 616 } | |
| 617 | |
| 618 bool DocumentEntry::HasFieldPresent(const base::Value* value, | |
| 619 bool* result) { | |
| 620 *result = (value != NULL); | |
| 621 return true; | |
| 622 } | |
| 623 | |
| 624 // static | |
| 625 void DocumentEntry::RegisterJSONConverter( | |
| 626 base::JSONValueConverter<DocumentEntry>* converter) { | |
| 627 // inheritant the parent registrations. | |
| 628 FeedEntry::RegisterJSONConverter( | |
| 629 reinterpret_cast<base::JSONValueConverter<FeedEntry>*>(converter)); | |
| 630 converter->RegisterStringField( | |
| 631 kResourceIdField, &DocumentEntry::resource_id_); | |
| 632 converter->RegisterStringField(kIDField, &DocumentEntry::id_); | |
| 633 converter->RegisterStringField(kTitleTField, &DocumentEntry::title_); | |
| 634 converter->RegisterCustomField<base::Time>( | |
| 635 kPublishedField, &DocumentEntry::published_time_, | |
| 636 &FeedEntry::GetTimeFromString); | |
| 637 converter->RegisterRepeatedMessage( | |
| 638 kFeedLinkField, &DocumentEntry::feed_links_); | |
| 639 converter->RegisterNestedField(kContentField, &DocumentEntry::content_); | |
| 640 | |
| 641 // File properties. If the document type is not a normal file, then | |
| 642 // that's no problem because those feed must not have these fields | |
| 643 // themselves, which does not report errors. | |
| 644 converter->RegisterStringField(kFileNameField, &DocumentEntry::filename_); | |
| 645 converter->RegisterStringField(kMD5Field, &DocumentEntry::file_md5_); | |
| 646 converter->RegisterCustomField<int64>( | |
| 647 kSizeField, &DocumentEntry::file_size_, &base::StringToInt64); | |
| 648 converter->RegisterStringField( | |
| 649 kSuggestedFileNameField, &DocumentEntry::suggested_filename_); | |
| 650 // Deleted are treated as 'trashed' items on web client side. Removed files | |
| 651 // are gone for good. We treat both cases as 'deleted' for this client. | |
| 652 converter->RegisterCustomValueField<bool>( | |
| 653 kDeletedField, &DocumentEntry::deleted_, &DocumentEntry::HasFieldPresent); | |
| 654 converter->RegisterCustomValueField<bool>( | |
| 655 kRemovedField, &DocumentEntry::removed_, &DocumentEntry::HasFieldPresent); | |
| 656 } | |
| 657 | |
| 658 std::string DocumentEntry::GetHostedDocumentExtension() const { | |
| 659 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { | |
| 660 if (kEntryKindMap[i].kind == kind_) { | |
| 661 if (kEntryKindMap[i].extension) | |
| 662 return std::string(kEntryKindMap[i].extension); | |
| 663 else | |
| 664 return std::string(); | |
| 665 } | |
| 666 } | |
| 667 return std::string(); | |
| 668 } | |
| 669 | |
| 670 // static | |
| 671 bool DocumentEntry::HasHostedDocumentExtension(const FilePath& file) { | |
| 672 FilePath::StringType file_extension = file.Extension(); | |
| 673 for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) { | |
| 674 const char* document_extension = kEntryKindMap[i].extension; | |
| 675 if (document_extension && file_extension == document_extension) | |
| 676 return true; | |
| 677 } | |
| 678 return false; | |
| 679 } | |
| 680 | |
| 681 // static | |
| 682 std::vector<int> DocumentEntry::GetAllEntryKinds() { | |
| 683 std::vector<int> entry_kinds; | |
| 684 entry_kinds.push_back(UNKNOWN); | |
| 685 for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) | |
| 686 entry_kinds.push_back(kEntryKindMap[i].kind); | |
| 687 return entry_kinds; | |
| 688 } | |
| 689 | |
| 690 // static | |
| 691 DocumentEntry::EntryKind DocumentEntry::GetEntryKindFromTerm( | |
| 692 const std::string& term) { | |
| 693 if (!StartsWithASCII(term, kTermPrefix, false)) { | |
| 694 DVLOG(1) << "Unexpected term prefix term " << term; | |
| 695 return DocumentEntry::UNKNOWN; | |
| 696 } | |
| 697 | |
| 698 std::string type = term.substr(strlen(kTermPrefix)); | |
| 699 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { | |
| 700 if (type == kEntryKindMap[i].entry) | |
| 701 return kEntryKindMap[i].kind; | |
| 702 } | |
| 703 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; | |
| 704 return DocumentEntry::UNKNOWN; | |
| 705 } | |
| 706 | |
| 707 void DocumentEntry::FillRemainingFields() { | |
| 708 // Set |kind_| and |labels_| based on the |categories_| in the class. | |
| 709 // JSONValueConverter does not have the ability to catch an element in a list | |
| 710 // based on a predicate. Thus we need to iterate over |categories_| and | |
| 711 // find the elements to set these fields as a post-process. | |
| 712 for (size_t i = 0; i < categories_.size(); ++i) { | |
| 713 const Category* category = categories_[i]; | |
| 714 if (category->type() == Category::KIND) | |
| 715 kind_ = GetEntryKindFromTerm(category->term()); | |
| 716 else if (category->type() == Category::LABEL) | |
| 717 labels_.push_back(category->label()); | |
| 718 } | |
| 719 } | |
| 720 | |
| 721 // static | |
| 722 DocumentEntry* DocumentEntry::ExtractAndParse( | |
| 723 const base::Value& value) { | |
| 724 const base::DictionaryValue* as_dict = NULL; | |
| 725 base::DictionaryValue* entry_dict = NULL; | |
| 726 if (value.GetAsDictionary(&as_dict) && | |
| 727 as_dict->GetDictionary(kEntryField, &entry_dict)) { | |
| 728 return DocumentEntry::CreateFrom(entry_dict); | |
| 729 } | |
| 730 return NULL; | |
| 731 } | |
| 732 | |
| 733 // static | |
| 734 DocumentEntry* DocumentEntry::CreateFrom(const base::Value* value) { | |
| 735 base::JSONValueConverter<DocumentEntry> converter; | |
| 736 scoped_ptr<DocumentEntry> entry(new DocumentEntry()); | |
| 737 if (!converter.Convert(*value, entry.get())) { | |
| 738 DVLOG(1) << "Invalid document entry!"; | |
| 739 return NULL; | |
| 740 } | |
| 741 | |
| 742 entry->FillRemainingFields(); | |
| 743 return entry.release(); | |
| 744 } | |
| 745 | |
| 746 // static. | |
| 747 DocumentEntry* DocumentEntry::CreateFromXml(XmlReader* xml_reader) { | |
| 748 if (xml_reader->NodeName() != kEntryNode) | |
| 749 return NULL; | |
| 750 | |
| 751 DocumentEntry* entry = new DocumentEntry(); | |
| 752 xml_reader->NodeAttribute(kETagAttr, &entry->etag_); | |
| 753 | |
| 754 if (!xml_reader->Read()) | |
| 755 return entry; | |
| 756 | |
| 757 bool skip_read = false; | |
| 758 do { | |
| 759 DVLOG(1) << "Parsing node " << xml_reader->NodeName(); | |
| 760 skip_read = false; | |
| 761 | |
| 762 if (xml_reader->NodeName() == kAuthorNode) { | |
| 763 scoped_ptr<Author> author(Author::CreateFromXml(xml_reader)); | |
| 764 if (author.get()) | |
| 765 entry->authors_.push_back(author.release()); | |
| 766 } | |
| 767 | |
| 768 if (xml_reader->NodeName() == kContentNode) { | |
| 769 scoped_ptr<Content> content(Content::CreateFromXml(xml_reader)); | |
| 770 if (content.get()) | |
| 771 entry->content_ = *content.get(); | |
| 772 } else if (xml_reader->NodeName() == kLinkNode) { | |
| 773 scoped_ptr<Link> link(Link::CreateFromXml(xml_reader)); | |
| 774 if (link.get()) | |
| 775 entry->links_.push_back(link.release()); | |
| 776 } else if (xml_reader->NodeName() == kFeedLinkNode) { | |
| 777 scoped_ptr<FeedLink> link(FeedLink::CreateFromXml(xml_reader)); | |
| 778 if (link.get()) | |
| 779 entry->feed_links_.push_back(link.release()); | |
| 780 } else if (xml_reader->NodeName() == kCategoryNode) { | |
| 781 scoped_ptr<Category> category(Category::CreateFromXml(xml_reader)); | |
| 782 if (category.get()) | |
| 783 entry->categories_.push_back(category.release()); | |
| 784 } else if (xml_reader->NodeName() == kUpdatedNode) { | |
| 785 std::string time; | |
| 786 if (xml_reader->ReadElementContent(&time)) | |
| 787 GetTimeFromString(time, &entry->updated_time_); | |
| 788 skip_read = true; | |
| 789 } else if (xml_reader->NodeName() == kPublishedNode) { | |
| 790 std::string time; | |
| 791 if (xml_reader->ReadElementContent(&time)) | |
| 792 GetTimeFromString(time, &entry->published_time_); | |
| 793 skip_read = true; | |
| 794 } else if (xml_reader->NodeName() == kIDNode) { | |
| 795 xml_reader->ReadElementContent(&entry->id_); | |
| 796 skip_read = true; | |
| 797 } else if (xml_reader->NodeName() == kResourceIdNode) { | |
| 798 xml_reader->ReadElementContent(&entry->resource_id_); | |
| 799 skip_read = true; | |
| 800 } else if (xml_reader->NodeName() == kTitleNode) { | |
| 801 std::string title; | |
| 802 if (xml_reader->ReadElementContent(&title)) | |
| 803 entry->title_ = UTF8ToUTF16(title); | |
| 804 skip_read = true; | |
| 805 } else if (xml_reader->NodeName() == kFilenameNode) { | |
| 806 std::string file_name; | |
| 807 if (xml_reader->ReadElementContent(&file_name)) | |
| 808 entry->filename_ = UTF8ToUTF16(file_name); | |
| 809 skip_read = true; | |
| 810 } else if (xml_reader->NodeName() == kSuggestedFilenameNode) { | |
| 811 std::string suggested_filename; | |
| 812 if (xml_reader->ReadElementContent(&suggested_filename)) | |
| 813 entry->suggested_filename_ = UTF8ToUTF16(suggested_filename); | |
| 814 skip_read = true; | |
| 815 } else if (xml_reader->NodeName() == kMd5ChecksumNode) { | |
| 816 xml_reader->ReadElementContent(&entry->file_md5_); | |
| 817 skip_read = true; | |
| 818 } else if (xml_reader->NodeName() == kSizeNode) { | |
| 819 std::string size; | |
| 820 if (xml_reader->ReadElementContent(&size)) | |
| 821 base::StringToInt64(size, &entry->file_size_); | |
| 822 skip_read = true; | |
| 823 } else { | |
| 824 DVLOG(1) << "Unknown node " << xml_reader->NodeName(); | |
| 825 } | |
| 826 } while (skip_read || xml_reader->Next()); | |
| 827 | |
| 828 entry->FillRemainingFields(); | |
| 829 return entry; | |
| 830 } | |
| 831 | |
| 832 // static | |
| 833 std::string DocumentEntry::GetEntryNodeName() { | |
| 834 return kEntryNode; | |
| 835 } | |
| 836 | |
| 837 //////////////////////////////////////////////////////////////////////////////// | |
| 838 // DocumentFeed implementation | |
| 839 | |
| 840 DocumentFeed::DocumentFeed() | |
| 841 : start_index_(0), | |
| 842 items_per_page_(0), | |
| 843 largest_changestamp_(0) { | |
| 844 } | |
| 845 | |
| 846 DocumentFeed::~DocumentFeed() { | |
| 847 } | |
| 848 | |
| 849 // static | |
| 850 void DocumentFeed::RegisterJSONConverter( | |
| 851 base::JSONValueConverter<DocumentFeed>* converter) { | |
| 852 // inheritance | |
| 853 FeedEntry::RegisterJSONConverter( | |
| 854 reinterpret_cast<base::JSONValueConverter<FeedEntry>*>(converter)); | |
| 855 // TODO(zelidrag): Once we figure out where these will be used, we should | |
| 856 // check for valid start_index_ and items_per_page_ values. | |
| 857 converter->RegisterCustomField<int>( | |
| 858 kStartIndexField, &DocumentFeed::start_index_, &base::StringToInt); | |
| 859 converter->RegisterCustomField<int>( | |
| 860 kItemsPerPageField, &DocumentFeed::items_per_page_, &base::StringToInt); | |
| 861 converter->RegisterStringField(kTitleTField, &DocumentFeed::title_); | |
| 862 converter->RegisterRepeatedMessage(kEntryField, &DocumentFeed::entries_); | |
| 863 converter->RegisterCustomField<int>( | |
| 864 kLargestChangestampField, &DocumentFeed::largest_changestamp_, | |
| 865 &base::StringToInt); | |
| 866 } | |
| 867 | |
| 868 bool DocumentFeed::Parse(const base::Value& value) { | |
| 869 base::JSONValueConverter<DocumentFeed> converter; | |
| 870 if (!converter.Convert(value, this)) { | |
| 871 DVLOG(1) << "Invalid document feed!"; | |
| 872 return false; | |
| 873 } | |
| 874 | |
| 875 ScopedVector<DocumentEntry>::iterator iter = entries_.begin(); | |
| 876 while (iter != entries_.end()) { | |
| 877 DocumentEntry* entry = (*iter); | |
| 878 entry->FillRemainingFields(); | |
| 879 ++iter; | |
| 880 } | |
| 881 return true; | |
| 882 } | |
| 883 | |
| 884 // static | |
| 885 scoped_ptr<DocumentFeed> DocumentFeed::ExtractAndParse( | |
| 886 const base::Value& value) { | |
| 887 const base::DictionaryValue* as_dict = NULL; | |
| 888 base::DictionaryValue* feed_dict = NULL; | |
| 889 if (value.GetAsDictionary(&as_dict) && | |
| 890 as_dict->GetDictionary(kFeedField, &feed_dict)) { | |
| 891 return DocumentFeed::CreateFrom(*feed_dict); | |
| 892 } | |
| 893 return scoped_ptr<DocumentFeed>(NULL); | |
| 894 } | |
| 895 | |
| 896 // static | |
| 897 scoped_ptr<DocumentFeed> DocumentFeed::CreateFrom(const base::Value& value) { | |
| 898 scoped_ptr<DocumentFeed> feed(new DocumentFeed()); | |
| 899 if (!feed->Parse(value)) { | |
| 900 DVLOG(1) << "Invalid document feed!"; | |
| 901 return scoped_ptr<DocumentFeed>(NULL); | |
| 902 } | |
| 903 | |
| 904 return feed.Pass(); | |
| 905 } | |
| 906 | |
| 907 bool DocumentFeed::GetNextFeedURL(GURL* url) { | |
| 908 DCHECK(url); | |
| 909 for (size_t i = 0; i < links_.size(); ++i) { | |
| 910 if (links_[i]->type() == Link::NEXT) { | |
| 911 *url = links_[i]->href(); | |
| 912 return true; | |
| 913 } | |
| 914 } | |
| 915 return false; | |
| 916 } | |
| 917 | |
| 918 //////////////////////////////////////////////////////////////////////////////// | |
| 919 // InstalledApp implementation | |
| 920 | |
| 921 InstalledApp::InstalledApp() : supports_create_(false) { | |
| 922 } | |
| 923 | |
| 924 InstalledApp::~InstalledApp() { | |
| 925 } | |
| 926 | |
| 927 InstalledApp::IconList InstalledApp::GetIconsForCategory( | |
| 928 AppIcon::IconCategory category) const { | |
| 929 IconList result; | |
| 930 | |
| 931 for (ScopedVector<AppIcon>::const_iterator icon_iter = app_icons_.begin(); | |
| 932 icon_iter != app_icons_.end(); ++icon_iter) { | |
| 933 if ((*icon_iter)->category() != category) | |
| 934 continue; | |
| 935 GURL icon_url = (*icon_iter)->GetIconURL(); | |
| 936 if (icon_url.is_empty()) | |
| 937 continue; | |
| 938 result.push_back(std::make_pair((*icon_iter)->icon_side_length(), | |
| 939 icon_url)); | |
| 940 } | |
| 941 | |
| 942 // Return a sorted list, smallest to largest. | |
| 943 std::sort(result.begin(), result.end(), SortBySize); | |
| 944 return result; | |
| 945 } | |
| 946 | |
| 947 GURL InstalledApp::GetProductUrl() const { | |
| 948 for (ScopedVector<Link>::const_iterator it = links_.begin(); | |
| 949 it != links_.end(); ++it) { | |
| 950 const Link* link = *it; | |
| 951 if (link->type() == Link::PRODUCT) | |
| 952 return link->href(); | |
| 953 } | |
| 954 return GURL(); | |
| 955 } | |
| 956 | |
| 957 // static | |
| 958 bool InstalledApp::GetValueString(const base::Value* value, | |
| 959 std::string* result) { | |
| 960 const base::DictionaryValue* dict = NULL; | |
| 961 if (!value->GetAsDictionary(&dict)) | |
| 962 return false; | |
| 963 | |
| 964 if (!dict->GetString(kTField, result)) | |
| 965 return false; | |
| 966 | |
| 967 return true; | |
| 968 } | |
| 969 | |
| 970 // static | |
| 971 void InstalledApp::RegisterJSONConverter( | |
| 972 base::JSONValueConverter<InstalledApp>* converter) { | |
| 973 converter->RegisterRepeatedMessage(kInstalledAppIconField, | |
| 974 &InstalledApp::app_icons_); | |
| 975 converter->RegisterStringField(kInstalledAppIdField, | |
| 976 &InstalledApp::app_id_); | |
| 977 converter->RegisterStringField(kInstalledAppNameField, | |
| 978 &InstalledApp::app_name_); | |
| 979 converter->RegisterStringField(kInstalledAppObjectTypeField, | |
| 980 &InstalledApp::object_type_); | |
| 981 converter->RegisterCustomField<bool>(kInstalledAppSupportsCreateField, | |
| 982 &InstalledApp::supports_create_, | |
| 983 &GetBoolFromString); | |
| 984 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField, | |
| 985 &InstalledApp::primary_mimetypes_, | |
| 986 &GetValueString); | |
| 987 converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField, | |
| 988 &InstalledApp::secondary_mimetypes_, | |
| 989 &GetValueString); | |
| 990 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField, | |
| 991 &InstalledApp::primary_extensions_, | |
| 992 &GetValueString); | |
| 993 converter->RegisterRepeatedCustomValue( | |
| 994 kInstalledAppSecondaryFileExtensionField, | |
| 995 &InstalledApp::secondary_extensions_, | |
| 996 &GetValueString); | |
| 997 converter->RegisterRepeatedMessage(kLinkField, &InstalledApp::links_); | |
| 998 } | |
| 999 | |
| 1000 //////////////////////////////////////////////////////////////////////////////// | |
| 1001 // AccountMetadataFeed implementation | |
| 1002 | |
| 1003 AccountMetadataFeed::AccountMetadataFeed() | |
| 1004 : quota_bytes_total_(0), | |
| 1005 quota_bytes_used_(0), | |
| 1006 largest_changestamp_(0) { | |
| 1007 } | |
| 1008 | |
| 1009 AccountMetadataFeed::~AccountMetadataFeed() { | |
| 1010 } | |
| 1011 | |
| 1012 // static | |
| 1013 void AccountMetadataFeed::RegisterJSONConverter( | |
| 1014 base::JSONValueConverter<AccountMetadataFeed>* converter) { | |
| 1015 converter->RegisterCustomField<int64>( | |
| 1016 kQuotaBytesTotalField, | |
| 1017 &AccountMetadataFeed::quota_bytes_total_, | |
| 1018 &base::StringToInt64); | |
| 1019 converter->RegisterCustomField<int64>( | |
| 1020 kQuotaBytesUsedField, | |
| 1021 &AccountMetadataFeed::quota_bytes_used_, | |
| 1022 &base::StringToInt64); | |
| 1023 converter->RegisterCustomField<int>( | |
| 1024 kLargestChangestampField, | |
| 1025 &AccountMetadataFeed::largest_changestamp_, | |
| 1026 &base::StringToInt); | |
| 1027 converter->RegisterRepeatedMessage(kInstalledAppField, | |
| 1028 &AccountMetadataFeed::installed_apps_); | |
| 1029 } | |
| 1030 | |
| 1031 // static | |
| 1032 scoped_ptr<AccountMetadataFeed> AccountMetadataFeed::CreateFrom( | |
| 1033 const base::Value& value) { | |
| 1034 scoped_ptr<AccountMetadataFeed> feed(new AccountMetadataFeed()); | |
| 1035 const base::DictionaryValue* dictionary = NULL; | |
| 1036 base::Value* entry = NULL; | |
| 1037 if (!value.GetAsDictionary(&dictionary) || | |
| 1038 !dictionary->Get(kEntryField, &entry) || | |
| 1039 !feed->Parse(*entry)) { | |
| 1040 LOG(ERROR) << "Unable to create: Invalid account metadata feed!"; | |
| 1041 return scoped_ptr<AccountMetadataFeed>(NULL); | |
| 1042 } | |
| 1043 | |
| 1044 return feed.Pass(); | |
| 1045 } | |
| 1046 | |
| 1047 bool AccountMetadataFeed::Parse(const base::Value& value) { | |
| 1048 base::JSONValueConverter<AccountMetadataFeed> converter; | |
| 1049 if (!converter.Convert(value, this)) { | |
| 1050 LOG(ERROR) << "Unable to parse: Invalid account metadata feed!"; | |
| 1051 return false; | |
| 1052 } | |
| 1053 return true; | |
| 1054 } | |
| 1055 | |
| 1056 } // namespace gdata | |
| OLD | NEW |