This source file includes following definitions.
- SetDefaultLanguage
- GetRuleSet
- CreateRuleSets
- OutputChar
- OutputArabic
- OutputHangul
- OutputHebrew
- OutputDefault
- iterator_
- Initialize
- IsInitialized
- SetText
- GetNextWord
- Reset
- Normalize
#include "chrome/renderer/spellchecker/spellcheck_worditerator.h"
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/renderer/spellchecker/spellcheck.h"
#include "third_party/icu/source/common/unicode/normlzr.h"
#include "third_party/icu/source/common/unicode/schriter.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "third_party/icu/source/i18n/unicode/ulocdata.h"
SpellcheckCharAttribute::SpellcheckCharAttribute()
: script_code_(USCRIPT_LATIN) {
}
SpellcheckCharAttribute::~SpellcheckCharAttribute() {
}
void SpellcheckCharAttribute::SetDefaultLanguage(const std::string& language) {
CreateRuleSets(language);
}
base::string16 SpellcheckCharAttribute::GetRuleSet(
bool allow_contraction) const {
return allow_contraction ?
ruleset_allow_contraction_ : ruleset_disallow_contraction_;
}
void SpellcheckCharAttribute::CreateRuleSets(const std::string& language) {
static const char kRuleTemplate[] =
"!!chain;"
"$CR = [\\p{Word_Break = CR}];"
"$LF = [\\p{Word_Break = LF}];"
"$Newline = [\\p{Word_Break = Newline}];"
"$Extend = [\\p{Word_Break = Extend}];"
"$Format = [\\p{Word_Break = Format}];"
"$Katakana = [\\p{Word_Break = Katakana}];"
"$ALetter = [\\p{script=%s}%s];"
"$MidNumLet = [\\p{Word_Break = MidNumLet}];"
"$MidLetter = [\\p{Word_Break = MidLetter}%s];"
"$MidNum = [\\p{Word_Break = MidNum}];"
"$Numeric = [\\p{Word_Break = Numeric}];"
"$ExtendNumLet = [\\p{Word_Break = ExtendNumLet}];"
"$Control = [\\p{Grapheme_Cluster_Break = Control}]; "
"%s"
"$KatakanaEx = $Katakana ($Extend | $Format)*;"
"$ALetterEx = $ALetterPlus ($Extend | $Format)*;"
"$MidNumLetEx = $MidNumLet ($Extend | $Format)*;"
"$MidLetterEx = $MidLetter ($Extend | $Format)*;"
"$MidNumEx = $MidNum ($Extend | $Format)*;"
"$NumericEx = $Numeric ($Extend | $Format)*;"
"$ExtendNumLetEx = $ExtendNumLet ($Extend | $Format)*;"
"$Hiragana = [\\p{script=Hiragana}];"
"$Ideographic = [\\p{Ideographic}];"
"$HiraganaEx = $Hiragana ($Extend | $Format)*;"
"$IdeographicEx = $Ideographic ($Extend | $Format)*;"
"!!forward;"
"$CR $LF;"
"[^$CR $LF $Newline]? ($Extend | $Format)+;"
"$ALetterEx {200};"
"$ALetterEx $ALetterEx {200};"
"%s"
"!!reverse;"
"$BackALetterEx = ($Format | $Extend)* $ALetterPlus;"
"$BackMidNumLetEx = ($Format | $Extend)* $MidNumLet;"
"$BackNumericEx = ($Format | $Extend)* $Numeric;"
"$BackMidNumEx = ($Format | $Extend)* $MidNum;"
"$BackMidLetterEx = ($Format | $Extend)* $MidLetter;"
"$BackKatakanaEx = ($Format | $Extend)* $Katakana;"
"$BackExtendNumLetEx= ($Format | $Extend)* $ExtendNumLet;"
"$LF $CR;"
"($Format | $Extend)* [^$CR $LF $Newline]?;"
"$BackALetterEx $BackALetterEx;"
"$BackALetterEx ($BackMidLetterEx | $BackMidNumLetEx) $BackALetterEx;"
"$BackNumericEx $BackNumericEx;"
"$BackNumericEx $BackALetterEx;"
"$BackALetterEx $BackNumericEx;"
"$BackNumericEx ($BackMidNumEx | $BackMidNumLetEx) $BackNumericEx;"
"$BackKatakanaEx $BackKatakanaEx;"
"$BackExtendNumLetEx ($BackALetterEx | $BackNumericEx |"
" $BackKatakanaEx | $BackExtendNumLetEx);"
"($BackALetterEx | $BackNumericEx | $BackKatakanaEx)"
" $BackExtendNumLetEx;"
"!!safe_reverse;"
"($Extend | $Format)+ .?;"
"($MidLetter | $MidNumLet) $BackALetterEx;"
"($MidNum | $MidNumLet) $BackNumericEx;"
"!!safe_forward;"
"($Extend | $Format)+ .?;"
"($MidLetterEx | $MidNumLetEx) $ALetterEx;"
"($MidNumEx | $MidNumLetEx) $NumericEx;";
UErrorCode error = U_ZERO_ERROR;
UScriptCode script_code[8];
int scripts = uscript_getCode(language.c_str(), script_code,
arraysize(script_code), &error);
if (U_SUCCESS(error) && scripts >= 1)
script_code_ = script_code[0];
const char* aletter = uscript_getName(script_code_);
if (!aletter)
aletter = "Latin";
const char kWithDictionary[] =
"$dictionary = [:LineBreak = Complex_Context:];"
"$ALetterPlus = [$ALetter [$dictionary-$Extend-$Control]];";
const char kWithoutDictionary[] = "$ALetterPlus = $ALetter;";
const char* aletter_plus = kWithoutDictionary;
if (script_code_ == USCRIPT_HANGUL || script_code_ == USCRIPT_THAI)
aletter_plus = kWithDictionary;
const char* aletter_extra = " [0123456789]";
if (script_code_ == USCRIPT_HEBREW || script_code_ == USCRIPT_ARABIC)
aletter_extra = "";
const char kMidLetterExtra[] = "";
const char kMidLetterExtraHebrew[] = "\"'";
const char* midletter_extra = kMidLetterExtra;
if (script_code_ == USCRIPT_HEBREW)
midletter_extra = kMidLetterExtraHebrew;
const char kAllowContraction[] =
"$ALetterEx ($MidLetterEx | $MidNumLetEx) $ALetterEx {200};";
const char kDisallowContraction[] = "";
ruleset_allow_contraction_ = base::ASCIIToUTF16(
base::StringPrintf(kRuleTemplate,
aletter,
aletter_extra,
midletter_extra,
aletter_plus,
kAllowContraction));
ruleset_disallow_contraction_ = base::ASCIIToUTF16(
base::StringPrintf(kRuleTemplate,
aletter,
aletter_extra,
midletter_extra,
aletter_plus,
kDisallowContraction));
}
bool SpellcheckCharAttribute::OutputChar(UChar c,
base::string16* output) const {
switch (script_code_) {
case USCRIPT_ARABIC:
return OutputArabic(c, output);
case USCRIPT_HANGUL:
return OutputHangul(c, output);
case USCRIPT_HEBREW:
return OutputHebrew(c, output);
default:
return OutputDefault(c, output);
}
}
bool SpellcheckCharAttribute::OutputArabic(UChar c,
base::string16* output) const {
if (0x0621 <= c && c <= 0x064D)
output->push_back(c);
return true;
}
bool SpellcheckCharAttribute::OutputHangul(UChar c,
base::string16* output) const {
const int kSBase = 0xAC00;
const int kLBase = 0x1100;
const int kVBase = 0x1161;
const int kTBase = 0x11A7;
const int kLCount = 19;
const int kVCount = 21;
const int kTCount = 28;
const int kNCount = kVCount * kTCount;
const int kSCount = kLCount * kNCount;
int index = c - kSBase;
if (index < 0 || index >= kSBase + kSCount) {
return OutputDefault(c, output);
}
int l = kLBase + index / kNCount;
int v = kVBase + (index % kNCount) / kTCount;
int t = kTBase + index % kTCount;
output->push_back(l);
output->push_back(v);
if (t != kTBase)
output->push_back(t);
return true;
}
bool SpellcheckCharAttribute::OutputHebrew(UChar c,
base::string16* output) const {
if ((0x05D0 <= c && c <= 0x05EA) || c == 0x22 || c == 0x27 ||
c == 0x05F4 || c == 0x05F3)
output->push_back(c);
return true;
}
bool SpellcheckCharAttribute::OutputDefault(UChar c,
base::string16* output) const {
UErrorCode status = U_ZERO_ERROR;
UScriptCode script_code = uscript_getScript(c, &status);
if (script_code == script_code_ || script_code == USCRIPT_COMMON)
output->push_back(c);
return true;
}
SpellcheckWordIterator::SpellcheckWordIterator()
: text_(NULL),
length_(0),
position_(UBRK_DONE),
attribute_(NULL),
iterator_(NULL) {
}
SpellcheckWordIterator::~SpellcheckWordIterator() {
Reset();
}
bool SpellcheckWordIterator::Initialize(
const SpellcheckCharAttribute* attribute,
bool allow_contraction) {
DCHECK(attribute);
UErrorCode open_status = U_ZERO_ERROR;
UParseError parse_status;
base::string16 rule(attribute->GetRuleSet(allow_contraction));
if (rule.empty())
return false;
iterator_ = ubrk_openRules(rule.c_str(), rule.length(), NULL, 0,
&parse_status, &open_status);
if (U_FAILURE(open_status))
return false;
attribute_ = attribute;
return true;
}
bool SpellcheckWordIterator::IsInitialized() const {
return !!iterator_;
}
bool SpellcheckWordIterator::SetText(const base::char16* text, size_t length) {
DCHECK(!!iterator_);
UErrorCode status = U_ZERO_ERROR;
ubrk_setText(iterator_, text, length, &status);
if (U_FAILURE(status))
return false;
position_ = ubrk_first(iterator_);
if (position_ == UBRK_DONE)
return false;
text_ = text;
length_ = static_cast<int>(length);
return true;
}
bool SpellcheckWordIterator::GetNextWord(base::string16* word_string,
int* word_start,
int* word_length) {
DCHECK(!!text_ && length_ > 0);
word_string->clear();
*word_start = 0;
*word_length = 0;
if (!text_ || position_ == UBRK_DONE)
return false;
int next = ubrk_next(iterator_);
while (next != UBRK_DONE) {
if (ubrk_getRuleStatus(iterator_) != UBRK_WORD_NONE) {
if (Normalize(position_, next - position_, word_string)) {
*word_start = position_;
*word_length = next - position_;
position_ = next;
return true;
}
}
position_ = next;
next = ubrk_next(iterator_);
}
position_ = UBRK_DONE;
return false;
}
void SpellcheckWordIterator::Reset() {
if (iterator_) {
ubrk_close(iterator_);
iterator_ = NULL;
}
}
bool SpellcheckWordIterator::Normalize(int input_start,
int input_length,
base::string16* output_string) const {
icu::UnicodeString input(FALSE, &text_[input_start], input_length);
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString output;
icu::Normalizer::normalize(input, UNORM_NFKC, 0, output, status);
if (status != U_ZERO_ERROR && status != U_STRING_NOT_TERMINATED_WARNING)
return false;
icu::StringCharacterIterator it(output);
for (UChar c = it.first(); c != icu::CharacterIterator::DONE; c = it.next())
attribute_->OutputChar(c, output_string);
return !output_string->empty();
}