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/picasa_albums_indexer.h"
6
7#include <algorithm>
8#include <utility>
9#include <vector>
10
11#include "base/logging.h"
12#include "base/strings/string_split.h"
13#include "base/strings/stringprintf.h"
14#include "chrome/common/ini_parser.h"
15
16namespace picasa {
17
18namespace {
19
20const char kAlbumSectionHeader[] = ".album:";
21const char kAlbumsKey[] = "albums";
22const int kMaxDedupeNumber = 1000;  // Chosen arbitrarily.
23
24class PicasaINIParser : public INIParser {
25 public:
26  PicasaINIParser(
27      const base::FilePath& folder_path, AlbumImagesMap* albums_images)
28      : folder_path_(folder_path),
29        albums_images_(albums_images) {
30  }
31  virtual ~PicasaINIParser() {}
32
33 private:
34  virtual void HandleTriplet(const std::string& section,
35                             const std::string& key,
36                             const std::string& value) OVERRIDE {
37    if (key != kAlbumsKey)
38      return;
39
40    // [.album:*] sections ignored as we get that data from the PMP files.
41    if (section.find(kAlbumSectionHeader) == 0)
42      return;
43
44    std::vector<std::string> containing_albums;
45    base::SplitString(value, ',', &containing_albums);
46    for (std::vector<std::string>::iterator it = containing_albums.begin();
47         it != containing_albums.end(); ++it) {
48      AlbumImagesMap::iterator album_map_it = albums_images_->find(*it);
49
50      // Ignore entry if the album uid is not listed among in |album_uids|
51      // in the constructor. Happens if the PMP and INI files are inconsistent.
52      if (album_map_it == albums_images_->end())
53        continue;
54
55      base::FilePath filename = base::FilePath::FromUTF8Unsafe(section);
56      AlbumImages& album_images = album_map_it->second;
57
58      // If filename is first of its name in album, simply add.
59      if (album_images.insert(
60              std::make_pair(section, folder_path_.Append(filename))).second) {
61        continue;
62      }
63
64      // Otherwise, de-dupe by appending a number starting at (1).
65      for (int i = 1; i < kMaxDedupeNumber; ++i) {
66        std::string deduped_filename =
67            filename.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", i))
68                .AsUTF8Unsafe();
69
70        // Attempt to add the de-duped name.
71        if (album_images.insert(
72                std::make_pair(deduped_filename, folder_path_.Append(filename)))
73                .second) {
74          break;
75        }
76      }
77    }
78  }
79
80  const base::FilePath folder_path_;
81  AlbumImagesMap* const albums_images_;
82};
83
84}  // namespace
85
86PicasaAlbumsIndexer::PicasaAlbumsIndexer(const AlbumUIDSet& album_uids) {
87  // Create an entry in the map for the valid album uids.
88  for (AlbumUIDSet::const_iterator it = album_uids.begin();
89       it != album_uids.end(); ++it) {
90    albums_images_[*it] = AlbumImages();
91  }
92}
93
94PicasaAlbumsIndexer::~PicasaAlbumsIndexer() {}
95
96void PicasaAlbumsIndexer::ParseFolderINI(
97    const std::vector<picasa::FolderINIContents>& folders_inis) {
98  // Make a copy for sorting
99  std::vector<picasa::FolderINIContents> folders_inis_sorted = folders_inis;
100
101  // Sort here so image names are deduplicated in a stable ordering.
102  std::sort(folders_inis_sorted.begin(), folders_inis_sorted.end());
103
104  for (std::vector<picasa::FolderINIContents>::const_iterator it =
105           folders_inis_sorted.begin();
106       it != folders_inis_sorted.end();
107       ++it) {
108    PicasaINIParser parser(it->folder_path, &albums_images_);
109    parser.Parse(it->ini_contents);
110  }
111}
112
113}  // namespace picasa
114