| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/i18n/break_iterator.h" |
| |
| #include "base/logging.h" |
| #include "starboard/types.h" |
| #include "third_party/icu/source/common/unicode/ubrk.h" |
| #include "third_party/icu/source/common/unicode/uchar.h" |
| #include "third_party/icu/source/common/unicode/ustring.h" |
| |
| namespace base { |
| namespace i18n { |
| |
| const size_t npos = static_cast<size_t>(-1); |
| |
| BreakIterator::BreakIterator(const StringPiece16& str, BreakType break_type) |
| : iter_(nullptr), |
| string_(str), |
| break_type_(break_type), |
| prev_(npos), |
| pos_(0) {} |
| |
| BreakIterator::BreakIterator(const StringPiece16& str, const string16& rules) |
| : iter_(nullptr), |
| string_(str), |
| rules_(rules), |
| break_type_(RULE_BASED), |
| prev_(npos), |
| pos_(0) {} |
| |
| BreakIterator::~BreakIterator() { |
| if (iter_) |
| ubrk_close(static_cast<UBreakIterator*>(iter_)); |
| } |
| |
| bool BreakIterator::Init() { |
| UErrorCode status = U_ZERO_ERROR; |
| UParseError parse_error; |
| UBreakIteratorType break_type; |
| switch (break_type_) { |
| case BREAK_CHARACTER: |
| break_type = UBRK_CHARACTER; |
| break; |
| case BREAK_WORD: |
| break_type = UBRK_WORD; |
| break; |
| case BREAK_LINE: |
| case BREAK_NEWLINE: |
| case RULE_BASED: // (Keep compiler happy, break_type not used in this case) |
| break_type = UBRK_LINE; |
| break; |
| default: |
| NOTREACHED() << "invalid break_type_"; |
| return false; |
| } |
| if (break_type_ == RULE_BASED) { |
| iter_ = ubrk_openRules(rules_.c_str(), |
| static_cast<int32_t>(rules_.length()), |
| string_.data(), |
| static_cast<int32_t>(string_.size()), |
| &parse_error, |
| &status); |
| if (U_FAILURE(status)) { |
| NOTREACHED() << "ubrk_openRules failed to parse rule string at line " |
| << parse_error.line << ", offset " << parse_error.offset; |
| } |
| } else { |
| iter_ = ubrk_open(break_type, nullptr, string_.data(), |
| static_cast<int32_t>(string_.size()), &status); |
| if (U_FAILURE(status)) { |
| NOTREACHED() << "ubrk_open failed for type " << break_type |
| << " with error " << status; |
| } |
| } |
| |
| if (U_FAILURE(status)) { |
| return false; |
| } |
| |
| // Move the iterator to the beginning of the string. |
| ubrk_first(static_cast<UBreakIterator*>(iter_)); |
| return true; |
| } |
| |
| bool BreakIterator::Advance() { |
| int32_t pos; |
| int32_t status; |
| prev_ = pos_; |
| switch (break_type_) { |
| case BREAK_CHARACTER: |
| case BREAK_WORD: |
| case BREAK_LINE: |
| case RULE_BASED: |
| pos = ubrk_next(static_cast<UBreakIterator*>(iter_)); |
| if (pos == UBRK_DONE) { |
| pos_ = npos; |
| return false; |
| } |
| pos_ = static_cast<size_t>(pos); |
| return true; |
| case BREAK_NEWLINE: |
| do { |
| pos = ubrk_next(static_cast<UBreakIterator*>(iter_)); |
| if (pos == UBRK_DONE) |
| break; |
| pos_ = static_cast<size_t>(pos); |
| status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_)); |
| } while (status >= UBRK_LINE_SOFT && status < UBRK_LINE_SOFT_LIMIT); |
| if (pos == UBRK_DONE && prev_ == pos_) { |
| pos_ = npos; |
| return false; |
| } |
| return true; |
| default: |
| NOTREACHED() << "invalid break_type_"; |
| return false; |
| } |
| } |
| |
| bool BreakIterator::SetText(const base::char16* text, const size_t length) { |
| UErrorCode status = U_ZERO_ERROR; |
| ubrk_setText(static_cast<UBreakIterator*>(iter_), |
| text, length, &status); |
| pos_ = 0; // implicit when ubrk_setText is done |
| prev_ = npos; |
| if (U_FAILURE(status)) { |
| NOTREACHED() << "ubrk_setText failed"; |
| return false; |
| } |
| string_ = StringPiece16(text, length); |
| return true; |
| } |
| |
| bool BreakIterator::IsWord() const { |
| return GetWordBreakStatus() == IS_WORD_BREAK; |
| } |
| |
| BreakIterator::WordBreakStatus BreakIterator::GetWordBreakStatus() const { |
| int32_t status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_)); |
| if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) |
| return IS_LINE_OR_CHAR_BREAK; |
| // In ICU 60, trying to advance past the end of the text does not change |
| // |status| so that |pos_| has to be checked as well as |status|. |
| // See http://bugs.icu-project.org/trac/ticket/13447 . |
| return (status == UBRK_WORD_NONE || pos_ == npos) ? IS_SKIPPABLE_WORD |
| : IS_WORD_BREAK; |
| } |
| |
| bool BreakIterator::IsEndOfWord(size_t position) const { |
| if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) |
| return false; |
| |
| UBreakIterator* iter = static_cast<UBreakIterator*>(iter_); |
| UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position)); |
| int32_t status = ubrk_getRuleStatus(iter); |
| return (!!boundary && status != UBRK_WORD_NONE); |
| } |
| |
| bool BreakIterator::IsStartOfWord(size_t position) const { |
| if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) |
| return false; |
| |
| UBreakIterator* iter = static_cast<UBreakIterator*>(iter_); |
| UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position)); |
| ubrk_next(iter); |
| int32_t next_status = ubrk_getRuleStatus(iter); |
| return (!!boundary && next_status != UBRK_WORD_NONE); |
| } |
| |
| bool BreakIterator::IsGraphemeBoundary(size_t position) const { |
| if (break_type_ != BREAK_CHARACTER) |
| return false; |
| |
| UBreakIterator* iter = static_cast<UBreakIterator*>(iter_); |
| return !!ubrk_isBoundary(iter, static_cast<int32_t>(position)); |
| } |
| |
| string16 BreakIterator::GetString() const { |
| return GetStringPiece().as_string(); |
| } |
| |
| StringPiece16 BreakIterator::GetStringPiece() const { |
| DCHECK(prev_ != npos && pos_ != npos); |
| return string_.substr(prev_, pos_ - prev_); |
| } |
| |
| } // namespace i18n |
| } // namespace base |