1// Copyright (c) 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// The |Feedback| object keeps track of each instance of user feedback in a map
6// |misspellings_|. This is a map from uint32 hashes to |Misspelling| objects.
7//
8// Each misspelling should be present in only one renderer process. The
9// |Feedback| objects keeps track of misspelling-renderer relationship in the
10// |renderers_| map of renderer process identifiers to a set of hashes.
11//
12// When the user adds a misspelling to their custom dictionary, all of the
13// |Misspelling| objects with the same misspelled string are updated. The
14// |Feedback| object facilitates efficient access to these misspellings through
15// a |text_| map of misspelled strings to a set of hashes.
16
17#include "chrome/browser/spellchecker/feedback.h"
18
19#include <algorithm>
20#include <iterator>
21
22#include "base/stl_util.h"
23
24namespace spellcheck {
25
26Feedback::Feedback() {
27}
28
29Feedback::~Feedback() {
30}
31
32Misspelling* Feedback::GetMisspelling(uint32 hash) {
33  HashMisspellingMap::iterator misspelling_it = misspellings_.find(hash);
34  if (misspelling_it == misspellings_.end())
35    return NULL;
36  return &misspelling_it->second;
37}
38
39void Feedback::FinalizeRemovedMisspellings(
40    int renderer_process_id,
41    const std::vector<uint32>& remaining_markers) {
42  RendererHashesMap::iterator renderer_it =
43      renderers_.find(renderer_process_id);
44  if (renderer_it == renderers_.end() || renderer_it->second.empty())
45    return;
46  HashCollection& renderer_hashes = renderer_it->second;
47  HashCollection remaining_hashes(remaining_markers.begin(),
48                                  remaining_markers.end());
49  std::vector<uint32> removed_hashes =
50      base::STLSetDifference<std::vector<uint32> >(renderer_hashes,
51                                                   remaining_hashes);
52  for (std::vector<uint32>::const_iterator hash_it = removed_hashes.begin();
53       hash_it != removed_hashes.end();
54       ++hash_it) {
55    HashMisspellingMap::iterator misspelling_it = misspellings_.find(*hash_it);
56    if (misspelling_it != misspellings_.end() &&
57        !misspelling_it->second.action.IsFinal()) {
58      misspelling_it->second.action.Finalize();
59    }
60  }
61}
62
63bool Feedback::RendererHasMisspellings(int renderer_process_id) const {
64  RendererHashesMap::const_iterator renderer_it =
65      renderers_.find(renderer_process_id);
66  return renderer_it != renderers_.end() && !renderer_it->second.empty();
67}
68
69std::vector<Misspelling> Feedback::GetMisspellingsInRenderer(
70    int renderer_process_id) const {
71  std::vector<Misspelling> misspellings_in_renderer;
72  RendererHashesMap::const_iterator renderer_it =
73      renderers_.find(renderer_process_id);
74  if (renderer_it == renderers_.end() || renderer_it->second.empty())
75    return misspellings_in_renderer;
76  const HashCollection& renderer_hashes = renderer_it->second;
77  for (HashCollection::const_iterator hash_it = renderer_hashes.begin();
78       hash_it != renderer_hashes.end();
79       ++hash_it) {
80    HashMisspellingMap::const_iterator misspelling_it =
81        misspellings_.find(*hash_it);
82    if (misspelling_it != misspellings_.end())
83      misspellings_in_renderer.push_back(misspelling_it->second);
84  }
85  return misspellings_in_renderer;
86}
87
88void Feedback::EraseFinalizedMisspellings(int renderer_process_id) {
89  RendererHashesMap::iterator renderer_it =
90      renderers_.find(renderer_process_id);
91  if (renderer_it == renderers_.end())
92    return;
93  HashCollection& renderer_hashes = renderer_it->second;
94  for (HashCollection::const_iterator hash_it = renderer_hashes.begin();
95       hash_it != renderer_hashes.end();) {
96    HashMisspellingMap::iterator misspelling_it = misspellings_.find(*hash_it);
97    HashCollection::iterator erasable_hash_it = hash_it;
98    ++hash_it;
99    if (misspelling_it == misspellings_.end())
100      continue;
101    const Misspelling& misspelling = misspelling_it->second;
102    if (!misspelling.action.IsFinal())
103      continue;
104    renderer_hashes.erase(erasable_hash_it);
105    text_[misspelling.GetMisspelledString()].erase(misspelling.hash);
106    misspellings_.erase(misspelling_it);
107  }
108  if (renderer_hashes.empty())
109    renderers_.erase(renderer_it);
110}
111
112bool Feedback::HasMisspelling(uint32 hash) const {
113  return !!misspellings_.count(hash);
114}
115
116void Feedback::AddMisspelling(int renderer_process_id,
117                              const Misspelling& misspelling) {
118  HashMisspellingMap::iterator misspelling_it =
119      misspellings_.find(misspelling.hash);
120  if (misspelling_it != misspellings_.end()) {
121    const Misspelling& existing_misspelling = misspelling_it->second;
122    text_[existing_misspelling.GetMisspelledString()].erase(misspelling.hash);
123    for (RendererHashesMap::iterator renderer_it = renderers_.begin();
124         renderer_it != renderers_.end();) {
125      HashCollection& renderer_hashes = renderer_it->second;
126      RendererHashesMap::iterator erasable_renderer_it = renderer_it;
127      ++renderer_it;
128      renderer_hashes.erase(misspelling.hash);
129      if (renderer_hashes.empty())
130        renderers_.erase(erasable_renderer_it);
131    }
132  }
133  misspellings_[misspelling.hash] = misspelling;
134  text_[misspelling.GetMisspelledString()].insert(misspelling.hash);
135  renderers_[renderer_process_id].insert(misspelling.hash);
136}
137
138bool Feedback::Empty() const {
139  return misspellings_.empty();
140}
141
142std::vector<int> Feedback::GetRendersWithMisspellings() const {
143  std::vector<int> renderers_with_misspellings;
144  for (RendererHashesMap::const_iterator renderer_it = renderers_.begin();
145       renderer_it != renderers_.end();
146       ++renderer_it) {
147    if (!renderer_it->second.empty())
148      renderers_with_misspellings.push_back(renderer_it->first);
149  }
150  return renderers_with_misspellings;
151}
152
153void Feedback::FinalizeAllMisspellings() {
154  for (HashMisspellingMap::iterator misspelling_it = misspellings_.begin();
155       misspelling_it != misspellings_.end();
156       ++misspelling_it) {
157    if (!misspelling_it->second.action.IsFinal())
158      misspelling_it->second.action.Finalize();
159  }
160}
161
162std::vector<Misspelling> Feedback::GetAllMisspellings() const {
163  std::vector<Misspelling> all_misspellings;
164  for (HashMisspellingMap::const_iterator misspelling_it =
165           misspellings_.begin();
166       misspelling_it != misspellings_.end();
167       ++misspelling_it) {
168    all_misspellings.push_back(misspelling_it->second);
169  }
170  return all_misspellings;
171}
172
173void Feedback::Clear() {
174  misspellings_.clear();
175  text_.clear();
176  renderers_.clear();
177}
178
179const std::set<uint32>& Feedback::FindMisspellings(
180    const base::string16& misspelled_text) const {
181  const TextHashesMap::const_iterator text_it = text_.find(misspelled_text);
182  return text_it == text_.end() ? empty_hash_collection_ : text_it->second;
183}
184
185}  // namespace spellcheck
186