Home | History | Annotate | Download | only in drive
      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 "google_apis/drive/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 "google_apis/drive/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
    884