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 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 bool FinishedOk() override
{ return Found("ImagePath"); }
57 PhotoInfo
* photo_info_
;
60 // Contents of the album 'KeyList' key are
66 bool ReadStringArray(XmlReader
* reader
, std::set
<uint64
>* photo_ids
) {
67 if (reader
->NodeName() != "array")
70 // Advance past the array node and into the body of the array.
74 int array_content_depth
= reader
->Depth();
76 while (iapps::SeekToNodeAtCurrentDepth(reader
, "string")) {
77 if (reader
->Depth() != array_content_depth
)
80 if (!iapps::ReadString(reader
, &photo_id
))
83 if (!base::StringToUint64(photo_id
, &id
))
85 photo_ids
->insert(id
);
91 class AlbumXmlDictReader
: public iapps::XmlDictReader
{
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
))
104 } else if (key
== "AlbumName") {
105 if (!iapps::ReadString(reader_
, &album_info_
->name
))
107 } else if (key
== "KeyList") {
108 if (!iapps::SeekToNodeAtCurrentDepth(reader_
, "array"))
110 if (!ReadStringArray(reader_
, &album_info_
->photo_ids
))
112 } else if (!SkipToNext()) {
118 bool FinishedOk() override
{ return !ShouldLoop(); }
121 AlbumInfo
* album_info_
;
124 // Inside the master image list, we expect photos to be arranged as
126 // <key>$PHOTO_ID</key>
130 // <key>$PHOTO_ID</key>
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"))
141 int photos_dict_depth
= reader
->Depth() + 1;
146 while (reader
->Depth() >= photos_dict_depth
) {
147 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "key"))
151 if (!reader
->ReadElementContent(&key
)) {
156 bool id_valid
= base::StringToUint64(key
, &id
);
159 reader
->Depth() != photos_dict_depth
) {
163 if (!iapps::SeekToNodeAtCurrentDepth(reader
, "dict")) {
168 PhotoInfo photo_info
;
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()) {
179 parser::Photo
photo(photo_info
.id
, photo_info
.location
,
180 photo_info
.original_location
);
181 all_photos
->insert(photo
);
189 IPhotoLibraryParser::IPhotoLibraryParser() {}
190 IPhotoLibraryParser::~IPhotoLibraryParser() {}
192 class IPhotoLibraryXmlDictReader
: public iapps::XmlDictReader
{
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") ||
207 while (iapps::SeekToNodeAtCurrentDepth(reader_
, "dict")) {
208 AlbumInfo album_info
;
209 AlbumXmlDictReader
dict_reader(reader_
, &album_info
);
210 if (dict_reader
.Read()) {
212 album
= album_info
.photo_ids
;
213 // Strip / from album name and dedupe any collisions.
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
)) {
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; }
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.
245 bool IPhotoLibraryParser::Parse(const std::string
& library_xml
) {
247 if (!reader
.Load(library_xml
))
250 // Find the plist node and then search within that tag.
251 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "plist"))
256 if (!iapps::SeekToNodeAtCurrentDepth(&reader
, "dict"))
259 IPhotoLibraryXmlDictReader
dict_reader(&reader
, &library_
);
260 return dict_reader
.Read();
263 } // namespace iphoto