OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/gdata/gdata_parser.h" | 5 #include "chrome/browser/chromeos/gdata/gdata_parser.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/scoped_ptr.h" | 8 #include "base/json/json_value_converter.h" |
9 #include "base/memory/scoped_ptr.h" | |
9 #include "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
11 #include "base/string_piece.h" | |
10 #include "base/string_util.h" | 12 #include "base/string_util.h" |
11 #include "base/values.h" | 13 #include "base/values.h" |
12 | 14 |
13 using base::Value; | 15 using base::Value; |
14 using base::DictionaryValue; | 16 using base::DictionaryValue; |
15 using base::ListValue; | 17 using base::ListValue; |
16 | 18 |
17 namespace gdata { | 19 namespace gdata { |
18 | 20 |
19 namespace { | 21 namespace { |
(...skipping 14 matching lines...) Expand all Loading... | |
34 const struct { | 36 const struct { |
35 DocumentEntry::EntryKind kind; | 37 DocumentEntry::EntryKind kind; |
36 const char* entry; | 38 const char* entry; |
37 } kEntryKindMap[] = { | 39 } kEntryKindMap[] = { |
38 { DocumentEntry::ITEM, "item" }, | 40 { DocumentEntry::ITEM, "item" }, |
39 { DocumentEntry::DOCUMENT, "document"}, | 41 { DocumentEntry::DOCUMENT, "document"}, |
40 { DocumentEntry::SPREADSHEET, "spreadsheet" }, | 42 { DocumentEntry::SPREADSHEET, "spreadsheet" }, |
41 { DocumentEntry::PRESENTATION, "presentation" }, | 43 { DocumentEntry::PRESENTATION, "presentation" }, |
42 { DocumentEntry::FOLDER, "folder"}, | 44 { DocumentEntry::FOLDER, "folder"}, |
43 { DocumentEntry::FILE, "file"}, | 45 { DocumentEntry::FILE, "file"}, |
44 { DocumentEntry::PDF, "pdf"}, | 46 { DocumentEntry::PDF, "pdf"} |
satorux1
2012/01/13 17:20:33
keeping , would make it easier to add new items.
Jun Mukai
2012/01/16 07:49:22
Done.
| |
45 { DocumentEntry::UNKNOWN, NULL} | |
46 }; | 47 }; |
47 | 48 |
48 const struct { | 49 const struct { |
49 Link::LinkType type; | 50 Link::LinkType type; |
50 const char* rel; | 51 const char* rel; |
51 } kLinkTypeMap[] = { | 52 } kLinkTypeMap[] = { |
52 { Link::SELF, | 53 { Link::SELF, |
53 "self" }, | 54 "self" }, |
54 { Link::NEXT, | 55 { Link::NEXT, |
55 "next" }, | 56 "next" }, |
(...skipping 13 matching lines...) Expand all Loading... | |
69 "http://schemas.google.com/g/2005#batch"}, | 70 "http://schemas.google.com/g/2005#batch"}, |
70 { Link::THUMBNAIL, | 71 { Link::THUMBNAIL, |
71 "http://schemas.google.com/docs/2007/thumbnail"}, | 72 "http://schemas.google.com/docs/2007/thumbnail"}, |
72 { Link::RESUMABLE_EDIT_MEDIA, | 73 { Link::RESUMABLE_EDIT_MEDIA, |
73 "http://schemas.google.com/g/2005#resumable-edit-media"}, | 74 "http://schemas.google.com/g/2005#resumable-edit-media"}, |
74 { Link::RESUMABLE_CREATE_MEDIA, | 75 { Link::RESUMABLE_CREATE_MEDIA, |
75 "http://schemas.google.com/g/2005#resumable-create-media"}, | 76 "http://schemas.google.com/g/2005#resumable-create-media"}, |
76 { Link::TABLES_FEED, | 77 { Link::TABLES_FEED, |
77 "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, | 78 "http://schemas.google.com/spreadsheets/2006#tablesfeed"}, |
78 { Link::WORKSHEET_FEED, | 79 { Link::WORKSHEET_FEED, |
79 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"}, | 80 "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"} |
80 { Link::UNKNOWN, | |
81 NULL} | |
82 }; | 81 }; |
83 | 82 |
84 const struct { | 83 const struct { |
85 FeedLink::FeedLinkType type; | 84 FeedLink::FeedLinkType type; |
86 const char* rel; | 85 const char* rel; |
87 } kFeedLinkTypeMap[] = { | 86 } kFeedLinkTypeMap[] = { |
88 { FeedLink::ACL, | 87 { FeedLink::ACL, |
89 "http://schemas.google.com/acl/2007#accessControlList" }, | 88 "http://schemas.google.com/acl/2007#accessControlList" }, |
90 { FeedLink::REVISIONS, | 89 { FeedLink::REVISIONS, |
91 "http://schemas.google.com/docs/2007/revisions" }, | 90 "http://schemas.google.com/docs/2007/revisions" } |
92 { FeedLink::UNKNOWN, | |
93 NULL} | |
94 }; | 91 }; |
95 | 92 |
96 const struct { | 93 const struct { |
97 Category::CategoryType type; | 94 Category::CategoryType type; |
98 const char* scheme; | 95 const char* scheme; |
99 } kCategoryTypeMap[] = { | 96 } kCategoryTypeMap[] = { |
100 { Category::KIND, | 97 { Category::KIND, |
101 "http://schemas.google.com/g/2005#kind" }, | 98 "http://schemas.google.com/g/2005#kind" }, |
102 { Category::LABEL, | 99 { Category::LABEL, |
103 "http://schemas.google.com/g/2005/labels" }, | 100 "http://schemas.google.com/g/2005/labels" } |
104 { Category::UNKNOWN, | |
105 NULL} | |
106 }; | 101 }; |
107 | 102 |
103 // Convert |url_string| to |result|. Always returns true to be used | |
satorux1
2012/01/13 17:20:33
Convert -> Converts
Jun Mukai
2012/01/16 07:49:22
Done.
| |
104 // for JSONValueConverter::RegisterCustomField method. | |
105 // TODO(mukai): make it return false in case of invalid |url_string|. | |
106 bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) { | |
107 *result = GURL(url_string.as_string()); | |
108 return true; | |
109 } | |
110 | |
108 } // namespace | 111 } // namespace |
109 | 112 |
110 const char Author::kNameField[] = "name.$t"; | 113 const char Author::kNameField[] = "name.$t"; |
111 const char Author::kEmailField[] = "email.$t"; | 114 const char Author::kEmailField[] = "email.$t"; |
112 | 115 |
113 Author::Author() { | 116 Author::Author() { |
114 } | 117 } |
115 | 118 |
116 bool Author::Parse(const DictionaryValue* dictionary) { | 119 // static |
117 if (dictionary->GetString(kNameField, &name_) && | 120 void Author::RegisterJSONConverter( |
118 dictionary->GetString(kEmailField, &email_)) { | 121 base::JSONValueConverter<Author>* converter) { |
119 return true; | 122 converter->RegisterStringField(kNameField, &Author::name_); |
120 } | 123 converter->RegisterStringField(kEmailField, &Author::email_); |
121 return false; | |
122 } | 124 } |
123 | 125 |
124 const char Link::kHrefField[] = "href"; | 126 const char Link::kHrefField[] = "href"; |
125 const char Link::kRelField[] = "rel"; | 127 const char Link::kRelField[] = "rel"; |
126 const char Link::kTitleField[] = "title"; | 128 const char Link::kTitleField[] = "title"; |
127 const char Link::kTypeField[] = "type"; | 129 const char Link::kTypeField[] = "type"; |
128 | 130 |
129 Link::Link() : type_(Link::UNKNOWN) { | 131 Link::Link() : type_(Link::UNKNOWN) { |
130 } | 132 } |
131 | 133 |
132 bool Link::Parse(const DictionaryValue* dictionary) { | 134 // static. |
133 std::string rel; | 135 bool Link::GetLinkType(const base::StringPiece& rel, Link::LinkType* result) { |
134 std::string href; | 136 for (size_t i = 0; i < arraysize(kLinkTypeMap); i++) { |
135 if (dictionary->GetString(kRelField, &rel) && | 137 if (rel == kLinkTypeMap[i].rel) { |
136 dictionary->GetString(kHrefField, &href) && | 138 *result = kLinkTypeMap[i].type; |
137 dictionary->GetString(kTypeField, &mime_type_)) { | |
138 href_ = GURL(href); | |
139 type_ = GetLinkType(rel); | |
140 if (type_ == Link::PARENT) | |
141 dictionary->GetString(kTitleField, &title_); | |
142 | |
143 if (type_ != Link::UNKNOWN) | |
144 return true; | 139 return true; |
140 } | |
145 } | 141 } |
142 DVLOG(1) << "Unknown link type for rel " << rel; | |
146 return false; | 143 return false; |
147 } | 144 } |
148 | 145 |
149 // static. | 146 // static |
150 Link::LinkType Link::GetLinkType(const std::string& rel) { | 147 void Link::RegisterJSONConverter(base::JSONValueConverter<Link>* converter) { |
151 for (size_t i = 0; kLinkTypeMap[i].rel; i++) { | 148 converter->RegisterCustomField<Link::LinkType>( |
152 if (rel == kLinkTypeMap[i].rel) | 149 kRelField, &Link::type_, &Link::GetLinkType); |
153 return kLinkTypeMap[i].type; | 150 converter->RegisterCustomField(kHrefField, &Link::href_, &GetGURLFromString); |
154 } | 151 converter->RegisterStringField(kTitleField, &Link::title_); |
155 DVLOG(1) << "Unknown link type for rel " << rel; | 152 converter->RegisterStringField(kTypeField, &Link::mime_type_); |
156 return Link::UNKNOWN; | |
157 } | 153 } |
158 | 154 |
159 const char FeedLink::kHrefField[] = "href"; | 155 const char FeedLink::kHrefField[] = "href"; |
160 const char FeedLink::kRelField[] = "rel"; | 156 const char FeedLink::kRelField[] = "rel"; |
161 | 157 |
162 FeedLink::FeedLink() : type_(FeedLink::UNKNOWN) { | 158 FeedLink::FeedLink() : type_(FeedLink::UNKNOWN) { |
163 } | 159 } |
164 | 160 |
165 bool FeedLink::Parse(const DictionaryValue* dictionary) { | 161 // static. |
166 std::string rel; | 162 bool FeedLink::GetFeedLinkType( |
167 std::string href; | 163 const base::StringPiece& rel, FeedLink::FeedLinkType* result) { |
168 if (dictionary->GetString(kRelField, &rel) && | 164 for (size_t i = 0; i < arraysize(kFeedLinkTypeMap); i++) { |
169 dictionary->GetString(kHrefField, &href)) { | 165 if (rel == kFeedLinkTypeMap[i].rel) { |
170 href_ = GURL(href); | 166 *result = kFeedLinkTypeMap[i].type; |
171 type_ = GetFeedLinkType(rel); | |
172 if (type_ != FeedLink::UNKNOWN) | |
173 return true; | 167 return true; |
168 } | |
174 } | 169 } |
170 DVLOG(1) << "Unknown feed link type for rel " << rel; | |
175 return false; | 171 return false; |
176 } | 172 } |
177 | 173 |
178 // static. | 174 // static |
179 FeedLink::FeedLinkType FeedLink::GetFeedLinkType(const std::string& rel) { | 175 void FeedLink::RegisterJSONConverter( |
180 for (size_t i = 0; kFeedLinkTypeMap[i].rel; i++) { | 176 base::JSONValueConverter<FeedLink>* converter) { |
181 if (rel == kFeedLinkTypeMap[i].rel) | 177 converter->RegisterCustomField<FeedLink::FeedLinkType>( |
182 return kFeedLinkTypeMap[i].type; | 178 kRelField, &FeedLink::type_, &FeedLink::GetFeedLinkType); |
183 } | 179 converter->RegisterCustomField( |
184 DVLOG(1) << "Unknown feed link type for rel " << rel; | 180 kHrefField, &FeedLink::href_, &GetGURLFromString); |
185 return FeedLink::UNKNOWN; | |
186 } | 181 } |
187 | 182 |
188 const char Category::kLabelField[] = "label"; | 183 const char Category::kLabelField[] = "label"; |
189 const char Category::kSchemeField[] = "scheme"; | 184 const char Category::kSchemeField[] = "scheme"; |
190 const char Category::kTermField[] = "term"; | 185 const char Category::kTermField[] = "term"; |
191 | 186 |
192 Category::Category() { | 187 Category::Category() : type_(UNKNOWN) { |
193 } | 188 } |
194 | 189 |
195 bool Category::Parse(const DictionaryValue* dictionary) { | 190 // Converts category.scheme into CategoryType enum. |
196 std::string scheme; | 191 bool Category::GetCategoryTypeFromScheme( |
197 if (dictionary->GetString(kLabelField, &label_) && | 192 const base::StringPiece& scheme, Category::CategoryType* result) { |
198 dictionary->GetString(kSchemeField, &scheme) && | 193 for (size_t i = 0; i < arraysize(kCategoryTypeMap); i++) { |
199 dictionary->GetString(kTermField, &term_)) { | 194 if (scheme == kCategoryTypeMap[i].scheme) { |
200 type_ = GetCategoryTypeFromScheme(scheme); | 195 *result = kCategoryTypeMap[i].type; |
201 if (type_ != Category::UNKNOWN) | |
202 return true; | 196 return true; |
203 | 197 } |
204 DVLOG(1) << "Unknown category:" | |
205 << "\n label = " << label_ | |
206 << "\n scheme = " << scheme | |
207 << "\n term = " << term_; | |
208 } | 198 } |
199 DVLOG(1) << "Unknown feed link type for scheme " << scheme; | |
209 return false; | 200 return false; |
210 } | 201 } |
211 | 202 |
212 // Converts category.scheme into CategoryType enum. | 203 // static |
213 Category::CategoryType Category::GetCategoryTypeFromScheme( | 204 void Category::RegisterJSONConverter( |
214 const std::string& scheme) { | 205 base::JSONValueConverter<Category>* converter) { |
215 for (size_t i = 0; kCategoryTypeMap[i].scheme; i++) { | 206 converter->RegisterStringField(kLabelField, &Category::label_); |
216 if (scheme == kCategoryTypeMap[i].scheme) | 207 converter->RegisterCustomField<Category::CategoryType>( |
217 return kCategoryTypeMap[i].type; | 208 kSchemeField, &Category::type_, &Category::GetCategoryTypeFromScheme); |
218 } | 209 converter->RegisterStringField(kTermField, &Category::term_); |
219 DVLOG(1) << "Unknown feed link type for scheme " << scheme; | |
220 return Category::UNKNOWN; | |
221 } | 210 } |
222 | 211 |
223 | |
224 const Link* GDataEntry::GetLinkByType(Link::LinkType type) const { | 212 const Link* GDataEntry::GetLinkByType(Link::LinkType type) const { |
225 for (ScopedVector<Link>::const_iterator iter = links_.begin(); | 213 for (size_t i = 0; i < links_.size(); ++i) { |
226 iter != links_.end(); ++iter) { | 214 if (links_[i]->type() == type) |
227 if ((*iter)->type() == type) | 215 return links_[i]; |
228 return (*iter); | |
229 } | 216 } |
230 return NULL; | 217 return NULL; |
231 } | 218 } |
232 | 219 |
220 const char Content::kSrcField[] = "src"; | |
221 const char Content::kTypeField[] = "type"; | |
222 | |
223 Content::Content() { | |
224 } | |
225 | |
226 // static | |
227 void Content::RegisterJSONConverter( | |
228 base::JSONValueConverter<Content>* converter) { | |
229 converter->RegisterCustomField(kSrcField, &Content::url_, &GetGURLFromString); | |
230 converter->RegisterStringField(kTypeField, &Content::mime_type_); | |
231 } | |
232 | |
233 const char GDataEntry::kTimeParsingDelimiters[] = "-:.TZ"; | 233 const char GDataEntry::kTimeParsingDelimiters[] = "-:.TZ"; |
234 const char GDataEntry::kAuthorField[] = "author"; | 234 const char GDataEntry::kAuthorField[] = "author"; |
235 const char GDataEntry::kLinkField[] = "link"; | 235 const char GDataEntry::kLinkField[] = "link"; |
236 const char GDataEntry::kCategoryField[] = "category"; | 236 const char GDataEntry::kCategoryField[] = "category"; |
237 const char GDataEntry::kETagField[] = "gd$etag"; | |
238 const char GDataEntry::kUpdatedField[] = "updated.$t"; | |
237 | 239 |
238 GDataEntry::GDataEntry() { | 240 GDataEntry::GDataEntry() { |
239 } | 241 } |
240 | 242 |
241 GDataEntry::~GDataEntry() { | 243 GDataEntry::~GDataEntry() { |
242 } | 244 } |
243 | 245 |
244 bool GDataEntry::ParseAuthors(const DictionaryValue* value_dict) { | 246 // static |
245 ListValue* authors = NULL; | 247 void GDataEntry::RegisterJSONConverter( |
246 if (value_dict->GetList(kAuthorField, &authors)) { | 248 base::JSONValueConverter<GDataEntry>* converter) { |
247 for (ListValue::iterator iter = authors->begin(); | 249 converter->RegisterStringField(kETagField, &GDataEntry::etag_); |
248 iter != authors->end(); | 250 converter->RegisterRepeatedMessage(kAuthorField, &GDataEntry::authors_); |
249 ++iter) { | 251 converter->RegisterRepeatedMessage(kLinkField, &GDataEntry::links_); |
250 if ((*iter)->GetType() != Value::TYPE_DICTIONARY) { | 252 converter->RegisterRepeatedMessage(kCategoryField, &GDataEntry::categories_); |
251 DVLOG(1) << "Invalid author list element"; | 253 converter->RegisterCustomField<base::Time>( |
252 return false; | 254 kUpdatedField, |
253 } | 255 &GDataEntry::updated_time_, |
254 DictionaryValue* author_dict = | 256 &GDataEntry::GetTimeFromString); |
255 reinterpret_cast<DictionaryValue*>(*iter); | |
256 scoped_ptr<Author> author(new Author()); | |
257 if (author->Parse(author_dict)) { | |
258 authors_.push_back(author.release()); | |
259 } else { | |
260 DVLOG(1) << "Invalid author etag = " << etag_; | |
261 } | |
262 } | |
263 } | |
264 return true; | |
265 } | 257 } |
266 | 258 |
267 bool GDataEntry::ParseLinks(const DictionaryValue* value_dict) { | 259 // static |
268 ListValue* links = NULL; | 260 bool GDataEntry::GetTimeFromString(const base::StringPiece& raw_value, |
269 if (value_dict->GetList(kLinkField, &links)) { | |
270 for (ListValue::iterator iter = links->begin(); | |
271 iter != links->end(); | |
272 ++iter) { | |
273 if ((*iter)->GetType() != Value::TYPE_DICTIONARY) { | |
274 DVLOG(1) << "Invalid link list element"; | |
275 return false; | |
276 } | |
277 DictionaryValue* link_dict = | |
278 reinterpret_cast<DictionaryValue*>(*iter); | |
279 scoped_ptr<Link> link(new Link()); | |
280 if (link->Parse(link_dict)) | |
281 links_.push_back(link.release()); | |
282 else | |
283 DVLOG(1) << "Invalid link etag = " << etag_; | |
284 } | |
285 } | |
286 return true; | |
287 } | |
288 | |
289 // static. | |
290 bool GDataEntry::GetTimeFromString(const std::string& raw_value, | |
291 base::Time* time) { | 261 base::Time* time) { |
292 std::vector<std::string> parts; | 262 std::vector<base::StringPiece> parts; |
293 if (Tokenize(raw_value, kTimeParsingDelimiters, &parts) != 7) | 263 if (Tokenize(raw_value, kTimeParsingDelimiters, &parts) != 7) |
294 return false; | 264 return false; |
295 | 265 |
296 base::Time::Exploded exploded; | 266 base::Time::Exploded exploded; |
297 if (!base::StringToInt(parts[0], &exploded.year) || | 267 if (!base::StringToInt(parts[0], &exploded.year) || |
298 !base::StringToInt(parts[1], &exploded.month) || | 268 !base::StringToInt(parts[1], &exploded.month) || |
299 !base::StringToInt(parts[2], &exploded.day_of_month) || | 269 !base::StringToInt(parts[2], &exploded.day_of_month) || |
300 !base::StringToInt(parts[3], &exploded.hour) || | 270 !base::StringToInt(parts[3], &exploded.hour) || |
301 !base::StringToInt(parts[4], &exploded.minute) || | 271 !base::StringToInt(parts[4], &exploded.minute) || |
302 !base::StringToInt(parts[5], &exploded.second) || | 272 !base::StringToInt(parts[5], &exploded.second) || |
303 !base::StringToInt(parts[6], &exploded.millisecond)) { | 273 !base::StringToInt(parts[6], &exploded.millisecond)) { |
304 return false; | 274 return false; |
305 } | 275 } |
306 | 276 |
307 exploded.day_of_week = 0; | 277 exploded.day_of_week = 0; |
308 if (!exploded.HasValidValues()) | 278 if (!exploded.HasValidValues()) |
309 return false; | 279 return false; |
310 | 280 |
311 *time = base::Time::FromLocalExploded(exploded); | 281 *time = base::Time::FromLocalExploded(exploded); |
312 return true; | 282 return true; |
313 } | 283 } |
314 | 284 |
315 bool GDataEntry::ParseCategories(const DictionaryValue* value_dict) { | |
316 ListValue* categories = NULL; | |
317 if (value_dict->GetList(kCategoryField, &categories)) { | |
318 for (ListValue::iterator iter = categories->begin(); | |
319 iter != categories->end(); | |
320 ++iter) { | |
321 if ((*iter)->GetType() != Value::TYPE_DICTIONARY) { | |
322 DVLOG(1) << "Invalid category list element"; | |
323 return false; | |
324 } | |
325 DictionaryValue* category_dict = | |
326 reinterpret_cast<DictionaryValue*>(*iter); | |
327 scoped_ptr<Category> category(new Category()); | |
328 if (category->Parse(category_dict)) { | |
329 OnAddCategory(category.get()); | |
330 categories_->push_back(category.release()); | |
331 } else { | |
332 DVLOG(1) << "Invalid category etag = " << etag_; | |
333 } | |
334 } | |
335 } | |
336 return true; | |
337 } | |
338 | |
339 // static. | |
340 bool GDataEntry::ParseDateTime(const DictionaryValue* dict, | |
341 const std::string& field, | |
342 base::Time* time) { | |
343 std::string raw_value; | |
344 if (!dict->GetString(field, &raw_value)) | |
345 return false; | |
346 | |
347 return GetTimeFromString(raw_value, time); | |
348 } | |
349 | |
350 const char DocumentEntry::kFeedLinkField[] = "gd$feedLink"; | 285 const char DocumentEntry::kFeedLinkField[] = "gd$feedLink"; |
351 const char DocumentEntry::kContentField[] = "content"; | 286 const char DocumentEntry::kContentField[] = "content"; |
352 const char DocumentEntry::kSrcField[] = "src"; | |
353 const char DocumentEntry::kTypeField[] = "type"; | |
354 const char DocumentEntry::kFileNameField[] = "docs$filename.$t"; | 287 const char DocumentEntry::kFileNameField[] = "docs$filename.$t"; |
355 const char DocumentEntry::kMD5Field[] = "docs$md5Checksum.$t"; | 288 const char DocumentEntry::kMD5Field[] = "docs$md5Checksum.$t"; |
356 const char DocumentEntry::kSizeField[] = "docs$size.$t"; | 289 const char DocumentEntry::kSizeField[] = "docs$size.$t"; |
357 const char DocumentEntry::kSuggestedFileNameField[] = | 290 const char DocumentEntry::kSuggestedFileNameField[] = |
358 "docs$suggestedFilename.$t"; | 291 "docs$suggestedFilename.$t"; |
359 const char DocumentEntry::kETagField[] = "gd$etag"; | |
360 const char DocumentEntry::kResourceIdField[] = "gd$resourceId.$t"; | 292 const char DocumentEntry::kResourceIdField[] = "gd$resourceId.$t"; |
361 const char DocumentEntry::kIDField[] = "id.$t"; | 293 const char DocumentEntry::kIDField[] = "id.$t"; |
362 const char DocumentEntry::kTitleField[] = "title.$t"; | 294 const char DocumentEntry::kTitleField[] = "title.$t"; |
363 const char DocumentEntry::kUpdatedField[] = "updated.$t"; | |
364 const char DocumentEntry::kPublishedField[] = "published.$t"; | 295 const char DocumentEntry::kPublishedField[] = "published.$t"; |
365 | 296 |
366 DocumentEntry::DocumentEntry() : kind_(DocumentEntry::UNKNOWN), file_size_(0) { | 297 DocumentEntry::DocumentEntry() : kind_(DocumentEntry::UNKNOWN), file_size_(0) { |
367 } | 298 } |
368 | 299 |
369 DocumentEntry::~DocumentEntry() { | 300 DocumentEntry::~DocumentEntry() { |
370 } | 301 } |
371 | 302 |
372 bool DocumentEntry::ParseFeedLinks(const DictionaryValue* value_dict) { | 303 // static |
373 ListValue* links = NULL; | 304 void DocumentEntry::RegisterJSONConverter( |
374 if (value_dict->GetList(kFeedLinkField, &links)) { | 305 base::JSONValueConverter<DocumentEntry>* converter) { |
375 for (ListValue::iterator iter = links->begin(); | 306 // inheritant the parent registrations. |
376 iter != links->end(); | 307 GDataEntry::RegisterJSONConverter( |
377 ++iter) { | 308 reinterpret_cast<base::JSONValueConverter<GDataEntry>*>(converter)); |
378 if ((*iter)->GetType() != Value::TYPE_DICTIONARY) { | 309 converter->RegisterStringField( |
379 DVLOG(1) << "Invalid feed link list element"; | 310 kResourceIdField, &DocumentEntry::resource_id_); |
380 return false; | 311 converter->RegisterStringField(kIDField, &DocumentEntry::id_); |
381 } | 312 converter->RegisterStringField(kTitleField, &DocumentEntry::title_); |
382 DictionaryValue* link_dict = | 313 converter->RegisterCustomField<base::Time>( |
383 reinterpret_cast<DictionaryValue*>(*iter); | 314 kPublishedField, &DocumentEntry::published_time_, |
384 scoped_ptr<FeedLink> link(new FeedLink()); | 315 &GDataEntry::GetTimeFromString); |
385 if (link->Parse(link_dict)) { | 316 converter->RegisterRepeatedMessage( |
386 feed_links_.push_back(link.release()); | 317 kFeedLinkField, &DocumentEntry::feed_links_); |
387 } else { | 318 converter->RegisterNestedField(kContentField, &DocumentEntry::content_); |
388 DVLOG(1) << "Invalid feed link etag = " << etag_; | 319 |
389 } | 320 // File properties. If the document type is not a normal file, then |
390 } | 321 // that's no problem because those feed must not have these fields |
391 } | 322 // themselves, which does not report errors. |
392 return true; | 323 converter->RegisterStringField(kFileNameField, &DocumentEntry::filename_); |
324 converter->RegisterStringField(kMD5Field, &DocumentEntry::file_md5_); | |
325 converter->RegisterCustomField<int64>( | |
326 kSizeField, &DocumentEntry::file_size_, &base::StringToInt64); | |
327 converter->RegisterStringField( | |
328 kSuggestedFileNameField, &DocumentEntry::suggested_filename_); | |
393 } | 329 } |
394 | 330 |
395 bool DocumentEntry::ParseContent(const DictionaryValue* value_dict) { | 331 // static |
396 base::DictionaryValue* content = NULL; | |
397 if (value_dict->GetDictionary(kContentField, &content)) { | |
398 std::string src; | |
399 if (content->GetString(kSrcField, &src) && | |
400 content->GetString(kTypeField, &content_mime_type_)) { | |
401 content_url_ = GURL(src); | |
402 return true; | |
403 } | |
404 } | |
405 DVLOG(1) << "Invalid item content etag = " << etag_; | |
406 return false; | |
407 } | |
408 | |
409 bool DocumentEntry::ParseFileProperties(const DictionaryValue* value_dict) { | |
410 if (!value_dict->GetString(kFileNameField, &filename_)) { | |
411 DVLOG(1) << "File item with no name! " << etag_; | |
412 return false; | |
413 } | |
414 | |
415 if (!value_dict->GetString(kMD5Field, &file_md5_)) { | |
416 DVLOG(1) << "File item with no md5! " << etag_; | |
417 return false; | |
418 } | |
419 | |
420 std::string file_size; | |
421 if (!value_dict->GetString(kSizeField, &file_size) || | |
422 !file_size.length()) { | |
423 DVLOG(1) << "File item with no size! " << etag_; | |
424 return false; | |
425 } | |
426 | |
427 if (!base::StringToInt64(file_size, &file_size_)) { | |
428 DVLOG(1) << "Invalid file size '" << file_size << "' for " << etag_; | |
429 return false; | |
430 } | |
431 | |
432 if (!value_dict->GetString(kSuggestedFileNameField, &suggested_filename_)) | |
433 DVLOG(1) << "File item with no docs$suggestedFilename! " << etag_; | |
434 | |
435 return true; | |
436 } | |
437 | |
438 bool DocumentEntry::Parse(const DictionaryValue* value_dict) { | |
439 if (!value_dict->GetString(kETagField, &etag_)) { | |
440 DVLOG(1) << "Item with no etag!"; | |
441 return false; | |
442 } | |
443 | |
444 if (!value_dict->GetString(kResourceIdField, &resource_id_)) { | |
445 DVLOG(1) << "Item with no resource id! " << etag_; | |
446 return false; | |
447 } | |
448 | |
449 if (!value_dict->GetString(kIDField, &id_)) { | |
450 DVLOG(1) << "Item with no id! " << etag_; | |
451 return false; | |
452 } | |
453 | |
454 if (!value_dict->GetString(kTitleField, &title_)) { | |
455 DVLOG(1) << "Item with no title! " << etag_; | |
456 return false; | |
457 } | |
458 | |
459 if (!ParseDateTime(value_dict, kUpdatedField, &updated_time_)) { | |
460 DVLOG(1) << "Item with no updated date! " << etag_; | |
461 return false; | |
462 } | |
463 | |
464 if (!ParseDateTime(value_dict, kPublishedField, &published_time_)) { | |
465 DVLOG(1) << "Item with no published date! " << etag_; | |
466 return false; | |
467 } | |
468 | |
469 // Parse categories, will set up entry->kind as well. | |
470 if (!ParseCategories(value_dict)) | |
471 return false; | |
472 | |
473 if (kind_ == DocumentEntry::FILE || kind_ == DocumentEntry::PDF) { | |
474 if (!ParseFileProperties(value_dict)) | |
475 return false; | |
476 } | |
477 | |
478 if (!ParseAuthors(value_dict)) | |
479 return false; | |
480 | |
481 if (!ParseContent(value_dict)) | |
482 return false; | |
483 | |
484 if (!ParseLinks(value_dict)) | |
485 return false; | |
486 | |
487 if (!ParseFeedLinks(value_dict)) | |
488 return false; | |
489 | |
490 return true; | |
491 } | |
492 | |
493 // static. | |
494 DocumentEntry::EntryKind DocumentEntry::GetEntryKindFromTerm( | 332 DocumentEntry::EntryKind DocumentEntry::GetEntryKindFromTerm( |
495 const std::string& term) { | 333 const std::string& term) { |
496 if (!StartsWithASCII(term, kTermPrefix, false)) { | 334 if (!StartsWithASCII(term, kTermPrefix, false)) { |
497 DVLOG(1) << "Unexpected term prefix term " << term; | 335 DVLOG(1) << "Unexpected term prefix term " << term; |
498 return DocumentEntry::UNKNOWN; | 336 return DocumentEntry::UNKNOWN; |
499 } | 337 } |
500 | 338 |
501 std::string type = term.substr(strlen(kTermPrefix)); | 339 std::string type = term.substr(strlen(kTermPrefix)); |
502 for (size_t i = 0; kEntryKindMap[i].entry; i++) { | 340 for (size_t i = 0; i < arraysize(kEntryKindMap); i++) { |
503 if (type == kEntryKindMap[i].entry) | 341 if (type == kEntryKindMap[i].entry) |
504 return kEntryKindMap[i].kind; | 342 return kEntryKindMap[i].kind; |
505 } | 343 } |
506 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; | 344 DVLOG(1) << "Unknown entry type for term " << term << ", type " << type; |
507 return DocumentEntry::UNKNOWN; | 345 return DocumentEntry::UNKNOWN; |
508 } | 346 } |
509 | 347 |
510 void DocumentEntry::OnAddCategory(Category* category) { | 348 void DocumentEntry::FillRemainingFields() { |
satorux1
2012/01/13 17:20:33
it may be nice to document why JSONValueConverter
Jun Mukai
2012/01/16 07:49:22
Done.
| |
511 if (category->type() == Category::KIND) | 349 for (size_t i = 0; i < categories_.size(); ++i) { |
512 kind_ = GetEntryKindFromTerm(category->term()); | 350 const Category* category = categories_[i]; |
513 else if (category->type() == Category::LABEL) | 351 if (category->type() == Category::KIND) |
514 labels_.push_back(category->label()); | 352 kind_ = GetEntryKindFromTerm(category->term()); |
353 else if (category->type() == Category::LABEL) | |
354 labels_.push_back(category->label()); | |
355 } | |
515 } | 356 } |
516 | 357 |
517 | |
518 const char DocumentFeed::kETagField[] = "gd$etag"; | |
519 const char DocumentFeed::kStartIndexField[] = "openSearch$startIndex.$t"; | 358 const char DocumentFeed::kStartIndexField[] = "openSearch$startIndex.$t"; |
520 const char DocumentFeed::kItemsPerPageField[] = | 359 const char DocumentFeed::kItemsPerPageField[] = |
521 "openSearch$itemsPerPage.$t"; | 360 "openSearch$itemsPerPage.$t"; |
522 const char DocumentFeed::kUpdatedField[] = "updated.$t"; | |
523 const char DocumentFeed::kTitleField[] = "title.$t"; | 361 const char DocumentFeed::kTitleField[] = "title.$t"; |
524 const char DocumentFeed::kEntryField[] = "entry"; | 362 const char DocumentFeed::kEntryField[] = "entry"; |
525 | 363 |
526 DocumentFeed::DocumentFeed() : start_index_(0), items_per_page_(0) { | 364 DocumentFeed::DocumentFeed() : start_index_(0), items_per_page_(0) { |
527 } | 365 } |
528 | 366 |
529 DocumentFeed::~DocumentFeed() { | 367 DocumentFeed::~DocumentFeed() { |
530 } | 368 } |
531 | 369 |
532 bool DocumentFeed::Parse(const DictionaryValue* value_dict) { | 370 // static |
533 if (!value_dict->GetString(kETagField, &etag_)) { | 371 void DocumentFeed::RegisterJSONConverter( |
534 DVLOG(1) << "Feed with no etag!"; | 372 base::JSONValueConverter<DocumentFeed>* converter) { |
535 return false; | 373 // inheritance |
536 } | 374 GDataEntry::RegisterJSONConverter( |
537 | 375 reinterpret_cast<base::JSONValueConverter<GDataEntry>*>(converter)); |
538 // TODO(zelidrag): Once we figure out where these will be used, we should | 376 // TODO(zelidrag): Once we figure out where these will be used, we should |
539 // check for valid start_index_ and items_per_page_ values. | 377 // check for valid start_index_ and items_per_page_ values. |
540 std::string start_index; | 378 converter->RegisterCustomField<int>( |
541 if (!value_dict->GetString(kStartIndexField, &start_index) || | 379 kStartIndexField, &DocumentFeed::start_index_, &base::StringToInt); |
542 !base::StringToInt(start_index, &start_index_)) { | 380 converter->RegisterCustomField<int>( |
543 DVLOG(1) << "Feed with no startIndex! " << etag_; | 381 kItemsPerPageField, &DocumentFeed::items_per_page_, &base::StringToInt); |
544 return false; | 382 converter->RegisterStringField(kTitleField, &DocumentFeed::title_); |
545 } | 383 converter->RegisterRepeatedMessage(kEntryField, &DocumentFeed::entries_); |
546 | |
547 std::string items_per_page; | |
548 if (!value_dict->GetString(kItemsPerPageField, &items_per_page) || | |
549 !base::StringToInt(items_per_page, &items_per_page_)) { | |
550 DVLOG(1) << "Feed with no itemsPerPage! " << etag_; | |
551 return false; | |
552 } | |
553 | |
554 if (!ParseDateTime(value_dict, kUpdatedField, &updated_time_)) { | |
555 DVLOG(1) << "Feed with no updated date! " << etag_; | |
556 return false; | |
557 } | |
558 | |
559 if (!value_dict->GetString(kUpdatedField, &title_)) { | |
560 DVLOG(1) << "Feed with no title!"; | |
561 return false; | |
562 } | |
563 | |
564 ListValue* entries = NULL; | |
565 if (value_dict->GetList(kEntryField, &entries)) { | |
566 for (ListValue::iterator iter = entries->begin(); | |
567 iter != entries->end(); | |
568 ++iter) { | |
569 DocumentEntry* entry = DocumentEntry::CreateFrom(*iter); | |
570 if (entry) | |
571 entries_.push_back(entry); | |
572 } | |
573 } | |
574 | |
575 // Parse categories. | |
576 if (!ParseCategories(value_dict)) | |
577 return false; | |
578 | |
579 // Parse author list. | |
580 if (!ParseAuthors(value_dict)) | |
581 return false; | |
582 | |
583 if (!ParseLinks(value_dict)) | |
584 return false; | |
585 | |
586 return true; | |
587 } | 384 } |
588 | 385 |
589 // static. | 386 // static |
590 DocumentEntry* DocumentEntry::CreateFrom(base::Value* value) { | 387 DocumentEntry* DocumentEntry::CreateFrom(base::Value* value) { |
591 if (!value || value->GetType() != Value::TYPE_DICTIONARY) | 388 base::JSONValueConverter<DocumentEntry> converter; |
592 return NULL; | |
593 | |
594 DictionaryValue* root = reinterpret_cast<DictionaryValue*>(value); | |
595 scoped_ptr<DocumentEntry> entry(new DocumentEntry()); | 389 scoped_ptr<DocumentEntry> entry(new DocumentEntry()); |
596 if (!entry->Parse(root)) { | 390 if (!converter.Convert(*value, entry.get())) { |
597 DVLOG(1) << "Invalid document entry!"; | 391 DVLOG(1) << "Invalid document entry!"; |
598 return NULL; | 392 return NULL; |
599 } | 393 } |
600 | 394 |
395 entry->FillRemainingFields(); | |
601 return entry.release(); | 396 return entry.release(); |
602 } | 397 } |
603 | 398 |
604 | 399 |
605 // static. | 400 bool DocumentFeed::Parse(base::Value* value) { |
401 base::JSONValueConverter<DocumentFeed> converter; | |
402 if (!converter.Convert(*value, this)) { | |
403 DVLOG(1) << "Invalid document feed!"; | |
404 return false; | |
405 } | |
406 | |
407 for (size_t i = 0; i < entries_.size(); ++i) { | |
408 entries_[i]->FillRemainingFields(); | |
409 } | |
410 return true; | |
411 } | |
412 | |
413 // static | |
606 DocumentFeed* DocumentFeed::CreateFrom(base::Value* value) { | 414 DocumentFeed* DocumentFeed::CreateFrom(base::Value* value) { |
607 if (!value || value->GetType() != Value::TYPE_DICTIONARY) | |
608 return NULL; | |
609 | |
610 DictionaryValue* root_entry_dict = | |
611 reinterpret_cast<DictionaryValue*>(value); | |
612 scoped_ptr<DocumentFeed> feed(new DocumentFeed()); | 415 scoped_ptr<DocumentFeed> feed(new DocumentFeed()); |
613 if (!feed->Parse(root_entry_dict)) { | 416 if (!feed->Parse(value)) { |
614 DVLOG(1) << "Invalid document feed!"; | 417 DVLOG(1) << "Invalid document feed!"; |
615 return NULL; | 418 return NULL; |
616 } | 419 } |
617 | 420 |
618 return feed.release(); | 421 return feed.release(); |
619 } | 422 } |
620 | 423 |
621 bool DocumentFeed::GetNextFeedURL(GURL* url) { | 424 bool DocumentFeed::GetNextFeedURL(GURL* url) { |
622 DCHECK(url); | 425 DCHECK(url); |
623 for (ScopedVector<Link>::iterator iter = links_.begin(); | 426 for (size_t i = 0; i < links_.size(); ++i) { |
624 iter != links_.end(); ++iter) { | 427 if (links_[i]->type() == Link::NEXT) { |
625 if ((*iter)->type() == Link::NEXT) { | 428 *url = links_[i]->href(); |
626 *url = (*iter)->href(); | |
627 return true; | 429 return true; |
628 } | 430 } |
629 } | 431 } |
630 return false; | 432 return false; |
631 } | 433 } |
632 | 434 |
633 } // namespace gdata | 435 } // namespace gdata |
OLD | NEW |