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_album_table_reader.h"
6
7#include <algorithm>
8#include <string>
9
10#include "base/path_service.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/common/media_galleries/pmp_constants.h"
14#include "chrome/utility/media_galleries/pmp_column_reader.h"
15
16namespace picasa {
17
18namespace {
19
20// |variant_time| is specified as the number of days from Dec 30, 1899.
21base::Time TimeFromMicrosoftVariantTime(double variant_time) {
22  base::TimeDelta variant_delta = base::TimeDelta::FromMicroseconds(
23      static_cast<int64>(variant_time * base::Time::kMicrosecondsPerDay));
24
25  return base::Time::FromLocalExploded(kPmpVariantTimeEpoch) + variant_delta;
26}
27
28}  // namespace
29
30PicasaAlbumTableReader::PicasaAlbumTableReader(AlbumTableFiles table_files)
31    : table_files_(table_files.Pass()),
32      initialized_(false) {
33}
34
35PicasaAlbumTableReader::~PicasaAlbumTableReader() {
36}
37
38const std::vector<AlbumInfo>& PicasaAlbumTableReader::folders() const {
39  DCHECK(initialized_);
40  return folders_;
41}
42
43const std::vector<AlbumInfo>& PicasaAlbumTableReader::albums() const {
44  DCHECK(initialized_);
45  return albums_;
46}
47
48bool PicasaAlbumTableReader::Init() {
49  if (initialized_)
50    return true;
51
52  if (!table_files_.indicator_file.IsValid())
53    return false;
54
55  PmpColumnReader category_column, date_column, filename_column, name_column,
56                  token_column, uid_column;
57  if (!category_column.ReadFile(&table_files_.category_file, PMP_TYPE_UINT32) ||
58      !date_column.ReadFile(&table_files_.date_file, PMP_TYPE_DOUBLE64) ||
59      !filename_column.ReadFile(&table_files_.filename_file, PMP_TYPE_STRING) ||
60      !name_column.ReadFile(&table_files_.name_file, PMP_TYPE_STRING) ||
61      !token_column.ReadFile(&table_files_.token_file, PMP_TYPE_STRING) ||
62      !uid_column.ReadFile(&table_files_.uid_file, PMP_TYPE_STRING)) {
63    return false;
64  }
65
66  // In the PMP format, columns can be different lengths. The number of rows
67  // in the table is max of all the columns, and short columns are NULL padded.
68  uint32 row_count = 0;
69  row_count = std::max(row_count, category_column.rows_read());
70  row_count = std::max(row_count, date_column.rows_read());
71  row_count = std::max(row_count, filename_column.rows_read());
72  row_count = std::max(row_count, name_column.rows_read());
73  row_count = std::max(row_count, token_column.rows_read());
74  row_count = std::max(row_count, uid_column.rows_read());
75
76  for (uint32 i = 0; i < row_count; i++) {
77    uint32 category = kAlbumCategoryInvalid;
78    double date = 0;
79    std::string name;
80    std::string uid;
81    // PMP tables often contain 'garbage' rows of deleted or auto-generated
82    // album-like entities. We ignore those rows.
83    if (!category_column.ReadUInt32(i, &category) ||
84        !date_column.ReadDouble64(i, &date) ||
85        !name_column.ReadString(i, &name) || name.empty() ||
86        !uid_column.ReadString(i, &uid) || uid.empty()) {
87      continue;
88    }
89
90    base::Time timestamp = TimeFromMicrosoftVariantTime(date);
91
92    if (category == kAlbumCategoryAlbum) {
93      std::string token;
94      if (!token_column.ReadString(i, &token) || token.empty() ||
95          !StartsWithASCII(token, kAlbumTokenPrefix, false)) {
96        continue;
97      }
98
99      albums_.push_back(AlbumInfo(name, timestamp, uid, base::FilePath()));
100    } else if (category == kAlbumCategoryFolder) {
101      std::string filename;
102      if (!filename_column.ReadString(i, &filename) || filename.empty())
103        continue;
104
105      base::FilePath path =
106          base::FilePath(base::FilePath::FromUTF8Unsafe(filename));
107
108      folders_.push_back(AlbumInfo(name, timestamp, uid, path));
109    }
110  }
111
112  initialized_ = true;
113  return true;
114}
115
116}  // namespace picasa
117