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

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: Disable some tests 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 FilePath::StringType file_extension = file.Extension();
653 for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) {
654 const char* document_extension = kEntryKindMap[i].extension;
655 if (document_extension && file_extension == document_extension)
656 return true;
657 }
658 return false;
659 }
660
661 // static
662 DriveEntryKind DocumentEntry::GetEntryKindFromTerm(
663 const std::string& term) {
664 if (!StartsWithASCII(term, kTermPrefix, false)) {
665 DVLOG(1) << "Unexpected term prefix term " << term;
666 return ENTRY_KIND_UNKNOWN;
667 }
668
669 std::string type = term.substr(strlen(kTermPrefix));
670 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) {
671 if (type == kEntryKindMap[i].entry)
672 return kEntryKindMap[i].kind;
673 }
674 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type;
675 return ENTRY_KIND_UNKNOWN;
676 }
677
678 // static
679 int DocumentEntry::ClassifyEntryKind(DriveEntryKind kind) {
680 int classes = 0;
681
682 // All DriveEntryKind members are listed here, so the compiler catches if a
683 // newly added member is missing here.
684 switch (kind) {
685 case ENTRY_KIND_UNKNOWN:
686 // Special entries.
687 case ENTRY_KIND_ITEM:
688 case ENTRY_KIND_SITE:
689 break;
690
691 // Hosted Google document.
692 case ENTRY_KIND_DOCUMENT:
693 case ENTRY_KIND_SPREADSHEET:
694 case ENTRY_KIND_PRESENTATION:
695 case ENTRY_KIND_DRAWING:
696 case ENTRY_KIND_TABLE:
697 classes = KIND_OF_GOOGLE_DOCUMENT | KIND_OF_HOSTED_DOCUMENT;
698 break;
699
700 // Hosted external application document.
701 case ENTRY_KIND_EXTERNAL_APP:
702 classes = KIND_OF_EXTERNAL_DOCUMENT | KIND_OF_HOSTED_DOCUMENT;
703 break;
704
705 // Folders, collections.
706 case ENTRY_KIND_FOLDER:
707 classes = KIND_OF_FOLDER;
708 break;
709
710 // Regular files.
711 case ENTRY_KIND_FILE:
712 case ENTRY_KIND_PDF:
713 classes = KIND_OF_FILE;
714 break;
715
716 case ENTRY_KIND_MAX_VALUE:
717 NOTREACHED();
718 }
719
720 return classes;
721 }
722
723 void DocumentEntry::FillRemainingFields() {
724 // Set |kind_| and |labels_| based on the |categories_| in the class.
725 // JSONValueConverter does not have the ability to catch an element in a list
726 // based on a predicate. Thus we need to iterate over |categories_| and
727 // find the elements to set these fields as a post-process.
728 for (size_t i = 0; i < categories_.size(); ++i) {
729 const Category* category = categories_[i];
730 if (category->type() == Category::CATEGORY_KIND)
731 kind_ = GetEntryKindFromTerm(category->term());
732 else if (category->type() == Category::CATEGORY_LABEL)
733 labels_.push_back(category->label());
734 }
735 }
736
737 // static
738 DocumentEntry* DocumentEntry::ExtractAndParse(
739 const base::Value& value) {
740 const base::DictionaryValue* as_dict = NULL;
741 const base::DictionaryValue* entry_dict = NULL;
742 if (value.GetAsDictionary(&as_dict) &&
743 as_dict->GetDictionary(kEntryField, &entry_dict)) {
744 return DocumentEntry::CreateFrom(*entry_dict);
745 }
746 return NULL;
747 }
748
749 // static
750 DocumentEntry* DocumentEntry::CreateFrom(const base::Value& value) {
751 base::JSONValueConverter<DocumentEntry> converter;
752 scoped_ptr<DocumentEntry> entry(new DocumentEntry());
753 if (!converter.Convert(value, entry.get())) {
754 DVLOG(1) << "Invalid document entry!";
755 return NULL;
756 }
757
758 entry->FillRemainingFields();
759 return entry.release();
760 }
761
762 // static.
763 DocumentEntry* DocumentEntry::CreateFromXml(XmlReader* xml_reader) {
764 if (xml_reader->NodeName() != kEntryNode)
765 return NULL;
766
767 DocumentEntry* entry = new DocumentEntry();
768 xml_reader->NodeAttribute(kETagAttr, &entry->etag_);
769
770 if (!xml_reader->Read())
771 return entry;
772
773 bool skip_read = false;
774 do {
775 DVLOG(1) << "Parsing node " << xml_reader->NodeName();
776 skip_read = false;
777
778 if (xml_reader->NodeName() == kAuthorNode) {
779 scoped_ptr<Author> author(Author::CreateFromXml(xml_reader));
780 if (author.get())
781 entry->authors_.push_back(author.release());
782 }
783
784 if (xml_reader->NodeName() == kContentNode) {
785 scoped_ptr<Content> content(Content::CreateFromXml(xml_reader));
786 if (content.get())
787 entry->content_ = *content.get();
788 } else if (xml_reader->NodeName() == kLinkNode) {
789 scoped_ptr<Link> link(Link::CreateFromXml(xml_reader));
790 if (link.get())
791 entry->links_.push_back(link.release());
792 } else if (xml_reader->NodeName() == kFeedLinkNode) {
793 scoped_ptr<FeedLink> link(FeedLink::CreateFromXml(xml_reader));
794 if (link.get())
795 entry->feed_links_.push_back(link.release());
796 } else if (xml_reader->NodeName() == kCategoryNode) {
797 scoped_ptr<Category> category(Category::CreateFromXml(xml_reader));
798 if (category.get())
799 entry->categories_.push_back(category.release());
800 } else if (xml_reader->NodeName() == kUpdatedNode) {
801 std::string time;
802 if (xml_reader->ReadElementContent(&time))
803 gdata::util::GetTimeFromString(time, &entry->updated_time_);
804 skip_read = true;
805 } else if (xml_reader->NodeName() == kPublishedNode) {
806 std::string time;
807 if (xml_reader->ReadElementContent(&time))
808 gdata::util::GetTimeFromString(time, &entry->published_time_);
809 skip_read = true;
810 } else if (xml_reader->NodeName() == kIDNode) {
811 xml_reader->ReadElementContent(&entry->id_);
812 skip_read = true;
813 } else if (xml_reader->NodeName() == kResourceIdNode) {
814 xml_reader->ReadElementContent(&entry->resource_id_);
815 skip_read = true;
816 } else if (xml_reader->NodeName() == kTitleNode) {
817 std::string title;
818 if (xml_reader->ReadElementContent(&title))
819 entry->title_ = UTF8ToUTF16(title);
820 skip_read = true;
821 } else if (xml_reader->NodeName() == kFilenameNode) {
822 std::string file_name;
823 if (xml_reader->ReadElementContent(&file_name))
824 entry->filename_ = UTF8ToUTF16(file_name);
825 skip_read = true;
826 } else if (xml_reader->NodeName() == kSuggestedFilenameNode) {
827 std::string suggested_filename;
828 if (xml_reader->ReadElementContent(&suggested_filename))
829 entry->suggested_filename_ = UTF8ToUTF16(suggested_filename);
830 skip_read = true;
831 } else if (xml_reader->NodeName() == kMd5ChecksumNode) {
832 xml_reader->ReadElementContent(&entry->file_md5_);
833 skip_read = true;
834 } else if (xml_reader->NodeName() == kSizeNode) {
835 std::string size;
836 if (xml_reader->ReadElementContent(&size))
837 base::StringToInt64(size, &entry->file_size_);
838 skip_read = true;
839 } else {
840 DVLOG(1) << "Unknown node " << xml_reader->NodeName();
841 }
842 } while (skip_read || xml_reader->Next());
843
844 entry->FillRemainingFields();
845 return entry;
846 }
847
848 // static
849 DocumentEntry* DocumentEntry::CreateFromFileResource(const FileResource& file) {
850 scoped_ptr<DocumentEntry> entry(new DocumentEntry());
851
852 // DocumentEntry
853 entry->resource_id_ = file.file_id();
854 entry->id_ = file.file_id();
855 entry->kind_ = file.GetKind();
856 entry->title_ = UTF8ToUTF16(file.title());
857 entry->published_time_ = file.created_date();
858 // TODO(kochi): entry->labels_
859 entry->content_.url_ = file.web_content_link();
860 entry->content_.mime_type_ = file.mime_type();
861 // TODO(kochi): entry->feed_links_
862
863 // For file entries
864 entry->filename_ = UTF8ToUTF16(file.title());
865 entry->suggested_filename_ = UTF8ToUTF16(file.title());
866 entry->file_md5_ = file.md5_checksum();
867 entry->file_size_ = file.file_size();
868
869 // If file is removed completely, that information is only available in
870 // ChangeResource, and is reflected in |removed_|. If file is trashed, the
871 // file entry still exists but with its "trashed" label true.
872 entry->deleted_ = file.labels().is_trashed();
873
874 // FeedEntry
875 entry->etag_ = file.etag();
876 // entry->authors_
877 // entry->links_.
878 if (!file.parents().empty()) {
879 Link* link = new Link();
880 link->type_ = Link::LINK_PARENT;
881 link->href_ = file.parents()[0]->parent_link();
882 entry->links_.push_back(link);
883 }
884 if (!file.self_link().is_empty()) {
885 Link* link = new Link();
886 link->type_ = Link::LINK_EDIT;
887 link->href_ = file.self_link();
888 entry->links_.push_back(link);
889 }
890 if (!file.thumbnail_link().is_empty()) {
891 Link* link = new Link();
892 link->type_ = Link::LINK_THUMBNAIL;
893 link->href_ = file.thumbnail_link();
894 entry->links_.push_back(link);
895 }
896 if (!file.alternate_link().is_empty()) {
897 Link* link = new Link();
898 link->type_ = Link::LINK_ALTERNATE;
899 link->href_ = file.alternate_link();
900 entry->links_.push_back(link);
901 }
902 if (!file.embed_link().is_empty()) {
903 Link* link = new Link();
904 link->type_ = Link::LINK_EMBED;
905 link->href_ = file.embed_link();
906 entry->links_.push_back(link);
907 }
908 // entry->categories_
909 entry->updated_time_ = file.modified_by_me_date();
910
911 entry->FillRemainingFields();
912 return entry.release();
913 }
914
915 // static
916 DocumentEntry*
917 DocumentEntry::CreateFromChangeResource(const ChangeResource& change) {
918 DocumentEntry* entry = CreateFromFileResource(change.file());
919
920 // If |is_deleted()| returns true, the file is removed from Drive.
921 entry->removed_ = change.is_deleted();
922
923 return entry;
924 }
925
926 // static
927 std::string DocumentEntry::GetEntryNodeName() {
928 return kEntryNode;
929 }
930
931 ////////////////////////////////////////////////////////////////////////////////
932 // DocumentFeed implementation
933
934 DocumentFeed::DocumentFeed()
935 : start_index_(0),
936 items_per_page_(0),
937 largest_changestamp_(0) {
938 }
939
940 DocumentFeed::~DocumentFeed() {
941 }
942
943 // static
944 void DocumentFeed::RegisterJSONConverter(
945 base::JSONValueConverter<DocumentFeed>* converter) {
946 // inheritance
947 FeedEntry::RegisterJSONConverter(
948 reinterpret_cast<base::JSONValueConverter<FeedEntry>*>(converter));
949 // TODO(zelidrag): Once we figure out where these will be used, we should
950 // check for valid start_index_ and items_per_page_ values.
951 converter->RegisterCustomField<int>(
952 kStartIndexField, &DocumentFeed::start_index_, &base::StringToInt);
953 converter->RegisterCustomField<int>(
954 kItemsPerPageField, &DocumentFeed::items_per_page_, &base::StringToInt);
955 converter->RegisterStringField(kTitleTField, &DocumentFeed::title_);
956 converter->RegisterRepeatedMessage(kEntryField, &DocumentFeed::entries_);
957 converter->RegisterCustomField<int64>(
958 kLargestChangestampField, &DocumentFeed::largest_changestamp_,
959 &base::StringToInt64);
960 }
961
962 bool DocumentFeed::Parse(const base::Value& value) {
963 base::JSONValueConverter<DocumentFeed> converter;
964 if (!converter.Convert(value, this)) {
965 DVLOG(1) << "Invalid document feed!";
966 return false;
967 }
968
969 ScopedVector<DocumentEntry>::iterator iter = entries_.begin();
970 while (iter != entries_.end()) {
971 DocumentEntry* entry = (*iter);
972 entry->FillRemainingFields();
973 ++iter;
974 }
975 return true;
976 }
977
978 // static
979 scoped_ptr<DocumentFeed> DocumentFeed::ExtractAndParse(
980 const base::Value& value) {
981 const base::DictionaryValue* as_dict = NULL;
982 const base::DictionaryValue* feed_dict = NULL;
983 if (value.GetAsDictionary(&as_dict) &&
984 as_dict->GetDictionary(kFeedField, &feed_dict)) {
985 return DocumentFeed::CreateFrom(*feed_dict);
986 }
987 return scoped_ptr<DocumentFeed>(NULL);
988 }
989
990 // static
991 scoped_ptr<DocumentFeed> DocumentFeed::CreateFrom(const base::Value& value) {
992 scoped_ptr<DocumentFeed> feed(new DocumentFeed());
993 if (!feed->Parse(value)) {
994 DVLOG(1) << "Invalid document feed!";
995 return scoped_ptr<DocumentFeed>(NULL);
996 }
997
998 return feed.Pass();
999 }
1000
1001 // static
1002 scoped_ptr<DocumentFeed> DocumentFeed::CreateFromChangeList(
1003 const ChangeList& changelist) {
1004 scoped_ptr<DocumentFeed> feed(new DocumentFeed());
1005 int64 largest_changestamp = 0;
1006 ScopedVector<ChangeResource>::const_iterator iter =
1007 changelist.items().begin();
1008 while (iter != changelist.items().end()) {
1009 const FileResource& file = (*iter)->file();
1010 largest_changestamp = std::max(largest_changestamp, (*iter)->change_id());
1011 feed->entries_.push_back(DocumentEntry::CreateFromFileResource(file));
1012 ++iter;
1013 }
1014 feed->largest_changestamp_ = largest_changestamp;
1015 return feed.Pass();
1016 }
1017
1018 bool DocumentFeed::GetNextFeedURL(GURL* url) {
1019 DCHECK(url);
1020 for (size_t i = 0; i < links_.size(); ++i) {
1021 if (links_[i]->type() == Link::LINK_NEXT) {
1022 *url = links_[i]->href();
1023 return true;
1024 }
1025 }
1026 return false;
1027 }
1028
1029 void DocumentFeed::ReleaseEntries(std::vector<DocumentEntry*>* entries) {
1030 entries_.release(entries);
1031 }
1032
1033 ////////////////////////////////////////////////////////////////////////////////
1034 // InstalledApp implementation
1035
1036 InstalledApp::InstalledApp() : supports_create_(false) {
1037 }
1038
1039 InstalledApp::~InstalledApp() {
1040 }
1041
1042 InstalledApp::IconList InstalledApp::GetIconsForCategory(
1043 AppIcon::IconCategory category) const {
1044 IconList result;
1045
1046 for (ScopedVector<AppIcon>::const_iterator icon_iter = app_icons_.begin();
1047 icon_iter != app_icons_.end(); ++icon_iter) {
1048 if ((*icon_iter)->category() != category)
1049 continue;
1050 GURL icon_url = (*icon_iter)->GetIconURL();
1051 if (icon_url.is_empty())
1052 continue;
1053 result.push_back(std::make_pair((*icon_iter)->icon_side_length(),
1054 icon_url));
1055 }
1056
1057 // Return a sorted list, smallest to largest.
1058 std::sort(result.begin(), result.end(), SortBySize);
1059 return result;
1060 }
1061
1062 GURL InstalledApp::GetProductUrl() const {
1063 for (ScopedVector<Link>::const_iterator it = links_.begin();
1064 it != links_.end(); ++it) {
1065 const Link* link = *it;
1066 if (link->type() == Link::LINK_PRODUCT)
1067 return link->href();
1068 }
1069 return GURL();
1070 }
1071
1072 // static
1073 bool InstalledApp::GetValueString(const base::Value* value,
1074 std::string* result) {
1075 const base::DictionaryValue* dict = NULL;
1076 if (!value->GetAsDictionary(&dict))
1077 return false;
1078
1079 if (!dict->GetString(kTField, result))
1080 return false;
1081
1082 return true;
1083 }
1084
1085 // static
1086 void InstalledApp::RegisterJSONConverter(
1087 base::JSONValueConverter<InstalledApp>* converter) {
1088 converter->RegisterRepeatedMessage(kInstalledAppIconField,
1089 &InstalledApp::app_icons_);
1090 converter->RegisterStringField(kInstalledAppIdField,
1091 &InstalledApp::app_id_);
1092 converter->RegisterStringField(kInstalledAppNameField,
1093 &InstalledApp::app_name_);
1094 converter->RegisterStringField(kInstalledAppObjectTypeField,
1095 &InstalledApp::object_type_);
1096 converter->RegisterCustomField<bool>(kInstalledAppSupportsCreateField,
1097 &InstalledApp::supports_create_,
1098 &GetBoolFromString);
1099 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField,
1100 &InstalledApp::primary_mimetypes_,
1101 &GetValueString);
1102 converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField,
1103 &InstalledApp::secondary_mimetypes_,
1104 &GetValueString);
1105 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField,
1106 &InstalledApp::primary_extensions_,
1107 &GetValueString);
1108 converter->RegisterRepeatedCustomValue(
1109 kInstalledAppSecondaryFileExtensionField,
1110 &InstalledApp::secondary_extensions_,
1111 &GetValueString);
1112 converter->RegisterRepeatedMessage(kLinkField, &InstalledApp::links_);
1113 }
1114
1115 ////////////////////////////////////////////////////////////////////////////////
1116 // AccountMetadataFeed implementation
1117
1118 AccountMetadataFeed::AccountMetadataFeed()
1119 : quota_bytes_total_(0),
1120 quota_bytes_used_(0),
1121 largest_changestamp_(0) {
1122 }
1123
1124 AccountMetadataFeed::~AccountMetadataFeed() {
1125 }
1126
1127 // static
1128 void AccountMetadataFeed::RegisterJSONConverter(
1129 base::JSONValueConverter<AccountMetadataFeed>* converter) {
1130 converter->RegisterCustomField<int64>(
1131 kQuotaBytesTotalField,
1132 &AccountMetadataFeed::quota_bytes_total_,
1133 &base::StringToInt64);
1134 converter->RegisterCustomField<int64>(
1135 kQuotaBytesUsedField,
1136 &AccountMetadataFeed::quota_bytes_used_,
1137 &base::StringToInt64);
1138 converter->RegisterCustomField<int64>(
1139 kLargestChangestampField,
1140 &AccountMetadataFeed::largest_changestamp_,
1141 &base::StringToInt64);
1142 converter->RegisterRepeatedMessage(kInstalledAppField,
1143 &AccountMetadataFeed::installed_apps_);
1144 }
1145
1146 // static
1147 scoped_ptr<AccountMetadataFeed> AccountMetadataFeed::CreateFrom(
1148 const base::Value& value) {
1149 scoped_ptr<AccountMetadataFeed> feed(new AccountMetadataFeed());
1150 const base::DictionaryValue* dictionary = NULL;
1151 const base::Value* entry = NULL;
1152 if (!value.GetAsDictionary(&dictionary) ||
1153 !dictionary->Get(kEntryField, &entry) ||
1154 !feed->Parse(*entry)) {
1155 LOG(ERROR) << "Unable to create: Invalid account metadata feed!";
1156 return scoped_ptr<AccountMetadataFeed>(NULL);
1157 }
1158
1159 return feed.Pass();
1160 }
1161
1162 bool AccountMetadataFeed::Parse(const base::Value& value) {
1163 base::JSONValueConverter<AccountMetadataFeed> converter;
1164 if (!converter.Convert(value, this)) {
1165 LOG(ERROR) << "Unable to parse: Invalid account metadata feed!";
1166 return false;
1167 }
1168 return true;
1169 }
1170
1171 } // namespace gdata
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698