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 // 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
) {
37 if (reader
->NodeName() != "dict")
40 int dict_content_depth
= reader
->Depth() + 1;
41 // Advance past the dict node and into the body of the dictionary.
45 bool found_location
= false;
46 while (reader
->Depth() >= dict_content_depth
) {
47 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "key"))
49 std::string found_key
;
50 if (!reader
->ReadElementContent(&found_key
))
52 DCHECK_EQ(dict_content_depth
, reader
->Depth());
54 if (found_key
== "ImagePath") {
58 if (!iapps::ReadString(reader
, &value
))
60 result
->location
= base::FilePath(value
);
61 found_location
= true;
62 } else if (found_key
== "OriginalPath") {
64 if (!iapps::ReadString(reader
, &value
))
66 result
->original_location
= base::FilePath(value
);
68 if (!iapps::SkipToNextElement(reader
))
75 // Seek to the end of the dictionary
76 while (reader
->Depth() >= dict_content_depth
)
79 return found_location
;
82 // Contents of the album 'KeyList' key are
88 bool ReadStringArray(XmlReader
* reader
, std::set
<uint64
>* photo_ids
) {
89 if (reader
->NodeName() != "array")
92 // Advance past the array node and into the body of the array.
96 int array_content_depth
= reader
->Depth();
98 while (iapps::SeekToNodeAtCurrentDepth(reader
, "string")) {
99 if (reader
->Depth() != array_content_depth
)
101 std::string photo_id
;
102 if (!iapps::ReadString(reader
, &photo_id
))
105 if (!base::StringToUint64(photo_id
, &id
))
107 photo_ids
->insert(id
);
113 bool GetAlbumInfoFromDict(XmlReader
* reader
, AlbumInfo
* result
) {
115 if (reader
->NodeName() != "dict")
118 int dict_content_depth
= reader
->Depth() + 1;
119 // Advance past the dict node and into the body of the dictionary.
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"))
130 std::string found_key
;
131 if (!reader
->ReadElementContent(&found_key
))
133 DCHECK_EQ(dict_content_depth
, reader
->Depth());
135 if (found_key
== "AlbumId") {
138 if (!iapps::ReadInteger(reader
, &result
->id
))
141 } else if (found_key
== "AlbumName") {
144 if (!iapps::ReadString(reader
, &result
->name
))
147 } else if (found_key
== "KeyList") {
150 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "array"))
152 if (!ReadStringArray(reader
, &result
->photo_ids
))
154 found_contents
= true;
156 if (!iapps::SkipToNextElement(reader
))
163 // Seek to the end of the dictionary
164 while (reader
->Depth() >= dict_content_depth
)
167 return found_id
&& found_name
&& found_contents
;
170 // Inside the master image list, we expect photos to be arranged as
172 // <key>$PHOTO_ID</key>
176 // <key>$PHOTO_ID</key>
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"))
187 int photos_dict_depth
= reader
->Depth() + 1;
192 while (reader
->Depth() >= photos_dict_depth
) {
193 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "key"))
197 if (!reader
->ReadElementContent(&key
)) {
202 bool id_valid
= base::StringToUint64(key
, &id
);
205 reader
->Depth() != photos_dict_depth
) {
209 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "dict")) {
214 PhotoInfo photo_info
;
216 if (!GetPhotoInfoFromDict(reader
, &photo_info
)) {
221 parser::Photo
photo(photo_info
.id
, photo_info
.location
,
222 photo_info
.original_location
);
223 all_photos
->insert(photo
);
231 IPhotoLibraryParser::IPhotoLibraryParser() {}
232 IPhotoLibraryParser::~IPhotoLibraryParser() {}
234 bool IPhotoLibraryParser::Parse(const std::string
& library_xml
) {
236 if (!reader
.Load(library_xml
))
239 // Find the plist node and then search within that tag.
240 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "plist"))
245 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "dict"))
248 int dict_content_depth
= reader
.Depth() + 1;
249 // Advance past the dict node and into the body of the dictionary.
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"))
259 std::string found_key
;
260 if (!reader
.ReadElementContent(&found_key
))
262 DCHECK_EQ(dict_content_depth
, reader
.Depth());
264 if (found_key
== "List of Albums") {
269 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "array") ||
274 while (iapps::SeekToNodeAtCurrentDepth(&reader
, "dict")) {
275 AlbumInfo album_info
;
276 if (GetAlbumInfoFromDict(&reader
, &album_info
)) {
278 album
= album_info
.photo_ids
;
279 // Strip / from album name and dedupe any collisions.
281 base::ReplaceChars(album_info
.name
, "//", " ", &name
);
282 if (!ContainsKey(library_
.albums
, name
)) {
283 library_
.albums
[name
] = album
;
285 library_
.albums
[name
+"("+base::Uint64ToString(album_info
.id
)+")"] =
290 } else if (found_key
== "Master Image List") {
294 if (!ParseAllPhotos(&reader
, &library_
.all_photos
))
302 } // namespace iphoto