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 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array") || |
| 208 !reader_->Read()) { |
| 209 return true; |
| 210 } |
| 211 while (iapps::SeekToNodeAtCurrentDepth(reader_, "dict")) { |
| 212 AlbumInfo album_info; |
| 213 AlbumXmlDictReader dict_reader(reader_, &album_info); |
| 214 if (dict_reader.Read()) { |
| 215 parser::Album album; |
| 216 album = album_info.photo_ids; |
| 217 // Strip / from album name and dedupe any collisions. |
| 218 std::string name; |
| 219 base::ReplaceChars(album_info.name, "//", " ", &name); |
| 220 if (ContainsKey(library_->albums, name)) |
| 221 name = name + "("+base::Uint64ToString(album_info.id)+")"; |
| 222 library_->albums[name] = album; |
| 223 } |
| 224 } |
| 225 } else if (key == "Master Image List") { |
| 226 if (!ParseAllPhotos(reader_, &library_->all_photos)) { |
| 227 ok_ = false; |
| 228 return false; |
| 229 } |
| 230 } |
| 231 return true; |
| 232 } |
| 233 |
| 234 virtual bool FinishedOk() OVERRIDE { |
| 235 return ok_; |
| 236 } |
| 237 |
| 238 // The IPhotoLibrary allows duplicate "List of Albums" and |
| 239 // "Master Image List" keys (although that seems odd.) |
| 240 virtual bool AllowRepeats() OVERRIDE { |
| 241 return true; |
| 242 } |
| 243 |
| 244 private: |
| 245 parser::Library* library_; |
| 246 |
| 247 // The base class bails when we request, and then calls |FinishedOk()| |
| 248 // to decide what to return. We need to remember that we bailed because |
| 249 // of an error. That's what |ok_| does. |
| 250 bool ok_; |
| 251 }; |
| 252 |
234 bool IPhotoLibraryParser::Parse(const std::string& library_xml) { | 253 bool IPhotoLibraryParser::Parse(const std::string& library_xml) { |
235 XmlReader reader; | 254 XmlReader reader; |
236 if (!reader.Load(library_xml)) | 255 if (!reader.Load(library_xml)) |
237 return false; | 256 return false; |
238 | 257 |
239 // Find the plist node and then search within that tag. | 258 // Find the plist node and then search within that tag. |
240 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist")) | 259 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist")) |
241 return false; | 260 return false; |
242 if (!reader.Read()) | 261 if (!reader.Read()) |
243 return false; | 262 return false; |
244 | 263 |
245 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict")) | 264 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict")) |
246 return false; | 265 return false; |
247 | 266 |
248 int dict_content_depth = reader.Depth() + 1; | 267 IPhotoLibraryXmlDictReader dict_reader(&reader, &library_); |
249 // Advance past the dict node and into the body of the dictionary. | 268 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 } | 269 } |
301 | 270 |
302 } // namespace iphoto | 271 } // namespace iphoto |
OLD | NEW |