1cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// Use of this source code is governed by a BSD-style license that can be
3cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com// found in the LICENSE file.
4cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com
5cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include "google_apis/drive/gdata_wapi_parser.h"
6cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com
7cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include <algorithm>
8cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com#include <string>
9cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com
10ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/basictypes.h"
11ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/files/file_path.h"
12ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "base/json/json_value_converter.h"
132af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/memory/scoped_ptr.h"
142af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_number_conversions.h"
152af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_piece.h"
162af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/string_util.h"
172af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/strings/utf_string_conversions.h"
182af2ad9cc0b2c7d911aed2e8d2ac77c0b7d3b5dfedisonn@google.com#include "base/values.h"
19ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com#include "google_apis/drive/time_util.h"
20ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com
21ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::Value;
22ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::DictionaryValue;
23ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comusing base::ListValue;
241f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com
25ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comnamespace google_apis {
26ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com
27ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comnamespace {
28ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com
298f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Term values for kSchemeKind category:
308f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTermPrefix[] = "http://schemas.google.com/docs/2007#";
318f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com
32ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com// Node names.
338f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kEntryNode[] = "entry";
348f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com
358f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Field names.
368f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kAuthorField[] = "author";
378f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kCategoryField[] = "category";
38ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kChangestampField[] = "docs$changestamp.value";
398f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kContentField[] = "content";
40ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kDeletedField[] = "gd$deleted";
41ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kETagField[] = "gd$etag";
422c8177767a4b3b6c27c6ac071c8619b557472521edisonn@google.comconst char kEmailField[] = "email.$t";
438f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kEntryField[] = "entry";
44ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kFeedField[] = "feed";
458f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kFeedLinkField[] = "gd$feedLink";
46ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kFileNameField[] = "docs$filename.$t";
478f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kHrefField[] = "href";
48ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kIDField[] = "id.$t";
498f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kItemsPerPageField[] = "openSearch$itemsPerPage.$t";
50ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLabelField[] = "label";
51ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLargestChangestampField[] = "docs$largestChangestamp.value";
52ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLastViewedField[] = "gd$lastViewed.$t";
53ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst char kLinkField[] = "link";
548f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kMD5Field[] = "docs$md5Checksum.$t";
558f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kNameField[] = "name.$t";
568f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kPublishedField[] = "published.$t";
578f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kRelField[] = "rel";
588f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kRemovedField[] = "docs$removed";
598f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kResourceIdField[] = "gd$resourceId.$t";
608f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kSchemeField[] = "scheme";
618f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kSizeField[] = "docs$size.$t";
625c70cdca5efe541b70d010e91607bf8626ea49cacommit-bot@chromium.orgconst char kSrcField[] = "src";
638f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kStartIndexField[] = "openSearch$startIndex.$t";
645c70cdca5efe541b70d010e91607bf8626ea49cacommit-bot@chromium.orgconst char kSuggestedFileNameField[] = "docs$suggestedFilename.$t";
658f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTermField[] = "term";
668f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kTitleField[] = "title";
67370a89980b2d38a6d01903b484bf404d6c48b496skia.committer@gmail.comconst char kTitleTField[] = "title.$t";
68370a89980b2d38a6d01903b484bf404d6c48b496skia.committer@gmail.comconst char kTypeField[] = "type";
698f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kUpdatedField[] = "updated.$t";
708f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com
718f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com// Link Prefixes
728f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst char kOpenWithPrefix[] = "http://schemas.google.com/docs/2007#open-with-";
738f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comconst size_t kOpenWithPrefixSize = arraysize(kOpenWithPrefix) - 1;
748f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com
758f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.comstruct LinkTypeMap {
768f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com  Link::LinkType type;
778f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com  const char* rel;
788f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com};
79ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com
80ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.comconst LinkTypeMap kLinkTypeMap[] = {
81ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com    { Link::LINK_SELF,
821f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com      "self" },
83ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com    { Link::LINK_NEXT,
84e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org      "next" },
85e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org    { Link::LINK_PARENT,
86e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org      "http://schemas.google.com/docs/2007#parent" },
87e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org    { Link::LINK_ALTERNATE,
88e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org      "alternate"},
89e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org    { Link::LINK_EDIT,
90e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "edit" },
91e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com    { Link::LINK_EDIT_MEDIA,
92e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "edit-media" },
93e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com    { Link::LINK_ALT_EDIT_MEDIA,
94e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "http://schemas.google.com/docs/2007#alt-edit-media" },
95e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com    { Link::LINK_ALT_POST,
96e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "http://schemas.google.com/docs/2007#alt-post" },
97e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com    { Link::LINK_FEED,
98e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "http://schemas.google.com/g/2005#feed"},
99e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com    { Link::LINK_POST,
100e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com      "http://schemas.google.com/g/2005#post"},
1018f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com    { Link::LINK_BATCH,
1028f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com      "http://schemas.google.com/g/2005#batch"},
1038f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com    { Link::LINK_THUMBNAIL,
1048f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com      "http://schemas.google.com/docs/2007/thumbnail"},
1058f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com    { Link::LINK_RESUMABLE_EDIT_MEDIA,
1069b14f26d0f3a974f3dd626c8354e1db1cfcd322frobertphillips      "http://schemas.google.com/g/2005#resumable-edit-media"},
1079b14f26d0f3a974f3dd626c8354e1db1cfcd322frobertphillips    { Link::LINK_RESUMABLE_CREATE_MEDIA,
108ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com      "http://schemas.google.com/g/2005#resumable-create-media"},
109ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com    { Link::LINK_TABLES_FEED,
110ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com      "http://schemas.google.com/spreadsheets/2006#tablesfeed"},
111ac03d91ee03599eab946a8ad25e33f9fc5f3166eedisonn@google.com    { Link::LINK_WORKSHEET_FEED,
112cf2cfa174ca878c144e17e9fc60ca8e9070d7dededisonn@google.com      "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"},
113    { Link::LINK_EMBED,
114      "http://schemas.google.com/docs/2007#embed"},
115    { Link::LINK_PRODUCT,
116      "http://schemas.google.com/docs/2007#product"},
117    { Link::LINK_ICON,
118      "http://schemas.google.com/docs/2007#icon"},
119    { Link::LINK_SHARE,
120      "http://schemas.google.com/docs/2007#share"},
121};
122
123struct ResourceLinkTypeMap {
124  ResourceLink::ResourceLinkType type;
125  const char* rel;
126};
127
128const ResourceLinkTypeMap kFeedLinkTypeMap[] = {
129    { ResourceLink::FEED_LINK_ACL,
130      "http://schemas.google.com/acl/2007#accessControlList" },
131    { ResourceLink::FEED_LINK_REVISIONS,
132      "http://schemas.google.com/docs/2007/revisions" },
133};
134
135struct CategoryTypeMap {
136  Category::CategoryType type;
137  const char* scheme;
138};
139
140const CategoryTypeMap kCategoryTypeMap[] = {
141    { Category::CATEGORY_KIND, "http://schemas.google.com/g/2005#kind" },
142    { Category::CATEGORY_LABEL, "http://schemas.google.com/g/2005/labels" },
143};
144
145// Converts |url_string| to |result|.  Always returns true to be used
146// for JSONValueConverter::RegisterCustomField method.
147// TODO(mukai): make it return false in case of invalid |url_string|.
148bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
149  *result = GURL(url_string.as_string());
150  return true;
151}
152
153}  // namespace
154
155////////////////////////////////////////////////////////////////////////////////
156// Author implementation
157
158Author::Author() {
159}
160
161// static
162void Author::RegisterJSONConverter(
163    base::JSONValueConverter<Author>* converter) {
164  converter->RegisterStringField(kNameField, &Author::name_);
165  converter->RegisterStringField(kEmailField, &Author::email_);
166}
167
168////////////////////////////////////////////////////////////////////////////////
169// Link implementation
170
171Link::Link() : type_(Link::LINK_UNKNOWN) {
172}
173
174Link::~Link() {
175}
176
177// static
178bool Link::GetAppID(const base::StringPiece& rel, std::string* app_id) {
179  DCHECK(app_id);
180  // Fast return path if the link clearly isn't an OPEN_WITH link.
181  if (rel.size() < kOpenWithPrefixSize) {
182    app_id->clear();
183    return true;
184  }
185
186  const std::string kOpenWithPrefixStr(kOpenWithPrefix);
187  if (StartsWithASCII(rel.as_string(), kOpenWithPrefixStr, false)) {
188    *app_id = rel.as_string().substr(kOpenWithPrefixStr.size());
189    return true;
190  }
191
192  app_id->clear();
193  return true;
194}
195
196// static.
197bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* type) {
198  DCHECK(type);
199  for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) {
200    if (rel == kLinkTypeMap[i].rel) {
201      *type = kLinkTypeMap[i].type;
202      return true;
203    }
204  }
205
206  // OPEN_WITH links have extra information at the end of the rel that is unique
207  // for each one, so we can't just check the usual map. This check is slightly
208  // redundant to provide a quick skip if it's obviously not an OPEN_WITH url.
209  if (rel.size() >= kOpenWithPrefixSize &&
210      StartsWithASCII(rel.as_string(), kOpenWithPrefix, false)) {
211    *type = LINK_OPEN_WITH;
212    return true;
213  }
214
215  // Let unknown link types through, just report it; if the link type is needed
216  // in the future, add it into LinkType and kLinkTypeMap.
217  DVLOG(1) << "Ignoring unknown link type for rel " << rel;
218  *type = LINK_UNKNOWN;
219  return true;
220}
221
222// static
223void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) {
224  converter->RegisterCustomField<Link::LinkType>(kRelField,
225                                                 &Link::type_,
226                                                 &Link::GetLinkType);
227  // We have to register kRelField twice because we extract two different pieces
228  // of data from the same rel field.
229  converter->RegisterCustomField<std::string>(kRelField,
230                                              &Link::app_id_,
231                                              &Link::GetAppID);
232  converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString);
233  converter->RegisterStringField(kTitleField, &Link::title_);
234  converter->RegisterStringField(kTypeField, &Link::mime_type_);
235}
236
237////////////////////////////////////////////////////////////////////////////////
238// ResourceLink implementation
239
240ResourceLink::ResourceLink() : type_(ResourceLink::FEED_LINK_UNKNOWN) {
241}
242
243// static.
244bool ResourceLink::GetFeedLinkType(
245    const base::StringPiece& rel, ResourceLink::ResourceLinkType* result) {
246  for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) {
247    if (rel == kFeedLinkTypeMap[i].rel) {
248      *result = kFeedLinkTypeMap[i].type;
249      return true;
250    }
251  }
252  DVLOG(1) << "Unknown feed link type for rel " << rel;
253  return false;
254}
255
256// static
257void ResourceLink::RegisterJSONConverter(
258    base::JSONValueConverter<ResourceLink>* converter) {
259  converter->RegisterCustomField<ResourceLink::ResourceLinkType>(
260      kRelField, &ResourceLink::type_, &ResourceLink::GetFeedLinkType);
261  converter->RegisterCustomField(
262      kHrefField, &ResourceLink::href_, &GetGURLFromString);
263}
264
265////////////////////////////////////////////////////////////////////////////////
266// Category implementation
267
268Category::Category() : type_(CATEGORY_UNKNOWN) {
269}
270
271// Converts category.scheme into CategoryType enum.
272bool Category::GetCategoryTypeFromScheme(
273    const base::StringPiece& scheme, Category::CategoryType* result) {
274  for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) {
275    if (scheme == kCategoryTypeMap[i].scheme) {
276      *result = kCategoryTypeMap[i].type;
277      return true;
278    }
279  }
280  DVLOG(1) << "Unknown feed link type for scheme " << scheme;
281  return false;
282}
283
284// static
285void Category::RegisterJSONConverter(
286    base::JSONValueConverter<Category>* converter) {
287  converter->RegisterStringField(kLabelField, &Category::label_);
288  converter->RegisterCustomField<Category::CategoryType>(
289      kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme);
290  converter->RegisterStringField(kTermField, &Category::term_);
291}
292
293const Link* CommonMetadata::GetLinkByType(Link::LinkType type) const {
294  for (size_t i = 0; i < links_.size(); ++i) {
295    if (links_[i]->type() == type)
296      return links_[i];
297  }
298  return NULL;
299}
300
301////////////////////////////////////////////////////////////////////////////////
302// Content implementation
303
304Content::Content() {
305}
306
307// static
308void Content::RegisterJSONConverter(
309    base::JSONValueConverter<Content>* converter) {
310  converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString);
311  converter->RegisterStringField(kTypeField, &Content::mime_type_);
312}
313
314////////////////////////////////////////////////////////////////////////////////
315// CommonMetadata implementation
316
317CommonMetadata::CommonMetadata() {
318}
319
320CommonMetadata::~CommonMetadata() {
321}
322
323// static
324template<typename CommonMetadataDescendant>
325void CommonMetadata::RegisterJSONConverter(
326    base::JSONValueConverter<CommonMetadataDescendant>* converter) {
327  converter->RegisterStringField(kETagField, &CommonMetadata::etag_);
328  converter->template RegisterRepeatedMessage<Author>(
329      kAuthorField, &CommonMetadata::authors_);
330  converter->template RegisterRepeatedMessage<Link>(
331      kLinkField, &CommonMetadata::links_);
332  converter->template RegisterRepeatedMessage<Category>(
333      kCategoryField, &CommonMetadata::categories_);
334  converter->template RegisterCustomField<base::Time>(
335      kUpdatedField, &CommonMetadata::updated_time_, &util::GetTimeFromString);
336}
337
338////////////////////////////////////////////////////////////////////////////////
339// ResourceEntry implementation
340
341ResourceEntry::ResourceEntry()
342    : kind_(ENTRY_KIND_UNKNOWN),
343      file_size_(0),
344      deleted_(false),
345      removed_(false),
346      changestamp_(0),
347      image_width_(-1),
348      image_height_(-1),
349      image_rotation_(-1) {
350}
351
352ResourceEntry::~ResourceEntry() {
353}
354
355bool ResourceEntry::HasFieldPresent(const base::Value* value,
356                                    bool* result) {
357  *result = (value != NULL);
358  return true;
359}
360
361bool ResourceEntry::ParseChangestamp(const base::Value* value,
362                                     int64* result) {
363  DCHECK(result);
364  if (!value) {
365    *result = 0;
366    return true;
367  }
368
369  std::string string_value;
370  if (value->GetAsString(&string_value) &&
371      base::StringToInt64(string_value, result))
372    return true;
373
374  return false;
375}
376
377// static
378void ResourceEntry::RegisterJSONConverter(
379    base::JSONValueConverter<ResourceEntry>* converter) {
380  // Inherit the parent registrations.
381  CommonMetadata::RegisterJSONConverter(converter);
382  converter->RegisterStringField(
383      kResourceIdField, &ResourceEntry::resource_id_);
384  converter->RegisterStringField(kIDField, &ResourceEntry::id_);
385  converter->RegisterStringField(kTitleTField, &ResourceEntry::title_);
386  converter->RegisterCustomField<base::Time>(
387      kPublishedField, &ResourceEntry::published_time_,
388      &util::GetTimeFromString);
389  converter->RegisterCustomField<base::Time>(
390      kLastViewedField, &ResourceEntry::last_viewed_time_,
391      &util::GetTimeFromString);
392  converter->RegisterRepeatedMessage(
393      kFeedLinkField, &ResourceEntry::resource_links_);
394  converter->RegisterNestedField(kContentField, &ResourceEntry::content_);
395
396  // File properties.  If the resource type is not a normal file, then
397  // that's no problem because those feed must not have these fields
398  // themselves, which does not report errors.
399  converter->RegisterStringField(kFileNameField, &ResourceEntry::filename_);
400  converter->RegisterStringField(kMD5Field, &ResourceEntry::file_md5_);
401  converter->RegisterCustomField<int64>(
402      kSizeField, &ResourceEntry::file_size_, &base::StringToInt64);
403  converter->RegisterStringField(
404      kSuggestedFileNameField, &ResourceEntry::suggested_filename_);
405  // Deleted are treated as 'trashed' items on web client side. Removed files
406  // are gone for good. We treat both cases as 'deleted' for this client.
407  converter->RegisterCustomValueField<bool>(
408      kDeletedField, &ResourceEntry::deleted_, &ResourceEntry::HasFieldPresent);
409  converter->RegisterCustomValueField<bool>(
410      kRemovedField, &ResourceEntry::removed_, &ResourceEntry::HasFieldPresent);
411  converter->RegisterCustomValueField<int64>(
412      kChangestampField, &ResourceEntry::changestamp_,
413      &ResourceEntry::ParseChangestamp);
414  // ImageMediaMetadata fields are not supported by WAPI.
415}
416
417// static
418ResourceEntry::ResourceEntryKind ResourceEntry::GetEntryKindFromTerm(
419    const std::string& term) {
420  if (!StartsWithASCII(term, kTermPrefix, false)) {
421    DVLOG(1) << "Unexpected term prefix term " << term;
422    return ENTRY_KIND_UNKNOWN;
423  }
424
425  std::string type = term.substr(strlen(kTermPrefix));
426  if (type == "folder")
427    return ENTRY_KIND_FOLDER;
428  if (type == "file" || type == "pdf")
429    return ENTRY_KIND_FILE;
430
431  DVLOG(1) << "Unknown entry type for term " << term << ", type " << type;
432  return ENTRY_KIND_UNKNOWN;
433}
434
435void ResourceEntry::FillRemainingFields() {
436  // Set |kind_| and |labels_| based on the |categories_| in the class.
437  // JSONValueConverter does not have the ability to catch an element in a list
438  // based on a predicate.  Thus we need to iterate over |categories_| and
439  // find the elements to set these fields as a post-process.
440  for (size_t i = 0; i < categories_.size(); ++i) {
441    const Category* category = categories_[i];
442    if (category->type() == Category::CATEGORY_KIND)
443      kind_ = GetEntryKindFromTerm(category->term());
444    else if (category->type() == Category::CATEGORY_LABEL)
445      labels_.push_back(category->label());
446  }
447}
448
449// static
450scoped_ptr<ResourceEntry> ResourceEntry::ExtractAndParse(
451    const base::Value& value) {
452  const base::DictionaryValue* as_dict = NULL;
453  const base::DictionaryValue* entry_dict = NULL;
454  if (value.GetAsDictionary(&as_dict) &&
455      as_dict->GetDictionary(kEntryField, &entry_dict)) {
456    return ResourceEntry::CreateFrom(*entry_dict);
457  }
458  return scoped_ptr<ResourceEntry>();
459}
460
461// static
462scoped_ptr<ResourceEntry> ResourceEntry::CreateFrom(const base::Value& value) {
463  base::JSONValueConverter<ResourceEntry> converter;
464  scoped_ptr<ResourceEntry> entry(new ResourceEntry());
465  if (!converter.Convert(value, entry.get())) {
466    DVLOG(1) << "Invalid resource entry!";
467    return scoped_ptr<ResourceEntry>();
468  }
469
470  entry->FillRemainingFields();
471  return entry.Pass();
472}
473
474// static
475std::string ResourceEntry::GetEntryNodeName() {
476  return kEntryNode;
477}
478
479////////////////////////////////////////////////////////////////////////////////
480// ResourceList implementation
481
482ResourceList::ResourceList()
483    : start_index_(0),
484      items_per_page_(0),
485      largest_changestamp_(0) {
486}
487
488ResourceList::~ResourceList() {
489}
490
491// static
492void ResourceList::RegisterJSONConverter(
493    base::JSONValueConverter<ResourceList>* converter) {
494  // inheritance
495  CommonMetadata::RegisterJSONConverter(converter);
496  // TODO(zelidrag): Once we figure out where these will be used, we should
497  // check for valid start_index_ and items_per_page_ values.
498  converter->RegisterCustomField<int>(
499      kStartIndexField, &ResourceList::start_index_, &base::StringToInt);
500  converter->RegisterCustomField<int>(
501      kItemsPerPageField, &ResourceList::items_per_page_, &base::StringToInt);
502  converter->RegisterStringField(kTitleTField, &ResourceList::title_);
503  converter->RegisterRepeatedMessage(kEntryField, &ResourceList::entries_);
504  converter->RegisterCustomField<int64>(
505     kLargestChangestampField, &ResourceList::largest_changestamp_,
506     &base::StringToInt64);
507}
508
509bool ResourceList::Parse(const base::Value& value) {
510  base::JSONValueConverter<ResourceList> converter;
511  if (!converter.Convert(value, this)) {
512    DVLOG(1) << "Invalid resource list!";
513    return false;
514  }
515
516  ScopedVector<ResourceEntry>::iterator iter = entries_.begin();
517  while (iter != entries_.end()) {
518    ResourceEntry* entry = (*iter);
519    entry->FillRemainingFields();
520    ++iter;
521  }
522  return true;
523}
524
525// static
526scoped_ptr<ResourceList> ResourceList::ExtractAndParse(
527    const base::Value& value) {
528  const base::DictionaryValue* as_dict = NULL;
529  const base::DictionaryValue* feed_dict = NULL;
530  if (value.GetAsDictionary(&as_dict) &&
531      as_dict->GetDictionary(kFeedField, &feed_dict)) {
532    return ResourceList::CreateFrom(*feed_dict);
533  }
534  return scoped_ptr<ResourceList>();
535}
536
537// static
538scoped_ptr<ResourceList> ResourceList::CreateFrom(const base::Value& value) {
539  scoped_ptr<ResourceList> feed(new ResourceList());
540  if (!feed->Parse(value)) {
541    DVLOG(1) << "Invalid resource list!";
542    return scoped_ptr<ResourceList>();
543  }
544
545  return feed.Pass();
546}
547
548bool ResourceList::GetNextFeedURL(GURL* url) const {
549  DCHECK(url);
550  for (size_t i = 0; i < links_.size(); ++i) {
551    if (links_[i]->type() == Link::LINK_NEXT) {
552      *url = links_[i]->href();
553      return true;
554    }
555  }
556  return false;
557}
558
559void ResourceList::ReleaseEntries(std::vector<ResourceEntry*>* entries) {
560  entries_.release(entries);
561}
562
563}  // namespace google_apis
564