Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / utility / media_galleries / iphoto_library_parser.cc
blobba98d8e0c9600f7eb8d5a7d733a7871885961280
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"
7 #include <string>
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"
16 namespace iphoto {
18 namespace {
20 struct PhotoInfo {
21 uint64 id;
22 base::FilePath location;
23 base::FilePath original_location;
26 struct AlbumInfo {
27 std::set<uint64> photo_ids;
28 std::string name;
29 uint64 id;
32 class PhotosXmlDictReader : public iapps::XmlDictReader {
33 public:
34 PhotosXmlDictReader(XmlReader* reader, PhotoInfo* photo_info)
35 : iapps::XmlDictReader(reader), photo_info_(photo_info) {}
37 bool HandleKeyImpl(const std::string& key) override {
38 if (key == "ImagePath") {
39 std::string value;
40 if (!iapps::ReadString(reader_, &value))
41 return false;
42 photo_info_->location = base::FilePath(value);
43 } else if (key == "OriginalPath") {
44 std::string value;
45 if (!iapps::ReadString(reader_, &value))
46 return false;
47 photo_info_->original_location = base::FilePath(value);
48 } else if (!SkipToNext()) {
49 return false;
51 return true;
54 bool FinishedOk() override { return Found("ImagePath"); }
56 private:
57 PhotoInfo* photo_info_;
60 // Contents of the album 'KeyList' key are
61 // <array>
62 // <string>1</string>
63 // <string>2</string>
64 // <string>3</string>
65 // </array>
66 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) {
67 if (reader->NodeName() != "array")
68 return false;
70 // Advance past the array node and into the body of the array.
71 if (!reader->Read())
72 return false;
74 int array_content_depth = reader->Depth();
76 while (iapps::SeekToNodeAtCurrentDepth(reader, "string")) {
77 if (reader->Depth() != array_content_depth)
78 return false;
79 std::string photo_id;
80 if (!iapps::ReadString(reader, &photo_id))
81 continue;
82 uint64 id;
83 if (!base::StringToUint64(photo_id, &id))
84 continue;
85 photo_ids->insert(id);
88 return true;
91 class AlbumXmlDictReader : public iapps::XmlDictReader {
92 public:
93 AlbumXmlDictReader(XmlReader* reader, AlbumInfo* album_info)
94 : iapps::XmlDictReader(reader), album_info_(album_info) {}
96 bool ShouldLoop() override {
97 return !(Found("AlbumId") && Found("AlbumName") && Found("KeyList"));
100 bool HandleKeyImpl(const std::string& key) override {
101 if (key == "AlbumId") {
102 if (!iapps::ReadInteger(reader_, &album_info_->id))
103 return false;
104 } else if (key == "AlbumName") {
105 if (!iapps::ReadString(reader_, &album_info_->name))
106 return false;
107 } else if (key == "KeyList") {
108 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array"))
109 return false;
110 if (!ReadStringArray(reader_, &album_info_->photo_ids))
111 return false;
112 } else if (!SkipToNext()) {
113 return false;
115 return true;
118 bool FinishedOk() override { return !ShouldLoop(); }
120 private:
121 AlbumInfo* album_info_;
124 // Inside the master image list, we expect photos to be arranged as
125 // <dict>
126 // <key>$PHOTO_ID</key>
127 // <dict>
128 // $photo properties
129 // </dict>
130 // <key>$PHOTO_ID</key>
131 // <dict>
132 // $photo properties
133 // </dict>
134 // ...
135 // </dict>
136 // Returns true on success, false on error.
137 bool ParseAllPhotos(XmlReader* reader,
138 std::set<iphoto::parser::Photo>* all_photos) {
139 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict"))
140 return false;
141 int photos_dict_depth = reader->Depth() + 1;
142 if (!reader->Read())
143 return false;
145 bool errors = false;
146 while (reader->Depth() >= photos_dict_depth) {
147 if (!iapps::SeekToNodeAtCurrentDepth(reader, "key"))
148 break;
150 std::string key;
151 if (!reader->ReadElementContent(&key)) {
152 errors = true;
153 break;
155 uint64 id;
156 bool id_valid = base::StringToUint64(key, &id);
158 if (!id_valid ||
159 reader->Depth() != photos_dict_depth) {
160 errors = true;
161 break;
163 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) {
164 errors = true;
165 break;
168 PhotoInfo photo_info;
169 photo_info.id = id;
170 // Walk through a dictionary filling in |result| with photo information.
171 // Return true if at least the location was found.
172 // In either case, the cursor is advanced out of the dictionary.
173 PhotosXmlDictReader dict_reader(reader, &photo_info);
174 if (!dict_reader.Read()) {
175 errors = true;
176 break;
179 parser::Photo photo(photo_info.id, photo_info.location,
180 photo_info.original_location);
181 all_photos->insert(photo);
184 return !errors;
187 } // namespace
189 IPhotoLibraryParser::IPhotoLibraryParser() {}
190 IPhotoLibraryParser::~IPhotoLibraryParser() {}
192 class IPhotoLibraryXmlDictReader : public iapps::XmlDictReader {
193 public:
194 IPhotoLibraryXmlDictReader(XmlReader* reader, parser::Library* library)
195 : iapps::XmlDictReader(reader), library_(library), ok_(true) {}
197 bool ShouldLoop() override {
198 return !(Found("List of Albums") && Found("Master Image List"));
201 bool HandleKeyImpl(const std::string& key) override {
202 if (key == "List of Albums") {
203 if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array") ||
204 !reader_->Read()) {
205 return true;
207 while (iapps::SeekToNodeAtCurrentDepth(reader_, "dict")) {
208 AlbumInfo album_info;
209 AlbumXmlDictReader dict_reader(reader_, &album_info);
210 if (dict_reader.Read()) {
211 parser::Album album;
212 album = album_info.photo_ids;
213 // Strip / from album name and dedupe any collisions.
214 std::string name;
215 base::ReplaceChars(album_info.name, "//", " ", &name);
216 if (ContainsKey(library_->albums, name))
217 name = name + "("+base::Uint64ToString(album_info.id)+")";
218 library_->albums[name] = album;
221 } else if (key == "Master Image List") {
222 if (!ParseAllPhotos(reader_, &library_->all_photos)) {
223 ok_ = false;
224 return false;
227 return true;
230 bool FinishedOk() override { return ok_; }
232 // The IPhotoLibrary allows duplicate "List of Albums" and
233 // "Master Image List" keys (although that seems odd.)
234 bool AllowRepeats() override { return true; }
236 private:
237 parser::Library* library_;
239 // The base class bails when we request, and then calls |FinishedOk()|
240 // to decide what to return. We need to remember that we bailed because
241 // of an error. That's what |ok_| does.
242 bool ok_;
245 bool IPhotoLibraryParser::Parse(const std::string& library_xml) {
246 XmlReader reader;
247 if (!reader.Load(library_xml))
248 return false;
250 // Find the plist node and then search within that tag.
251 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist"))
252 return false;
253 if (!reader.Read())
254 return false;
256 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
257 return false;
259 IPhotoLibraryXmlDictReader dict_reader(&reader, &library_);
260 return dict_reader.Read();
263 } // namespace iphoto