Componentize AccountReconcilor.
[chromium-blink-merge.git] / chrome / utility / media_galleries / iphoto_library_parser.cc
blobcd52a6b647765eeae60c47af4600d830c25ba357
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 // Walk through a dictionary filling in |result| with photo information. Return
33 // true if at least the id and location were found.
34 // In either case, the cursor is advanced out of the dictionary.
35 bool GetPhotoInfoFromDict(XmlReader* reader, PhotoInfo* result) {
36 DCHECK(result);
37 if (reader->NodeName() != "dict")
38 return false;
40 int dict_content_depth = reader->Depth() + 1;
41 // Advance past the dict node and into the body of the dictionary.
42 if (!reader->Read())
43 return false;
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());
54 if (found_key == "ImagePath") {
55 if (found_location)
56 break;
57 std::string value;
58 if (!iapps::ReadString(reader, &value))
59 break;
60 result->location = base::FilePath(value);
61 found_location = true;
62 } else if (found_key == "OriginalPath") {
63 std::string value;
64 if (!iapps::ReadString(reader, &value))
65 break;
66 result->original_location = base::FilePath(value);
67 } else {
68 if (!iapps::SkipToNextElement(reader))
69 break;
70 if (!reader->Next())
71 break;
75 // Seek to the end of the dictionary
76 while (reader->Depth() >= dict_content_depth)
77 reader->Next();
79 return found_location;
82 // Contents of the album 'KeyList' key are
83 // <array>
84 // <string>1</string>
85 // <string>2</string>
86 // <string>3</string>
87 // </array>
88 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) {
89 if (reader->NodeName() != "array")
90 return false;
92 // Advance past the array node and into the body of the array.
93 if (!reader->Read())
94 return false;
96 int array_content_depth = reader->Depth();
98 while (iapps::SeekToNodeAtCurrentDepth(reader, "string")) {
99 if (reader->Depth() != array_content_depth)
100 return false;
101 std::string photo_id;
102 if (!iapps::ReadString(reader, &photo_id))
103 continue;
104 uint64 id;
105 if (!base::StringToUint64(photo_id, &id))
106 continue;
107 photo_ids->insert(id);
110 return true;
113 bool GetAlbumInfoFromDict(XmlReader* reader, AlbumInfo* result) {
114 DCHECK(result);
115 if (reader->NodeName() != "dict")
116 return false;
118 int dict_content_depth = reader->Depth() + 1;
119 // Advance past the dict node and into the body of the dictionary.
120 if (!reader->Read())
121 return false;
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());
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;
163 // Seek to the end of the dictionary
164 while (reader->Depth() >= dict_content_depth)
165 reader->Next();
167 return found_id && found_name && found_contents;
170 // Inside the master image list, we expect photos to be arranged as
171 // <dict>
172 // <key>$PHOTO_ID</key>
173 // <dict>
174 // $photo properties
175 // </dict>
176 // <key>$PHOTO_ID</key>
177 // <dict>
178 // $photo properties
179 // </dict>
180 // ...
181 // </dict>
182 // Returns true on success, false on error.
183 bool ParseAllPhotos(XmlReader* reader,
184 std::set<iphoto::parser::Photo>* all_photos) {
185 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict"))
186 return false;
187 int photos_dict_depth = reader->Depth() + 1;
188 if (!reader->Read())
189 return false;
191 bool errors = false;
192 while (reader->Depth() >= photos_dict_depth) {
193 if (!iapps::SeekToNodeAtCurrentDepth(reader, "key"))
194 break;
196 std::string key;
197 if (!reader->ReadElementContent(&key)) {
198 errors = true;
199 break;
201 uint64 id;
202 bool id_valid = base::StringToUint64(key, &id);
204 if (!id_valid ||
205 reader->Depth() != photos_dict_depth) {
206 errors = true;
207 break;
209 if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) {
210 errors = true;
211 break;
214 PhotoInfo photo_info;
215 photo_info.id = id;
216 if (!GetPhotoInfoFromDict(reader, &photo_info)) {
217 errors = true;
218 break;
221 parser::Photo photo(photo_info.id, photo_info.location,
222 photo_info.original_location);
223 all_photos->insert(photo);
226 return !errors;
229 } // namespace
231 IPhotoLibraryParser::IPhotoLibraryParser() {}
232 IPhotoLibraryParser::~IPhotoLibraryParser() {}
234 bool IPhotoLibraryParser::Parse(const std::string& library_xml) {
235 XmlReader reader;
236 if (!reader.Load(library_xml))
237 return false;
239 // Find the plist node and then search within that tag.
240 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist"))
241 return false;
242 if (!reader.Read())
243 return false;
245 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
246 return false;
248 int dict_content_depth = reader.Depth() + 1;
249 // Advance past the dict node and into the body of the dictionary.
250 if (!reader.Read())
251 return false;
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());
264 if (found_key == "List of Albums") {
265 if (found_albums)
266 continue;
267 found_albums = true;
269 if (!iapps::SeekToNodeAtCurrentDepth(&reader, "array") ||
270 !reader.Read()) {
271 continue;
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;
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;
299 return true;
302 } // namespace iphoto