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 |