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/browser/media_galleries/fileapi/iphoto_data_provider.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/location.h"
12#include "base/logging.h"
13#include "base/stl_util.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/threading/thread_restrictions.h"
16#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
17#include "chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h"
18
19namespace iphoto {
20
21IPhotoDataProvider::IPhotoDataProvider(const base::FilePath& library_path)
22    : iapps::IAppsDataProvider(library_path),
23      weak_factory_(this) {}
24
25IPhotoDataProvider::~IPhotoDataProvider() {}
26
27void IPhotoDataProvider::DoParseLibrary(
28    const base::FilePath& library_path,
29    const ReadyCallback& ready_callback) {
30  xml_parser_ = new iapps::SafeIAppsLibraryParser;
31  xml_parser_->ParseIPhotoLibrary(
32      library_path,
33      base::Bind(&IPhotoDataProvider::OnLibraryParsed,
34                 weak_factory_.GetWeakPtr(),
35                 ready_callback));
36}
37
38void IPhotoDataProvider::OnLibraryParsed(const ReadyCallback& ready_callback,
39                                         bool result,
40                                         const parser::Library& library) {
41  DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
42  set_valid(result);
43  if (valid())
44    BuildIndices(library);
45  ready_callback.Run(valid());
46}
47
48void IPhotoDataProvider::BuildIndices(const parser::Library& library) {
49  typedef base::hash_map<uint64, const base::FilePath*> IdIndex;
50
51  IdIndex photo_id_index;
52  IdIndex originals_id_index;
53  for (std::set<parser::Photo>::const_iterator photo_it =
54           library.all_photos.begin();
55       photo_it != library.all_photos.end(); photo_it++) {
56    photo_id_index[photo_it->id] = &(photo_it->location);
57    if (!photo_it->original_location.empty())
58      originals_id_index[photo_it->id] = &(photo_it->original_location);
59  }
60
61  // Build up a set of IDs which have in-album duplicates.
62  // Those are the filenames we want to globally mangle.
63  std::set<uint64> dupe_ids;
64  for (parser::Albums::const_iterator album_it = library.albums.begin();
65       album_it != library.albums.end(); album_it++) {
66    const parser::Album& album = album_it->second;
67
68    std::set<std::string> album_paths;
69    for (parser::Album::const_iterator id_it = album.begin();
70         id_it != album.end(); id_it++) {
71      uint64 id = *id_it;
72      IdIndex::const_iterator photo_it = photo_id_index.find(id);
73      if (photo_it == photo_id_index.end())
74        continue;
75
76      std::string filename = photo_it->second->BaseName().value();
77      if (ContainsKey(album_paths, filename))
78        dupe_ids.insert(id);
79      else
80        album_paths.insert(filename);
81    }
82  }
83
84  // Now build the directory index.
85  dir_index_.clear();
86  originals_index_.clear();
87  for (parser::Albums::const_iterator album_it = library.albums.begin();
88       album_it != library.albums.end(); album_it++) {
89    std::string album_name = album_it->first;
90    const parser::Album& album = album_it->second;
91
92    for (parser::Album::const_iterator id_it = album.begin();
93         id_it != album.end(); id_it++) {
94      uint64 id = *id_it;
95      IdIndex::const_iterator photo_it = photo_id_index.find(id);
96      if (photo_it == photo_id_index.end())
97        continue;
98      base::FilePath path = *(photo_it->second);
99
100      std::string filename = path.BaseName().value();
101      if (ContainsKey(dupe_ids, id)) {
102        filename = path.BaseName().InsertBeforeExtension(
103          "(" + base::Uint64ToString(id) + ")").value();
104      }
105
106      dir_index_[album_name][filename] = path;
107
108      IdIndex::const_iterator original_it = originals_id_index.find(id);
109      if (original_it != originals_id_index.end())
110        originals_index_[album_name][filename] = *(original_it->second);
111    }
112  }
113}
114
115std::vector<std::string> IPhotoDataProvider::GetAlbumNames() const {
116  std::vector<std::string> names;
117
118  for (DirIndex::const_iterator dir_it = dir_index_.begin();
119       dir_it != dir_index_.end(); dir_it++) {
120    names.push_back(dir_it->first);
121  }
122
123  return names;
124}
125
126std::map<std::string, base::FilePath> IPhotoDataProvider::GetAlbumContents(
127    const std::string& album) const {
128  std::map<std::string, base::FilePath> locations;
129  DirIndex::const_iterator dir_it = dir_index_.find(album);
130  if (dir_it == dir_index_.end())
131    return locations;
132
133  for (FileIndex::const_iterator file_it = dir_it->second.begin();
134       file_it != dir_it->second.end(); file_it++) {
135    locations.insert(make_pair(file_it->first, file_it->second));
136  }
137
138  return locations;
139}
140
141base::FilePath IPhotoDataProvider::GetPhotoLocationInAlbum(
142    const std::string& album,
143    const std::string& filename) const {
144  DirIndex::const_iterator dir_it = dir_index_.find(album);
145  if (dir_it == dir_index_.end())
146    return base::FilePath();
147  FileIndex::const_iterator file_it = dir_it->second.find(filename);
148  if (file_it == dir_it->second.end())
149    return base::FilePath();
150  return file_it->second;
151}
152
153bool IPhotoDataProvider::HasOriginals(const std::string& album) const {
154  DirIndex::const_iterator originals_it = originals_index_.find(album);
155  return originals_it != originals_index_.end();
156}
157
158std::map<std::string, base::FilePath> IPhotoDataProvider::GetOriginals(
159    const std::string& album) const {
160  std::map<std::string, base::FilePath> locations;
161  DirIndex::const_iterator originals_it = originals_index_.find(album);
162  if (originals_it == originals_index_.end())
163    return locations;
164
165  for (FileIndex::const_iterator file_it = originals_it->second.begin();
166       file_it != originals_it->second.end(); file_it++) {
167    locations.insert(make_pair(file_it->first, file_it->second));
168  }
169
170  return locations;
171}
172
173base::FilePath IPhotoDataProvider::GetOriginalPhotoLocation(
174      const std::string& album,
175      const std::string& filename) const {
176  DirIndex::const_iterator originals_it = originals_index_.find(album);
177  if (originals_it == originals_index_.end())
178    return base::FilePath();
179  FileIndex::const_iterator file_it = originals_it->second.find(filename);
180  if (file_it == originals_it->second.end())
181    return base::FilePath();
182  return file_it->second;
183}
184
185}  // namespace iphoto
186