Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / utility / media_galleries / iphoto_library_parser.cc
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.
4
5 #include "chrome/utility/media_galleries/iphoto_library_parser.h"
6
7 #include <string>
8
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"
15
16 namespace iphoto {
17
18 namespace {
19
20 struct PhotoInfo {
21   uint64 id;
22   base::FilePath location;
23   base::FilePath original_location;
24 };
25
26 struct AlbumInfo {
27   std::set<uint64> photo_ids;
28   std::string name;
29   uint64 id;
30 };
31
32 class PhotosXmlDictReader : public iapps::XmlDictReader {
33  public:
34   PhotosXmlDictReader(XmlReader* reader, PhotoInfo* photo_info)
35     : iapps::XmlDictReader(reader), photo_info_(photo_info) {}
36
37   virtual 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;
50     }
51     return true;
52   }
53
54   virtual bool FinishedOk() OVERRIDE {
55     return Found("ImagePath");
56   }
57
58  private:
59   PhotoInfo* photo_info_;
60 };
61
62 // Contents of the album 'KeyList' key are
63 // <array>
64 //  <string>1</string>
65 //  <string>2</string>
66 //  <string>3</string>
67 // </array>
68 bool ReadStringArray(XmlReader* reader, std::set<uint64>* photo_ids) {
69   if (reader->NodeName() != "array")
70     return false;
71
72   // Advance past the array node and into the body of the array.
73   if (!reader->Read())
74     return false;
75
76   int array_content_depth = reader->Depth();
77
78   while (iapps::SeekToNodeAtCurrentDepth(reader, "string")) {
79     if (reader->Depth() != array_content_depth)
80       return false;
81     std::string photo_id;
82     if (!iapps::ReadString(reader, &photo_id))
83       continue;
84     uint64 id;
85     if (!base::StringToUint64(photo_id, &id))
86       continue;
87     photo_ids->insert(id);
88   }
89
90   return true;
91 }
92
93 class AlbumXmlDictReader : public iapps::XmlDictReader {
94  public:
95   AlbumXmlDictReader(XmlReader* reader, AlbumInfo* album_info)
96     : iapps::XmlDictReader(reader), album_info_(album_info) {}
97
98   virtual bool ShouldLoop() OVERRIDE {
99     return !(Found("AlbumId") && Found("AlbumName") && Found("KeyList"));
100   }
101
102   virtual bool HandleKeyImpl(const std::string& key) OVERRIDE {
103     if (key == "AlbumId") {
104       if (!iapps::ReadInteger(reader_, &album_info_->id))
105         return false;
106     } else if (key == "AlbumName") {
107       if (!iapps::ReadString(reader_, &album_info_->name))
108         return false;
109     } else if (key == "KeyList") {
110       if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array"))
111         return false;
112       if (!ReadStringArray(reader_, &album_info_->photo_ids))
113         return false;
114     } else if (!SkipToNext()) {
115       return false;
116     }
117     return true;
118   }
119
120   virtual bool FinishedOk() OVERRIDE {
121     return !ShouldLoop();
122   }
123
124  private:
125   AlbumInfo* album_info_;
126 };
127
128 // Inside the master image list, we expect photos to be arranged as
129 //  <dict>
130 //   <key>$PHOTO_ID</key>
131 //   <dict>
132 //     $photo properties
133 //   </dict>
134 //   <key>$PHOTO_ID</key>
135 //   <dict>
136 //     $photo properties
137 //   </dict>
138 //   ...
139 //  </dict>
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"))
144     return false;
145   int photos_dict_depth = reader->Depth() + 1;
146   if (!reader->Read())
147     return false;
148
149   bool errors = false;
150   while (reader->Depth() >= photos_dict_depth) {
151     if (!iapps::SeekToNodeAtCurrentDepth(reader, "key"))
152       break;
153
154     std::string key;
155     if (!reader->ReadElementContent(&key)) {
156       errors = true;
157       break;
158     }
159     uint64 id;
160     bool id_valid = base::StringToUint64(key, &id);
161
162     if (!id_valid ||
163         reader->Depth() != photos_dict_depth) {
164       errors = true;
165       break;
166     }
167     if (!iapps::SeekToNodeAtCurrentDepth(reader, "dict")) {
168       errors = true;
169       break;
170     }
171
172     PhotoInfo photo_info;
173     photo_info.id = id;
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()) {
179       errors = true;
180       break;
181     }
182
183     parser::Photo photo(photo_info.id, photo_info.location,
184                         photo_info.original_location);
185     all_photos->insert(photo);
186   }
187
188   return !errors;
189 }
190
191 }  // namespace
192
193 IPhotoLibraryParser::IPhotoLibraryParser() {}
194 IPhotoLibraryParser::~IPhotoLibraryParser() {}
195
196 class IPhotoLibraryXmlDictReader : public iapps::XmlDictReader {
197  public:
198   IPhotoLibraryXmlDictReader(XmlReader* reader, parser::Library* library)
199     : iapps::XmlDictReader(reader), library_(library), ok_(true) {}
200
201   virtual bool ShouldLoop() OVERRIDE {
202     return !(Found("List of Albums") && Found("Master Image List"));
203   }
204
205   virtual bool HandleKeyImpl(const std::string& key) OVERRIDE {
206     if (key == "List of Albums") {
207       if (!iapps::SeekToNodeAtCurrentDepth(reader_, "array") ||
208           !reader_->Read()) {
209         return true;
210       }
211       while (iapps::SeekToNodeAtCurrentDepth(reader_, "dict")) {
212         AlbumInfo album_info;
213         AlbumXmlDictReader dict_reader(reader_, &album_info);
214         if (dict_reader.Read()) {
215           parser::Album album;
216           album = album_info.photo_ids;
217           // Strip / from album name and dedupe any collisions.
218           std::string name;
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;
223         }
224       }
225     } else if (key == "Master Image List") {
226       if (!ParseAllPhotos(reader_, &library_->all_photos)) {
227         ok_ = false;
228         return false;
229       }
230     }
231     return true;
232   }
233
234   virtual bool FinishedOk() OVERRIDE {
235     return ok_;
236   }
237
238   // The IPhotoLibrary allows duplicate "List of Albums" and
239   // "Master Image List" keys (although that seems odd.)
240   virtual bool AllowRepeats() OVERRIDE {
241     return true;
242   }
243
244  private:
245   parser::Library* library_;
246
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.
250   bool ok_;
251 };
252
253 bool IPhotoLibraryParser::Parse(const std::string& library_xml) {
254   XmlReader reader;
255   if (!reader.Load(library_xml))
256     return false;
257
258   // Find the plist node and then search within that tag.
259   if (!iapps::SeekToNodeAtCurrentDepth(&reader, "plist"))
260     return false;
261   if (!reader.Read())
262     return false;
263
264   if (!iapps::SeekToNodeAtCurrentDepth(&reader, "dict"))
265     return false;
266
267   IPhotoLibraryXmlDictReader dict_reader(&reader, &library_);
268   return dict_reader.Read();
269 }
270
271 }  // namespace iphoto