1// Copyright (c) 2012 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/renderer/spellchecker/spellcheck_language.h"
6
7#include "base/logging.h"
8#include "chrome/renderer/spellchecker/spellcheck_worditerator.h"
9#include "chrome/renderer/spellchecker/spelling_engine.h"
10
11
12SpellcheckLanguage::SpellcheckLanguage()
13    : platform_spelling_engine_(CreateNativeSpellingEngine()) {
14}
15
16SpellcheckLanguage::~SpellcheckLanguage() {
17}
18
19void SpellcheckLanguage::Init(base::File file, const std::string& language) {
20  DCHECK(platform_spelling_engine_.get());
21  platform_spelling_engine_->Init(file.Pass());
22
23  character_attributes_.SetDefaultLanguage(language);
24  text_iterator_.Reset();
25  contraction_iterator_.Reset();
26}
27
28bool SpellcheckLanguage::InitializeIfNeeded() {
29  DCHECK(platform_spelling_engine_.get());
30  return platform_spelling_engine_->InitializeIfNeeded();
31}
32
33bool SpellcheckLanguage::SpellCheckWord(
34    const base::char16* in_word,
35    int in_word_len,
36    int tag,
37    int* misspelling_start,
38    int* misspelling_len,
39    std::vector<base::string16>* optional_suggestions) {
40  DCHECK(in_word_len >= 0);
41  DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
42
43  // Do nothing if we need to delay initialization. (Rather than blocking,
44  // report the word as correctly spelled.)
45  if (InitializeIfNeeded())
46    return true;
47
48  // Do nothing if spell checking is disabled.
49  if (!platform_spelling_engine_.get() ||
50      !platform_spelling_engine_->IsEnabled())
51    return true;
52
53  *misspelling_start = 0;
54  *misspelling_len = 0;
55  if (in_word_len == 0)
56    return true;  // No input means always spelled correctly.
57
58  base::string16 word;
59  int word_start;
60  int word_length;
61  if (!text_iterator_.IsInitialized() &&
62      !text_iterator_.Initialize(&character_attributes_, true)) {
63      // We failed to initialize text_iterator_, return as spelled correctly.
64      VLOG(1) << "Failed to initialize SpellcheckWordIterator";
65      return true;
66  }
67
68  text_iterator_.SetText(in_word, in_word_len);
69  DCHECK(platform_spelling_engine_.get());
70  while (text_iterator_.GetNextWord(&word, &word_start, &word_length)) {
71    // Found a word (or a contraction) that the spellchecker can check the
72    // spelling of.
73    if (platform_spelling_engine_->CheckSpelling(word, tag))
74      continue;
75
76    // If the given word is a concatenated word of two or more valid words
77    // (e.g. "hello:hello"), we should treat it as a valid word.
78    if (IsValidContraction(word, tag))
79      continue;
80
81    *misspelling_start = word_start;
82    *misspelling_len = word_length;
83
84    // Get the list of suggested words.
85    if (optional_suggestions) {
86      platform_spelling_engine_->FillSuggestionList(word,
87                                                    optional_suggestions);
88    }
89    return false;
90  }
91
92  return true;
93}
94
95// Returns whether or not the given string is a valid contraction.
96// This function is a fall-back when the SpellcheckWordIterator class
97// returns a concatenated word which is not in the selected dictionary
98// (e.g. "in'n'out") but each word is valid.
99bool SpellcheckLanguage::IsValidContraction(const base::string16& contraction,
100                                            int tag) {
101  if (!contraction_iterator_.IsInitialized() &&
102      !contraction_iterator_.Initialize(&character_attributes_, false)) {
103    // We failed to initialize the word iterator, return as spelled correctly.
104    VLOG(1) << "Failed to initialize contraction_iterator_";
105    return true;
106  }
107
108  contraction_iterator_.SetText(contraction.c_str(), contraction.length());
109
110  base::string16 word;
111  int word_start;
112  int word_length;
113
114  DCHECK(platform_spelling_engine_.get());
115  while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) {
116    if (!platform_spelling_engine_->CheckSpelling(word, tag))
117      return false;
118  }
119  return true;
120}
121
122bool SpellcheckLanguage::IsEnabled() {
123  DCHECK(platform_spelling_engine_.get());
124  return platform_spelling_engine_->IsEnabled();
125}
126