Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_wapi_parser.cc

Issue 10920091: Move Drive API files to google_apis directory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reflect comments Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/gdata/gdata_wapi_parser.h ('k') | chrome/browser/chromeos/gdata/gdata_wapi_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698