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/google_apis/gdata_wapi_parser.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/json/json_value_converter.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "base/strings/string_piece.h" | |
16 #include "base/strings/string_util.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "base/values.h" | |
19 #include "chrome/browser/google_apis/time_util.h" | |
20 | |
21 using base::Value; | |
22 using base::DictionaryValue; | |
23 using base::ListValue; | |
24 | |
25 namespace google_apis { | |
26 | |
27 namespace { | |
28 | |
29 // Term values for kSchemeKind category: | |
30 const char kTermPrefix[] = "http://schemas.google.com/docs/2007#"; | |
31 | |
32 // Node names. | |
33 const char kEntryNode[] = "entry"; | |
34 | |
35 // Field names. | |
36 const char kAuthorField[] = "author"; | |
37 const char kCategoryField[] = "category"; | |
38 const char kChangestampField[] = "docs$changestamp.value"; | |
39 const char kContentField[] = "content"; | |
40 const char kDeletedField[] = "gd$deleted"; | |
41 const char kETagField[] = "gd$etag"; | |
42 const char kEmailField[] = "email.$t"; | |
43 const char kEntryField[] = "entry"; | |
44 const char kFeedField[] = "feed"; | |
45 const char kFeedLinkField[] = "gd$feedLink"; | |
46 const char kFileNameField[] = "docs$filename.$t"; | |
47 const char kHrefField[] = "href"; | |
48 const char kIDField[] = "id.$t"; | |
49 const char kInstalledAppField[] = "docs$installedApp"; | |
50 const char kInstalledAppNameField[] = "docs$installedAppName"; | |
51 const char kInstalledAppIdField[] = "docs$installedAppId"; | |
52 const char kInstalledAppIconField[] = "docs$installedAppIcon"; | |
53 const char kInstalledAppIconCategoryField[] = "docs$installedAppIconCategory"; | |
54 const char kInstalledAppIconSizeField[] = "docs$installedAppIconSize"; | |
55 const char kInstalledAppObjectTypeField[] = "docs$installedAppObjectType"; | |
56 const char kInstalledAppPrimaryFileExtensionField[] = | |
57 "docs$installedAppPrimaryFileExtension"; | |
58 const char kInstalledAppPrimaryMimeTypeField[] = | |
59 "docs$installedAppPrimaryMimeType"; | |
60 const char kInstalledAppSecondaryFileExtensionField[] = | |
61 "docs$installedAppSecondaryFileExtension"; | |
62 const char kInstalledAppSecondaryMimeTypeField[] = | |
63 "docs$installedAppSecondaryMimeType"; | |
64 const char kInstalledAppSupportsCreateField[] = | |
65 "docs$installedAppSupportsCreate"; | |
66 const char kItemsPerPageField[] = "openSearch$itemsPerPage.$t"; | |
67 const char kLabelField[] = "label"; | |
68 const char kLargestChangestampField[] = "docs$largestChangestamp.value"; | |
69 const char kLastViewedField[] = "gd$lastViewed.$t"; | |
70 const char kLinkField[] = "link"; | |
71 const char kMD5Field[] = "docs$md5Checksum.$t"; | |
72 const char kNameField[] = "name.$t"; | |
73 const char kPublishedField[] = "published.$t"; | |
74 const char kQuotaBytesTotalField[] = "gd$quotaBytesTotal.$t"; | |
75 const char kQuotaBytesUsedField[] = "gd$quotaBytesUsed.$t"; | |
76 const char kRelField[] = "rel"; | |
77 const char kRemovedField[] = "docs$removed"; | |
78 const char kResourceIdField[] = "gd$resourceId.$t"; | |
79 const char kSchemeField[] = "scheme"; | |
80 const char kSizeField[] = "docs$size.$t"; | |
81 const char kSrcField[] = "src"; | |
82 const char kStartIndexField[] = "openSearch$startIndex.$t"; | |
83 const char kSuggestedFileNameField[] = "docs$suggestedFilename.$t"; | |
84 const char kTField[] = "$t"; | |
85 const char kTermField[] = "term"; | |
86 const char kTitleField[] = "title"; | |
87 const char kTitleTField[] = "title.$t"; | |
88 const char kTypeField[] = "type"; | |
89 const char kUpdatedField[] = "updated.$t"; | |
90 | |
91 // Link Prefixes | |
92 const char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-"; | |
93 const size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1; | |
94 | |
95 struct EntryKindMap { | |
96 DriveEntryKind kind; | |
97 const char* entry; | |
98 const char* extension; | |
99 }; | |
100 | |
101 const EntryKindMap kEntryKindMap[] = { | |
102 { ENTRY_KIND_UNKNOWN, "unknown", NULL}, | |
103 { ENTRY_KIND_ITEM, "item", NULL}, | |
104 { ENTRY_KIND_DOCUMENT, "document", ".gdoc"}, | |
105 { ENTRY_KIND_SPREADSHEET, "spreadsheet", ".gsheet"}, | |
106 { ENTRY_KIND_PRESENTATION, "presentation", ".gslides" }, | |
107 { ENTRY_KIND_DRAWING, "drawing", ".gdraw"}, | |
108 { ENTRY_KIND_TABLE, "table", ".gtable"}, | |
109 { ENTRY_KIND_FORM, "form", ".gform"}, | |
110 { ENTRY_KIND_EXTERNAL_APP, "externalapp", ".glink"}, | |
111 { ENTRY_KIND_SITE, "site", NULL}, | |
112 { ENTRY_KIND_FOLDER, "folder", NULL}, | |
113 { ENTRY_KIND_FILE, "file", NULL}, | |
114 { ENTRY_KIND_PDF, "pdf", NULL}, | |
115 }; | |
116 COMPILE_ASSERT(arraysize(kEntryKindMap) == ENTRY_KIND_MAX_VALUE, | |
117 EntryKindMap_and_DriveEntryKind_are_not_in_sync); | |
118 | |
119 struct LinkTypeMap { | |
120 Link::LinkType type; | |
121 const char* rel; | |
122 }; | |
123 | |
124 const LinkTypeMap kLinkTypeMap[] = { | |
125 { Link::LINK_SELF, | |
126 "self" }, | |
127 { Link::LINK_NEXT, | |
128 "next" }, | |
129 { Link::LINK_PARENT, | |
130 "http://schemas.google.com/docs/2007#parent" }, | |
131 { Link::LINK_ALTERNATE, | |
132 "alternate"}, | |
133 { Link::LINK_EDIT, | |
134 "edit" }, | |
135 { Link::LINK_EDIT_MEDIA, | |
136 "edit-media" }, | |
137 { Link::LINK_ALT_EDIT_MEDIA, | |
138 "http://schemas.google.com/docs/2007#alt-edit-media" }, | |
139 { Link::LINK_ALT_POST, | |
140 "http://schemas.google.com/docs/2007#alt-post" }, | |
141 { Link::LINK_FEED, | |
142 "http://schemas.google.com/g/2005#feed"}, | |
143 { Link::LINK_POST, | |
144 "http://schemas.google.com/g/2005#post"}, | |
145 { Link::LINK_BATCH, | |
146 "http://schemas.google.com/g/2005#batch"}, | |
147 { Link::LINK_THUMBNAIL, | |
148 "http://schemas.google.com/docs/2007/thumbnail"}, | |
149 { Link::LINK_RESUMABLE_EDIT_MEDIA, | |
150 "http://schemas.google.com/g/2005#resumable-edit-media"}, | |
151 { Link::LINK_RESUMABLE_CREATE_MEDIA, | |
152 "http://schemas.google.com/g/2005#resumable-create-media"}, | |
153 { Link::LINK_TABLES_FEED, | |
154 "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, | |
155 { Link::LINK_WORKSHEET_FEED, | |
156 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"}, | |
157 { Link::LINK_EMBED, | |
158 "http://schemas.google.com/docs/2007#embed"}, | |
159 { Link::LINK_PRODUCT, | |
160 "http://schemas.google.com/docs/2007#product"}, | |
161 { Link::LINK_ICON, | |
162 "http://schemas.google.com/docs/2007#icon"}, | |
163 { Link::LINK_SHARE, | |
164 "http://schemas.google.com/docs/2007#share"}, | |
165 }; | |
166 | |
167 struct ResourceLinkTypeMap { | |
168 ResourceLink::ResourceLinkType type; | |
169 const char* rel; | |
170 }; | |
171 | |
172 const ResourceLinkTypeMap kFeedLinkTypeMap[] = { | |
173 { ResourceLink::FEED_LINK_ACL, | |
174 "http://schemas.google.com/acl/2007#accessControlList" }, | |
175 { ResourceLink::FEED_LINK_REVISIONS, | |
176 "http://schemas.google.com/docs/2007/revisions" }, | |
177 }; | |
178 | |
179 struct CategoryTypeMap { | |
180 Category::CategoryType type; | |
181 const char* scheme; | |
182 }; | |
183 | |
184 const CategoryTypeMap kCategoryTypeMap[] = { | |
185 { Category::CATEGORY_KIND, "http://schemas.google.com/g/2005#kind" }, | |
186 { Category::CATEGORY_LABEL, "http://schemas.google.com/g/2005/labels" }, | |
187 }; | |
188 | |
189 struct AppIconCategoryMap { | |
190 AppIcon::IconCategory category; | |
191 const char* category_name; | |
192 }; | |
193 | |
194 const AppIconCategoryMap kAppIconCategoryMap[] = { | |
195 { AppIcon::ICON_DOCUMENT, "document" }, | |
196 { AppIcon::ICON_APPLICATION, "application" }, | |
197 { AppIcon::ICON_SHARED_DOCUMENT, "documentShared" }, | |
198 }; | |
199 | |
200 // Converts |url_string| to |result|. Always returns true to be used | |
201 // for JSONValueConverter::RegisterCustomField method. | |
202 // TODO(mukai): make it return false in case of invalid |url_string|. | |
203 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) { | |
204 *result = GURL(url_string.as_string()); | |
205 return true; | |
206 } | |
207 | |
208 // Converts boolean string values like "true" into bool. | |
209 bool GetBoolFromString(const base::StringPiece& value, bool* result) { | |
210 *result = (value == "true"); | |
211 return true; | |
212 } | |
213 | |
214 bool SortBySize(const InstalledApp::IconList::value_type& a, | |
215 const InstalledApp::IconList::value_type& b) { | |
216 return a.first < b.first; | |
217 } | |
218 | |
219 } // namespace | |
220 | |
221 //////////////////////////////////////////////////////////////////////////////// | |
222 // Author implementation | |
223 | |
224 Author::Author() { | |
225 } | |
226 | |
227 // static | |
228 void Author::RegisterJSONConverter( | |
229 base::JSONValueConverter<Author>* converter) { | |
230 converter->RegisterStringField(kNameField, &Author::name_); | |
231 converter->RegisterStringField(kEmailField, &Author::email_); | |
232 } | |
233 | |
234 //////////////////////////////////////////////////////////////////////////////// | |
235 // Link implementation | |
236 | |
237 Link::Link() : type_(Link::LINK_UNKNOWN) { | |
238 } | |
239 | |
240 Link::~Link() { | |
241 } | |
242 | |
243 // static | |
244 bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) { | |
245 DCHECK(app_id); | |
246 // Fast return path if the link clearly isn't an OPEN_WITH link. | |
247 if (rel.size() < kOpenWithPrefixSize) { | |
248 app_id->clear(); | |
249 return true; | |
250 } | |
251 | |
252 const std::string kOpenWithPrefixStr(kOpenWithPrefix); | |
253 if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) { | |
254 *app_id = rel.as_string().substr(kOpenWithPrefixStr.size()); | |
255 return true; | |
256 } | |
257 | |
258 app_id->clear(); | |
259 return true; | |
260 } | |
261 | |
262 // static. | |
263 bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) { | |
264 DCHECK(type); | |
265 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) { | |
266 if (rel == kLinkTypeMap[i].rel) { | |
267 *type = kLinkTypeMap[i].type; | |
268 return true; | |
269 } | |
270 } | |
271 | |
272 // OPEN_WITH links have extra information at the end of the rel that is unique | |
273 // for each one, so we can't just check the usual map. This check is slightly | |
274 // redundant to provide a quick skip if it's obviously not an OPEN_WITH url. | |
275 if (rel.size() >= kOpenWithPrefixSize && | |
276 StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) { | |
277 *type = LINK_OPEN_WITH; | |
278 return true; | |
279 } | |
280 | |
281 // Let unknown link types through, just report it; if the link type is needed | |
282 // in the future, add it into LinkType and kLinkTypeMap. | |
283 DVLOG(1) << "Ignoring unknown link type for rel " << rel; | |
284 *type = LINK_UNKNOWN; | |
285 return true; | |
286 } | |
287 | |
288 // static | |
289 void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) { | |
290 converter->RegisterCustomField<Link::LinkType>(kRelField, | |
291 &Link::type_, | |
292 &Link::GetLinkType); | |
293 // We have to register kRelField twice because we extract two different pieces | |
294 // of data from the same rel field. | |
295 converter->RegisterCustomField<std::string>(kRelField, | |
296 &Link::app_id_, | |
297 &Link::GetAppID); | |
298 converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString); | |
299 converter->RegisterStringField(kTitleField, &Link::title_); | |
300 converter->RegisterStringField(kTypeField, &Link::mime_type_); | |
301 } | |
302 | |
303 //////////////////////////////////////////////////////////////////////////////// | |
304 // ResourceLink implementation | |
305 | |
306 ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN) { | |
307 } | |
308 | |
309 // static. | |
310 bool ResourceLink::GetFeedLinkType( | |
311 const base::StringPiece& rel, ResourceLink::ResourceLinkType* result) { | |
312 for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) { | |
313 if (rel == kFeedLinkTypeMap[i].rel) { | |
314 *result = kFeedLinkTypeMap[i].type; | |
315 return true; | |
316 } | |
317 } | |
318 DVLOG(1) << "Unknown feed link type for rel " << rel; | |
319 return false; | |
320 } | |
321 | |
322 // static | |
323 void ResourceLink::RegisterJSONConverter( | |
324 base::JSONValueConverter<ResourceLink>* converter) { | |
325 converter->RegisterCustomField<ResourceLink::ResourceLinkType>( | |
326 kRelField, &ResourceLink::type_, &ResourceLink::GetFeedLinkType); | |
327 converter->RegisterCustomField( | |
328 kHrefField, &ResourceLink::href_, &GetGURLFromString); | |
329 } | |
330 | |
331 //////////////////////////////////////////////////////////////////////////////// | |
332 // Category implementation | |
333 | |
334 Category::Category() : type_(CATEGORY_UNKNOWN) { | |
335 } | |
336 | |
337 // Converts category.scheme into CategoryType enum. | |
338 bool Category::GetCategoryTypeFromScheme( | |
339 const base::StringPiece& scheme, Category::CategoryType* result) { | |
340 for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) { | |
341 if (scheme == kCategoryTypeMap[i].scheme) { | |
342 *result = kCategoryTypeMap[i].type; | |
343 return true; | |
344 } | |
345 } | |
346 DVLOG(1) << "Unknown feed link type for scheme " << scheme; | |
347 return false; | |
348 } | |
349 | |
350 // static | |
351 void Category::RegisterJSONConverter( | |
352 base::JSONValueConverter<Category>* converter) { | |
353 converter->RegisterStringField(kLabelField, &Category::label_); | |
354 converter->RegisterCustomField<Category::CategoryType>( | |
355 kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme); | |
356 converter->RegisterStringField(kTermField, &Category::term_); | |
357 } | |
358 | |
359 const Link* CommonMetadata::GetLinkByType(Link::LinkType type) const { | |
360 for (size_t i = 0; i < links_.size(); ++i) { | |
361 if (links_[i]->type() == type) | |
362 return links_[i]; | |
363 } | |
364 return NULL; | |
365 } | |
366 | |
367 //////////////////////////////////////////////////////////////////////////////// | |
368 // Content implementation | |
369 | |
370 Content::Content() { | |
371 } | |
372 | |
373 // static | |
374 void Content::RegisterJSONConverter( | |
375 base::JSONValueConverter<Content>* converter) { | |
376 converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString); | |
377 converter->RegisterStringField(kTypeField, &Content::mime_type_); | |
378 } | |
379 | |
380 //////////////////////////////////////////////////////////////////////////////// | |
381 // AppIcon implementation | |
382 | |
383 AppIcon::AppIcon() : category_(AppIcon::ICON_UNKNOWN), icon_side_length_(0) { | |
384 } | |
385 | |
386 AppIcon::~AppIcon() { | |
387 } | |
388 | |
389 // static | |
390 void AppIcon::RegisterJSONConverter( | |
391 base::JSONValueConverter<AppIcon>* converter) { | |
392 converter->RegisterCustomField<AppIcon::IconCategory>( | |
393 kInstalledAppIconCategoryField, | |
394 &AppIcon::category_, | |
395 &AppIcon::GetIconCategory); | |
396 converter->RegisterCustomField<int>(kInstalledAppIconSizeField, | |
397 &AppIcon::icon_side_length_, | |
398 base::StringToInt); | |
399 converter->RegisterRepeatedMessage(kLinkField, &AppIcon::links_); | |
400 } | |
401 | |
402 GURL AppIcon::GetIconURL() const { | |
403 for (size_t i = 0; i < links_.size(); ++i) { | |
404 if (links_[i]->type() == Link::LINK_ICON) | |
405 return links_[i]->href(); | |
406 } | |
407 return GURL(); | |
408 } | |
409 | |
410 // static | |
411 bool AppIcon::GetIconCategory(const base::StringPiece& category, | |
412 AppIcon::IconCategory* result) { | |
413 for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) { | |
414 if (category == kAppIconCategoryMap[i].category_name) { | |
415 *result = kAppIconCategoryMap[i].category; | |
416 return true; | |
417 } | |
418 } | |
419 DVLOG(1) << "Unknown icon category " << category; | |
420 return false; | |
421 } | |
422 | |
423 //////////////////////////////////////////////////////////////////////////////// | |
424 // CommonMetadata implementation | |
425 | |
426 CommonMetadata::CommonMetadata() { | |
427 } | |
428 | |
429 CommonMetadata::~CommonMetadata() { | |
430 } | |
431 | |
432 // static | |
433 template<typename CommonMetadataDescendant> | |
434 void CommonMetadata::RegisterJSONConverter( | |
435 base::JSONValueConverter<CommonMetadataDescendant>* converter) { | |
436 converter->RegisterStringField(kETagField, &CommonMetadata::etag_); | |
437 converter->template RegisterRepeatedMessage<Author>( | |
438 kAuthorField, &CommonMetadata::authors_); | |
439 converter->template RegisterRepeatedMessage<Link>( | |
440 kLinkField, &CommonMetadata::links_); | |
441 converter->template RegisterRepeatedMessage<Category>( | |
442 kCategoryField, &CommonMetadata::categories_); | |
443 converter->template RegisterCustomField<base::Time>( | |
444 kUpdatedField, &CommonMetadata::updated_time_, &util::GetTimeFromString); | |
445 } | |
446 | |
447 //////////////////////////////////////////////////////////////////////////////// | |
448 // ResourceEntry implementation | |
449 | |
450 ResourceEntry::ResourceEntry() | |
451 : kind_(ENTRY_KIND_UNKNOWN), | |
452 file_size_(0), | |
453 deleted_(false), | |
454 removed_(false), | |
455 changestamp_(0), | |
456 image_width_(-1), | |
457 image_height_(-1), | |
458 image_rotation_(-1) { | |
459 } | |
460 | |
461 ResourceEntry::~ResourceEntry() { | |
462 } | |
463 | |
464 bool ResourceEntry::HasFieldPresent(const base::Value* value, | |
465 bool* result) { | |
466 *result = (value != NULL); | |
467 return true; | |
468 } | |
469 | |
470 bool ResourceEntry::ParseChangestamp(const base::Value* value, | |
471 int64* result) { | |
472 DCHECK(result); | |
473 if (!value) { | |
474 *result = 0; | |
475 return true; | |
476 } | |
477 | |
478 std::string string_value; | |
479 if (value->GetAsString(&string_value) && | |
480 base::StringToInt64(string_value, result)) | |
481 return true; | |
482 | |
483 return false; | |
484 } | |
485 | |
486 // static | |
487 void ResourceEntry::RegisterJSONConverter( | |
488 base::JSONValueConverter<ResourceEntry>* converter) { | |
489 // Inherit the parent registrations. | |
490 CommonMetadata::RegisterJSONConverter(converter); | |
491 converter->RegisterStringField( | |
492 kResourceIdField, &ResourceEntry::resource_id_); | |
493 converter->RegisterStringField(kIDField, &ResourceEntry::id_); | |
494 converter->RegisterStringField(kTitleTField, &ResourceEntry::title_); | |
495 converter->RegisterCustomField<base::Time>( | |
496 kPublishedField, &ResourceEntry::published_time_, | |
497 &util::GetTimeFromString); | |
498 converter->RegisterCustomField<base::Time>( | |
499 kLastViewedField, &ResourceEntry::last_viewed_time_, | |
500 &util::GetTimeFromString); | |
501 converter->RegisterRepeatedMessage( | |
502 kFeedLinkField, &ResourceEntry::resource_links_); | |
503 converter->RegisterNestedField(kContentField, &ResourceEntry::content_); | |
504 | |
505 // File properties. If the resource type is not a normal file, then | |
506 // that's no problem because those feed must not have these fields | |
507 // themselves, which does not report errors. | |
508 converter->RegisterStringField(kFileNameField, &ResourceEntry::filename_); | |
509 converter->RegisterStringField(kMD5Field, &ResourceEntry::file_md5_); | |
510 converter->RegisterCustomField<int64>( | |
511 kSizeField, &ResourceEntry::file_size_, &base::StringToInt64); | |
512 converter->RegisterStringField( | |
513 kSuggestedFileNameField, &ResourceEntry::suggested_filename_); | |
514 // Deleted are treated as 'trashed' items on web client side. Removed files | |
515 // are gone for good. We treat both cases as 'deleted' for this client. | |
516 converter->RegisterCustomValueField<bool>( | |
517 kDeletedField, &ResourceEntry::deleted_, &ResourceEntry::HasFieldPresent); | |
518 converter->RegisterCustomValueField<bool>( | |
519 kRemovedField, &ResourceEntry::removed_, &ResourceEntry::HasFieldPresent); | |
520 converter->RegisterCustomValueField<int64>( | |
521 kChangestampField, &ResourceEntry::changestamp_, | |
522 &ResourceEntry::ParseChangestamp); | |
523 // ImageMediaMetadata fields are not supported by WAPI. | |
524 } | |
525 | |
526 std::string ResourceEntry::GetHostedDocumentExtension() const { | |
527 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { | |
528 if (kEntryKindMap[i].kind == kind_) { | |
529 if (kEntryKindMap[i].extension) | |
530 return std::string(kEntryKindMap[i].extension); | |
531 else | |
532 return std::string(); | |
533 } | |
534 } | |
535 return std::string(); | |
536 } | |
537 | |
538 // static | |
539 int ResourceEntry::ClassifyEntryKindByFileExtension( | |
540 const base::FilePath& file_path) { | |
541 #if defined(OS_WIN) | |
542 std::string file_extension = WideToUTF8(file_path.Extension()); | |
543 #else | |
544 std::string file_extension = file_path.Extension(); | |
545 #endif | |
546 for (size_t i = 0; i < arraysize(kEntryKindMap); ++i) { | |
547 const char* document_extension = kEntryKindMap[i].extension; | |
548 if (document_extension && file_extension == document_extension) | |
549 return ClassifyEntryKind(kEntryKindMap[i].kind); | |
550 } | |
551 return 0; | |
552 } | |
553 | |
554 // static | |
555 DriveEntryKind ResourceEntry::GetEntryKindFromTerm( | |
556 const std::string& term) { | |
557 if (!StartsWithASCII(term, kTermPrefix, false)) { | |
558 DVLOG(1) << "Unexpected term prefix term " << term; | |
559 return ENTRY_KIND_UNKNOWN; | |
560 } | |
561 | |
562 std::string type = term.substr(strlen(kTermPrefix)); | |
563 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { | |
564 if (type == kEntryKindMap[i].entry) | |
565 return kEntryKindMap[i].kind; | |
566 } | |
567 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; | |
568 return ENTRY_KIND_UNKNOWN; | |
569 } | |
570 | |
571 // static | |
572 int ResourceEntry::ClassifyEntryKind(DriveEntryKind kind) { | |
573 int classes = 0; | |
574 | |
575 // All DriveEntryKind members are listed here, so the compiler catches if a | |
576 // newly added member is missing here. | |
577 switch (kind) { | |
578 case ENTRY_KIND_UNKNOWN: | |
579 // Special entries. | |
580 case ENTRY_KIND_ITEM: | |
581 case ENTRY_KIND_SITE: | |
582 break; | |
583 | |
584 // Hosted Google document. | |
585 case ENTRY_KIND_DOCUMENT: | |
586 case ENTRY_KIND_SPREADSHEET: | |
587 case ENTRY_KIND_PRESENTATION: | |
588 case ENTRY_KIND_DRAWING: | |
589 case ENTRY_KIND_TABLE: | |
590 case ENTRY_KIND_FORM: | |
591 classes = KIND_OF_GOOGLE_DOCUMENT | KIND_OF_HOSTED_DOCUMENT; | |
592 break; | |
593 | |
594 // Hosted external application document. | |
595 case ENTRY_KIND_EXTERNAL_APP: | |
596 classes = KIND_OF_EXTERNAL_DOCUMENT | KIND_OF_HOSTED_DOCUMENT; | |
597 break; | |
598 | |
599 // Folders, collections. | |
600 case ENTRY_KIND_FOLDER: | |
601 classes = KIND_OF_FOLDER; | |
602 break; | |
603 | |
604 // Regular files. | |
605 case ENTRY_KIND_FILE: | |
606 case ENTRY_KIND_PDF: | |
607 classes = KIND_OF_FILE; | |
608 break; | |
609 | |
610 case ENTRY_KIND_MAX_VALUE: | |
611 NOTREACHED(); | |
612 } | |
613 | |
614 return classes; | |
615 } | |
616 | |
617 void ResourceEntry::FillRemainingFields() { | |
618 // Set |kind_| and |labels_| based on the |categories_| in the class. | |
619 // JSONValueConverter does not have the ability to catch an element in a list | |
620 // based on a predicate. Thus we need to iterate over |categories_| and | |
621 // find the elements to set these fields as a post-process. | |
622 for (size_t i = 0; i < categories_.size(); ++i) { | |
623 const Category* category = categories_[i]; | |
624 if (category->type() == Category::CATEGORY_KIND) | |
625 kind_ = GetEntryKindFromTerm(category->term()); | |
626 else if (category->type() == Category::CATEGORY_LABEL) | |
627 labels_.push_back(category->label()); | |
628 } | |
629 } | |
630 | |
631 // static | |
632 scoped_ptr<ResourceEntry> ResourceEntry::ExtractAndParse( | |
633 const base::Value& value) { | |
634 const base::DictionaryValue* as_dict = NULL; | |
635 const base::DictionaryValue* entry_dict = NULL; | |
636 if (value.GetAsDictionary(&as_dict) && | |
637 as_dict->GetDictionary(kEntryField, &entry_dict)) { | |
638 return ResourceEntry::CreateFrom(*entry_dict); | |
639 } | |
640 return scoped_ptr<ResourceEntry>(); | |
641 } | |
642 | |
643 // static | |
644 scoped_ptr<ResourceEntry> ResourceEntry::CreateFrom(const base::Value& value) { | |
645 base::JSONValueConverter<ResourceEntry> converter; | |
646 scoped_ptr<ResourceEntry> entry(new ResourceEntry()); | |
647 if (!converter.Convert(value, entry.get())) { | |
648 DVLOG(1) << "Invalid resource entry!"; | |
649 return scoped_ptr<ResourceEntry>(); | |
650 } | |
651 | |
652 entry->FillRemainingFields(); | |
653 return entry.Pass(); | |
654 } | |
655 | |
656 // static | |
657 std::string ResourceEntry::GetEntryNodeName() { | |
658 return kEntryNode; | |
659 } | |
660 | |
661 //////////////////////////////////////////////////////////////////////////////// | |
662 // ResourceList implementation | |
663 | |
664 ResourceList::ResourceList() | |
665 : start_index_(0), | |
666 items_per_page_(0), | |
667 largest_changestamp_(0) { | |
668 } | |
669 | |
670 ResourceList::~ResourceList() { | |
671 } | |
672 | |
673 // static | |
674 void ResourceList::RegisterJSONConverter( | |
675 base::JSONValueConverter<ResourceList>* converter) { | |
676 // inheritance | |
677 CommonMetadata::RegisterJSONConverter(converter); | |
678 // TODO(zelidrag): Once we figure out where these will be used, we should | |
679 // check for valid start_index_ and items_per_page_ values. | |
680 converter->RegisterCustomField<int>( | |
681 kStartIndexField, &ResourceList::start_index_, &base::StringToInt); | |
682 converter->RegisterCustomField<int>( | |
683 kItemsPerPageField, &ResourceList::items_per_page_, &base::StringToInt); | |
684 converter->RegisterStringField(kTitleTField, &ResourceList::title_); | |
685 converter->RegisterRepeatedMessage(kEntryField, &ResourceList::entries_); | |
686 converter->RegisterCustomField<int64>( | |
687 kLargestChangestampField, &ResourceList::largest_changestamp_, | |
688 &base::StringToInt64); | |
689 } | |
690 | |
691 bool ResourceList::Parse(const base::Value& value) { | |
692 base::JSONValueConverter<ResourceList> converter; | |
693 if (!converter.Convert(value, this)) { | |
694 DVLOG(1) << "Invalid resource list!"; | |
695 return false; | |
696 } | |
697 | |
698 ScopedVector<ResourceEntry>::iterator iter = entries_.begin(); | |
699 while (iter != entries_.end()) { | |
700 ResourceEntry* entry = (*iter); | |
701 entry->FillRemainingFields(); | |
702 ++iter; | |
703 } | |
704 return true; | |
705 } | |
706 | |
707 // static | |
708 scoped_ptr<ResourceList> ResourceList::ExtractAndParse( | |
709 const base::Value& value) { | |
710 const base::DictionaryValue* as_dict = NULL; | |
711 const base::DictionaryValue* feed_dict = NULL; | |
712 if (value.GetAsDictionary(&as_dict) && | |
713 as_dict->GetDictionary(kFeedField, &feed_dict)) { | |
714 return ResourceList::CreateFrom(*feed_dict); | |
715 } | |
716 return scoped_ptr<ResourceList>(); | |
717 } | |
718 | |
719 // static | |
720 scoped_ptr<ResourceList> ResourceList::CreateFrom(const base::Value& value) { | |
721 scoped_ptr<ResourceList> feed(new ResourceList()); | |
722 if (!feed->Parse(value)) { | |
723 DVLOG(1) << "Invalid resource list!"; | |
724 return scoped_ptr<ResourceList>(); | |
725 } | |
726 | |
727 return feed.Pass(); | |
728 } | |
729 | |
730 bool ResourceList::GetNextFeedURL(GURL* url) const { | |
731 DCHECK(url); | |
732 for (size_t i = 0; i < links_.size(); ++i) { | |
733 if (links_[i]->type() == Link::LINK_NEXT) { | |
734 *url = links_[i]->href(); | |
735 return true; | |
736 } | |
737 } | |
738 return false; | |
739 } | |
740 | |
741 void ResourceList::ReleaseEntries(std::vector<ResourceEntry*>* entries) { | |
742 entries_.release(entries); | |
743 } | |
744 | |
745 //////////////////////////////////////////////////////////////////////////////// | |
746 // InstalledApp implementation | |
747 | |
748 InstalledApp::InstalledApp() : supports_create_(false) { | |
749 } | |
750 | |
751 InstalledApp::~InstalledApp() { | |
752 } | |
753 | |
754 InstalledApp::IconList InstalledApp::GetIconsForCategory( | |
755 AppIcon::IconCategory category) const { | |
756 IconList result; | |
757 | |
758 for (ScopedVector<AppIcon>::const_iterator icon_iter = app_icons_.begin(); | |
759 icon_iter != app_icons_.end(); ++icon_iter) { | |
760 if ((*icon_iter)->category() != category) | |
761 continue; | |
762 GURL icon_url = (*icon_iter)->GetIconURL(); | |
763 if (icon_url.is_empty()) | |
764 continue; | |
765 result.push_back(std::make_pair((*icon_iter)->icon_side_length(), | |
766 icon_url)); | |
767 } | |
768 | |
769 // Return a sorted list, smallest to largest. | |
770 std::sort(result.begin(), result.end(), SortBySize); | |
771 return result; | |
772 } | |
773 | |
774 GURL InstalledApp::GetProductUrl() const { | |
775 for (ScopedVector<Link>::const_iterator it = links_.begin(); | |
776 it != links_.end(); ++it) { | |
777 const Link* link = *it; | |
778 if (link->type() == Link::LINK_PRODUCT) | |
779 return link->href(); | |
780 } | |
781 return GURL(); | |
782 } | |
783 | |
784 // static | |
785 bool InstalledApp::GetValueString(const base::Value* value, | |
786 std::string* result) { | |
787 const base::DictionaryValue* dict = NULL; | |
788 if (!value->GetAsDictionary(&dict)) | |
789 return false; | |
790 | |
791 if (!dict->GetString(kTField, result)) | |
792 return false; | |
793 | |
794 return true; | |
795 } | |
796 | |
797 // static | |
798 void InstalledApp::RegisterJSONConverter( | |
799 base::JSONValueConverter<InstalledApp>* converter) { | |
800 converter->RegisterRepeatedMessage(kInstalledAppIconField, | |
801 &InstalledApp::app_icons_); | |
802 converter->RegisterStringField(kInstalledAppIdField, | |
803 &InstalledApp::app_id_); | |
804 converter->RegisterStringField(kInstalledAppNameField, | |
805 &InstalledApp::app_name_); | |
806 converter->RegisterStringField(kInstalledAppObjectTypeField, | |
807 &InstalledApp::object_type_); | |
808 converter->RegisterCustomField<bool>(kInstalledAppSupportsCreateField, | |
809 &InstalledApp::supports_create_, | |
810 &GetBoolFromString); | |
811 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryMimeTypeField, | |
812 &InstalledApp::primary_mimetypes_, | |
813 &GetValueString); | |
814 converter->RegisterRepeatedCustomValue(kInstalledAppSecondaryMimeTypeField, | |
815 &InstalledApp::secondary_mimetypes_, | |
816 &GetValueString); | |
817 converter->RegisterRepeatedCustomValue(kInstalledAppPrimaryFileExtensionField, | |
818 &InstalledApp::primary_extensions_, | |
819 &GetValueString); | |
820 converter->RegisterRepeatedCustomValue( | |
821 kInstalledAppSecondaryFileExtensionField, | |
822 &InstalledApp::secondary_extensions_, | |
823 &GetValueString); | |
824 converter->RegisterRepeatedMessage(kLinkField, &InstalledApp::links_); | |
825 } | |
826 | |
827 //////////////////////////////////////////////////////////////////////////////// | |
828 // AccountMetadata implementation | |
829 | |
830 AccountMetadata::AccountMetadata() | |
831 : quota_bytes_total_(0), | |
832 quota_bytes_used_(0), | |
833 largest_changestamp_(0) { | |
834 } | |
835 | |
836 AccountMetadata::~AccountMetadata() { | |
837 } | |
838 | |
839 // static | |
840 void AccountMetadata::RegisterJSONConverter( | |
841 base::JSONValueConverter<AccountMetadata>* converter) { | |
842 converter->RegisterCustomField<int64>( | |
843 kQuotaBytesTotalField, | |
844 &AccountMetadata::quota_bytes_total_, | |
845 &base::StringToInt64); | |
846 converter->RegisterCustomField<int64>( | |
847 kQuotaBytesUsedField, | |
848 &AccountMetadata::quota_bytes_used_, | |
849 &base::StringToInt64); | |
850 converter->RegisterCustomField<int64>( | |
851 kLargestChangestampField, | |
852 &AccountMetadata::largest_changestamp_, | |
853 &base::StringToInt64); | |
854 converter->RegisterRepeatedMessage(kInstalledAppField, | |
855 &AccountMetadata::installed_apps_); | |
856 } | |
857 | |
858 // static | |
859 scoped_ptr<AccountMetadata> AccountMetadata::CreateFrom( | |
860 const base::Value& value) { | |
861 scoped_ptr<AccountMetadata> metadata(new AccountMetadata()); | |
862 const base::DictionaryValue* dictionary = NULL; | |
863 const base::Value* entry = NULL; | |
864 if (!value.GetAsDictionary(&dictionary) || | |
865 !dictionary->Get(kEntryField, &entry) || | |
866 !metadata->Parse(*entry)) { | |
867 LOG(ERROR) << "Unable to create: Invalid account metadata feed!"; | |
868 return scoped_ptr<AccountMetadata>(); | |
869 } | |
870 | |
871 return metadata.Pass(); | |
872 } | |
873 | |
874 bool AccountMetadata::Parse(const base::Value& value) { | |
875 base::JSONValueConverter<AccountMetadata> converter; | |
876 if (!converter.Convert(value, this)) { | |
877 LOG(ERROR) << "Unable to parse: Invalid account metadata feed!"; | |
878 return false; | |
879 } | |
880 return true; | |
881 } | |
882 | |
883 } // namespace google_apis | |
OLD | NEW |