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