| 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 |