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
3 // found in the LICENSE file.
5 #include "chrome/utility/media_galleries/iphoto_library_parser.h"
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/utility/media_galleries/iapps_xml_utils.h"
14 #include "third_party/libxml/chromium/libxml_utils.h"
22 base::FilePath location;
23 base::FilePath original_location;
27 std::set<uint64> photo_ids;
32 class PhotosXmlDictReader : public iapps::XmlDictReader {
34 PhotosXmlDictReader(XmlReader* reader, PhotoInfo* photo_info)
35 : iapps::XmlDictReader(reader), photo_info_(photo_info) {}
37 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE {
38 if (key == "ImagePath") {
40 if (!iapps::ReadString(reader_, &value))
42 photo_info_->location = base::FilePath(value);
43 } else if (key == "OriginalPath") {
45 if (!iapps::ReadString(reader_, &value))
47 photo_info_->original_location = base::FilePath(value);
48 } else if (!SkipToNext()) {
54 virtual bool FinishedOk() OVERRIDE {
55 return Found("ImagePath");
59 PhotoInfo* photo_info_;
62 // Contents of the album 'KeyList' key are
68 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) {
69 if (reader->NodeName() != "array")
72 // Advance past the array node and into the body of the array.
76 int array_content_depth = reader->Depth();
78 while (iapps::SeekToNodeAtCurrentDepth(reader, "string")) {
79 if (reader->Depth() != array_content_depth)
82 if (!iapps::ReadString(reader, &photo_id))
85 if (!base::StringToUint64(photo_id, &id))
87 photo_ids->insert(id);
93 class AlbumXmlDictReader : public iapps::XmlDictReader {
95 AlbumXmlDictReader(XmlReader* reader, AlbumInfo* album_info)
96 : iapps::XmlDictReader(reader), album_info_(album_info) {}
98 virtual bool ShouldLoop() OVERRIDE {
99 return !(Found("AlbumId") && Found("AlbumName") && Found("KeyList"));
102 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE {
103 if (key == "AlbumId") {
104 if (!iapps::ReadInteger(reader_, &album_info_->id))
106 } else if (key == "AlbumName") {
107 if (!iapps::ReadString(reader_, &album_info_->name))
109 } else if (key == "KeyList") {
110 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array"))
112 if (!ReadStringArray(reader_, &album_info_->photo_ids))
114 } else if (!SkipToNext()) {
120 virtual bool FinishedOk() OVERRIDE {
121 return !ShouldLoop();
125 AlbumInfo* album_info_;
128 // Inside the master image list, we expect photos to be arranged as
130 // <key>$PHOTO_ID</key>
134 // <key>$PHOTO_ID</key>
140 // Returns true on success, false on error.
141 bool ParseAllPhotos(XmlReader* reader,
142 std::set<iphoto::parser::Photo>* all_photos) {
143 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict"))
145 int photos_dict_depth = reader->Depth() + 1;
150 while (reader->Depth() >= photos_dict_depth) {
151 if (!iapps::SeekToNodeAtCurrentDepth(reader, "key"))
155 if (!reader->ReadElementContent(&key)) {
160 bool id_valid = base::StringToUint64(key, &id);
163 reader->Depth() != photos_dict_depth) {
167 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) {
172 PhotoInfo 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()) {
183 parser::Photo photo(photo_info.id, photo_info.location,
184 photo_info.original_location);
185 all_photos->insert(photo);
193 IPhotoLibraryParser::IPhotoLibraryParser() {}
194 IPhotoLibraryParser::~IPhotoLibraryParser() {}
196 class IPhotoLibraryXmlDictReader : public iapps::XmlDictReader {
198 IPhotoLibraryXmlDictReader(XmlReader* reader, parser::Library* library)
199 : iapps::XmlDictReader(reader), library_(library), ok_(true) {}
201 virtual bool ShouldLoop() OVERRIDE {
202 return !(Found("List of Albums") && Found("Master Image List"));
205 virtual bool HandleKeyImpl(const std::string& key) OVERRIDE {
206 if (key == "List of Albums") {
207 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array") ||
211 while (iapps::SeekToNodeAtCurrentDepth(reader_, "dict")) {
212 AlbumInfo album_info;
213 AlbumXmlDictReader dict_reader(reader_, &album_info);
214 if (dict_reader.Read()) {
216 album = album_info.photo_ids;
217 // Strip / from album name and dedupe any collisions.
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;
225 } else if (key == "Master Image List") {
226 if (!ParseAllPhotos(reader_, &library_->all_photos)) {
234 virtual bool FinishedOk() OVERRIDE {
238 // The IPhotoLibrary allows duplicate "List of Albums" and
239 // "Master Image List" keys (although that seems odd.)
240 virtual bool AllowRepeats() OVERRIDE {
245 parser::Library* library_;
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.
253 bool IPhotoLibraryParser::Parse(const std::string& library_xml) {
255 if (!reader.Load(library_xml))
258 // Find the plist node and then search within that tag.
259 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist"))
264 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
267 IPhotoLibraryXmlDictReader dict_reader(&reader, &library_);
268 return dict_reader.Read();
271 } // namespace iphoto