OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/utility/media_galleries/iphoto_library_parser.h" | 5 #include "chrome/utility/media_galleries/iphoto_library_parser.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
22 base::FilePath location; | 22 base::FilePath location; |
23 base::FilePath original_location; | 23 base::FilePath original_location; |
24 }; | 24 }; |
25 | 25 |
26 struct AlbumInfo { | 26 struct AlbumInfo { |
27 std::set<uint64> photo_ids; | 27 std::set<uint64> photo_ids; |
28 std::string name; | 28 std::string name; |
29 uint64 id; | 29 uint64 id; |
30 }; | 30 }; |
31 | 31 |
32 // Walk through a dictionary filling in |result| with photo information. Return | 32 class PhotosXmlDictReader : public iapps::XmlDictReader { |
33 // true if at least the id and location were found. | 33 public: |
34 // In either case, the cursor is advanced out of the dictionary. | 34 PhotosXmlDictReader(XmlReader* reader, PhotoInfo* photo_info) |
35 bool GetPhotoInfoFromDict(XmlReader* reader, PhotoInfo* result) { | 35 : iapps::XmlDictReader(reader), photo_info_(photo_info) {} |
36 DCHECK(result); | |
37 if (reader->NodeName() != "dict") | |
38 return false; | |
39 | 36 |
40 int dict_content_depth = reader->Depth() + 1; | 37 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE { |
41 // Advance past the dict node and into the body of the dictionary. | 38 if (key == "ImagePath") { |
42 if (!reader->Read()) | |
43 return false; | |
44 | |
45 bool found_location = false; | |
46 while (reader->Depth() >= dict_content_depth) { | |
47 if (!iapps::SeekToNodeAtCurrentDepth(reader, "key")) | |
48 break; | |
49 std::string found_key; | |
50 if (!reader->ReadElementContent(&found_key)) | |
51 break; | |
52 DCHECK_EQ(dict_content_depth, reader->Depth()); | |
53 | |
54 if (found_key == "ImagePath") { | |
55 if (found_location) | |
56 break; | |
57 std::string value; | 39 std::string value; |
58 if (!iapps::ReadString(reader, &value)) | 40 if (!iapps::ReadString(reader_, &value)) |
59 break; | 41 return false; |
60 result->location = base::FilePath(value); | 42 photo_info_->location = base::FilePath(value); |
61 found_location = true; | 43 } else if (key == "OriginalPath") { |
62 } else if (found_key == "OriginalPath") { | |
63 std::string value; | 44 std::string value; |
64 if (!iapps::ReadString(reader, &value)) | 45 if (!iapps::ReadString(reader_, &value)) |
65 break; | 46 return false; |
66 result->original_location = base::FilePath(value); | 47 photo_info_->original_location = base::FilePath(value); |
67 } else { | 48 } else if (!SkipToNext()) { |
68 if (!iapps::SkipToNextElement(reader)) | 49 return false; |
69 break; | |
70 if (!reader->Next()) | |
71 break; | |
72 } | 50 } |
51 return true; | |
73 } | 52 } |
74 | 53 |
75 // Seek to the end of the dictionary | 54 virtual bool FinishedOk() OVERRIDE { |
76 while (reader->Depth() >= dict_content_depth) | 55 return Found("ImagePath"); |
77 reader->Next(); | 56 } |
78 | 57 |
79 return found_location; | 58 private: |
80 } | 59 PhotoInfo* photo_info_; |
60 }; | |
81 | 61 |
82 // Contents of the album 'KeyList' key are | 62 // Contents of the album 'KeyList' key are |
83 // <array> | 63 // <array> |
84 // <string>1</string> | 64 // <string>1</string> |
85 // <string>2</string> | 65 // <string>2</string> |
86 // <string>3</string> | 66 // <string>3</string> |
87 // </array> | 67 // </array> |
88 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) { | 68 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) { |
89 if (reader->NodeName() != "array") | 69 if (reader->NodeName() != "array") |
90 return false; | 70 return false; |
(...skipping 12 matching lines...) Expand all Loading... | |
103 continue; | 83 continue; |
104 uint64 id; | 84 uint64 id; |
105 if (!base::StringToUint64(photo_id, &id)) | 85 if (!base::StringToUint64(photo_id, &id)) |
106 continue; | 86 continue; |
107 photo_ids->insert(id); | 87 photo_ids->insert(id); |
108 } | 88 } |
109 | 89 |
110 return true; | 90 return true; |
111 } | 91 } |
112 | 92 |
113 bool GetAlbumInfoFromDict(XmlReader* reader, AlbumInfo* result) { | 93 class AlbumXmlDictReader : public iapps::XmlDictReader { |
114 DCHECK(result); | 94 public: |
115 if (reader->NodeName() != "dict") | 95 AlbumXmlDictReader(XmlReader* reader, AlbumInfo* album_info) |
116 return false; | 96 : iapps::XmlDictReader(reader), album_info_(album_info) {} |
117 | 97 |
118 int dict_content_depth = reader->Depth() + 1; | 98 virtual bool ShouldLoop() OVERRIDE { |
119 // Advance past the dict node and into the body of the dictionary. | 99 return !(Found("AlbumId") && Found("AlbumName") && Found("KeyList")); |
120 if (!reader->Read()) | |
121 return false; | |
122 | |
123 bool found_id = false; | |
124 bool found_name = false; | |
125 bool found_contents = false; | |
126 while (reader->Depth() >= dict_content_depth && | |
127 !(found_id && found_name && found_contents)) { | |
128 if (!iapps::SeekToNodeAtCurrentDepth(reader, "key")) | |
129 break; | |
130 std::string found_key; | |
131 if (!reader->ReadElementContent(&found_key)) | |
132 break; | |
133 DCHECK_EQ(dict_content_depth, reader->Depth()); | |
134 | |
135 if (found_key == "AlbumId") { | |
136 if (found_id) | |
137 break; | |
138 if (!iapps::ReadInteger(reader, &result->id)) | |
139 break; | |
140 found_id = true; | |
141 } else if (found_key == "AlbumName") { | |
142 if (found_name) | |
143 break; | |
144 if (!iapps::ReadString(reader, &result->name)) | |
145 break; | |
146 found_name = true; | |
147 } else if (found_key == "KeyList") { | |
148 if (found_contents) | |
149 break; | |
150 if (!iapps::SeekToNodeAtCurrentDepth(reader, "array")) | |
151 break; | |
152 if (!ReadStringArray(reader, &result->photo_ids)) | |
153 break; | |
154 found_contents = true; | |
155 } else { | |
156 if (!iapps::SkipToNextElement(reader)) | |
157 break; | |
158 if (!reader->Next()) | |
159 break; | |
160 } | |
161 } | 100 } |
162 | 101 |
163 // Seek to the end of the dictionary | 102 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE { |
164 while (reader->Depth() >= dict_content_depth) | 103 if (key == "AlbumId") { |
165 reader->Next(); | 104 if (!iapps::ReadInteger(reader_, &album_info_->id)) |
105 return false; | |
106 } else if (key == "AlbumName") { | |
107 if (!iapps::ReadString(reader_, &album_info_->name)) | |
108 return false; | |
109 } else if (key == "KeyList") { | |
110 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array")) | |
111 return false; | |
112 if (!ReadStringArray(reader_, &album_info_->photo_ids)) | |
113 return false; | |
114 } else if (!SkipToNext()) { | |
115 return false; | |
116 } | |
117 return true; | |
118 } | |
166 | 119 |
167 return found_id && found_name && found_contents; | 120 virtual bool FinishedOk() OVERRIDE { |
168 } | 121 return !ShouldLoop(); |
122 } | |
123 | |
124 private: | |
125 AlbumInfo* album_info_; | |
126 }; | |
169 | 127 |
170 // Inside the master image list, we expect photos to be arranged as | 128 // Inside the master image list, we expect photos to be arranged as |
171 // <dict> | 129 // <dict> |
172 // <key>$PHOTO_ID</key> | 130 // <key>$PHOTO_ID</key> |
173 // <dict> | 131 // <dict> |
174 // $photo properties | 132 // $photo properties |
175 // </dict> | 133 // </dict> |
176 // <key>$PHOTO_ID</key> | 134 // <key>$PHOTO_ID</key> |
177 // <dict> | 135 // <dict> |
178 // $photo properties | 136 // $photo properties |
(...skipping 27 matching lines...) Expand all Loading... | |
206 errors = true; | 164 errors = true; |
207 break; | 165 break; |
208 } | 166 } |
209 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) { | 167 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) { |
210 errors = true; | 168 errors = true; |
211 break; | 169 break; |
212 } | 170 } |
213 | 171 |
214 PhotoInfo photo_info; | 172 PhotoInfo photo_info; |
215 photo_info.id = id; | 173 photo_info.id = id; |
216 if (!GetPhotoInfoFromDict(reader, &photo_info)) { | 174 // Walk through a dictionary filling in |result| with photo information. |
175 // Return true if at least the location was found. | |
176 // In either case, the cursor is advanced out of the dictionary. | |
177 PhotosXmlDictReader dict_reader(reader, &photo_info); | |
178 if (!dict_reader.Read()) { | |
217 errors = true; | 179 errors = true; |
218 break; | 180 break; |
219 } | 181 } |
220 | 182 |
221 parser::Photo photo(photo_info.id, photo_info.location, | 183 parser::Photo photo(photo_info.id, photo_info.location, |
222 photo_info.original_location); | 184 photo_info.original_location); |
223 all_photos->insert(photo); | 185 all_photos->insert(photo); |
224 } | 186 } |
225 | 187 |
226 return !errors; | 188 return !errors; |
227 } | 189 } |
228 | 190 |
229 } // namespace | 191 } // namespace |
230 | 192 |
231 IPhotoLibraryParser::IPhotoLibraryParser() {} | 193 IPhotoLibraryParser::IPhotoLibraryParser() {} |
232 IPhotoLibraryParser::~IPhotoLibraryParser() {} | 194 IPhotoLibraryParser::~IPhotoLibraryParser() {} |
233 | 195 |
196 class IPhotoLibraryXmlDictReader : public iapps::XmlDictReader { | |
197 public: | |
198 IPhotoLibraryXmlDictReader(XmlReader* reader, parser::Library* library) | |
199 : iapps::XmlDictReader(reader), library_(library), ok_(true) {} | |
200 | |
201 virtual bool ShouldLoop() OVERRIDE { | |
202 return !(Found("List of Albums") && Found("Master Image List")); | |
203 } | |
204 | |
205 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE { | |
206 if (key == "List of Albums") { | |
207 | |
Lei Zhang
2014/08/05 19:38:24
nit: no empty lines at the beginning of a block.
Kevin Bailey
2014/08/05 21:52:06
Done.
| |
208 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array") || | |
209 !reader_->Read()) { | |
210 return true; | |
211 } | |
212 while (iapps::SeekToNodeAtCurrentDepth(reader_, "dict")) { | |
213 AlbumInfo album_info; | |
214 AlbumXmlDictReader dict_reader(reader_, &album_info); | |
215 if (dict_reader.Read()) { | |
216 parser::Album album; | |
217 album = album_info.photo_ids; | |
218 // Strip / from album name and dedupe any collisions. | |
219 std::string name; | |
220 base::ReplaceChars(album_info.name, "//", " ", &name); | |
221 if (!ContainsKey(library_->albums, name)) { | |
Lei Zhang
2014/08/05 19:38:24
This block can be shortened to:
if (ContainsKey(l
Kevin Bailey
2014/08/05 21:52:05
Done.
| |
222 library_->albums[name] = album; | |
223 } else { | |
224 library_->albums[name+"("+base::Uint64ToString(album_info.id)+")"] = | |
225 album; | |
226 } | |
227 } | |
228 } | |
229 } else if (key == "Master Image List") { | |
230 if (!ParseAllPhotos(reader_, &library_->all_photos)) { | |
231 ok_ = false; | |
232 return false; | |
233 } | |
234 } | |
235 return true; | |
236 } | |
237 | |
238 virtual bool FinishedOk() OVERRIDE { | |
239 return ok_; | |
240 } | |
241 | |
242 // The IPhotoLibrary allows duplicate "List of Albums" and | |
243 // "Master Image List" keys (although that seems odd.) | |
244 virtual bool AllowRepeats() OVERRIDE { | |
245 return true; | |
246 } | |
247 | |
248 private: | |
249 parser::Library* library_; | |
Lei Zhang
2014/08/05 19:38:24
nit: blank line after
Kevin Bailey
2014/08/05 21:52:06
Done.
| |
250 // The base class bails when we request, and then calls |FinishedOk()| | |
251 // to decide what to return. We need to remember that we bailed because | |
252 // of an error. That's what |ok_| does. | |
253 bool ok_; | |
254 }; | |
255 | |
234 bool IPhotoLibraryParser::Parse(const std::string& library_xml) { | 256 bool IPhotoLibraryParser::Parse(const std::string& library_xml) { |
235 XmlReader reader; | 257 XmlReader reader; |
236 if (!reader.Load(library_xml)) | 258 if (!reader.Load(library_xml)) |
237 return false; | 259 return false; |
238 | 260 |
239 // Find the plist node and then search within that tag. | 261 // Find the plist node and then search within that tag. |
240 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist")) | 262 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist")) |
241 return false; | 263 return false; |
242 if (!reader.Read()) | 264 if (!reader.Read()) |
243 return false; | 265 return false; |
244 | 266 |
245 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict")) | 267 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict")) |
246 return false; | 268 return false; |
247 | 269 |
248 int dict_content_depth = reader.Depth() + 1; | 270 IPhotoLibraryXmlDictReader dict_reader(&reader, &library_); |
249 // Advance past the dict node and into the body of the dictionary. | 271 return dict_reader.Read(); |
250 if (!reader.Read()) | |
251 return false; | |
252 | |
253 bool found_photos = false; | |
254 bool found_albums = false; | |
255 while (reader.Depth() >= dict_content_depth && | |
256 !(found_photos && found_albums)) { | |
257 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "key")) | |
258 break; | |
259 std::string found_key; | |
260 if (!reader.ReadElementContent(&found_key)) | |
261 break; | |
262 DCHECK_EQ(dict_content_depth, reader.Depth()); | |
263 | |
264 if (found_key == "List of Albums") { | |
265 if (found_albums) | |
266 continue; | |
267 found_albums = true; | |
268 | |
269 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "array") || | |
270 !reader.Read()) { | |
271 continue; | |
272 } | |
273 | |
274 while (iapps::SeekToNodeAtCurrentDepth(&reader, "dict")) { | |
275 AlbumInfo album_info; | |
276 if (GetAlbumInfoFromDict(&reader, &album_info)) { | |
277 parser::Album album; | |
278 album = album_info.photo_ids; | |
279 // Strip / from album name and dedupe any collisions. | |
280 std::string name; | |
281 base::ReplaceChars(album_info.name, "//", " ", &name); | |
282 if (!ContainsKey(library_.albums, name)) { | |
283 library_.albums[name] = album; | |
284 } else { | |
285 library_.albums[name+"("+base::Uint64ToString(album_info.id)+")"] = | |
286 album; | |
287 } | |
288 } | |
289 } | |
290 } else if (found_key == "Master Image List") { | |
291 if (found_photos) | |
292 continue; | |
293 found_photos = true; | |
294 if (!ParseAllPhotos(&reader, &library_.all_photos)) | |
295 return false; | |
296 } | |
297 } | |
298 | |
299 return true; | |
300 } | 272 } |
301 | 273 |
302 } // namespace iphoto | 274 } // namespace iphoto |
OLD | NEW |