// Copyright (C) 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
* Copyright (C) 2001-2014 IBM and others. All rights reserved.
**********************************************************************
* Date Name Description
* 03/22/2000 helena Creation.
**********************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_COLLATION && !UCONFIG_NO_BREAK_ITERATION
#include "unicode/stsearch.h"
#include "usrchimp.h"
#include "cmemory.h"
U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringSearch)
// public constructors and destructors -----------------------------------
StringSearch::StringSearch(const UnicodeString &pattern,
const UnicodeString &text,
const Locale &locale,
BreakIterator *breakiter,
UErrorCode &status) :
SearchIterator(text, breakiter),
m_pattern_(pattern)
{
if (U_FAILURE(status)) {
m_strsrch_ = NULL;
return;
}
m_strsrch_ = usearch_open(m_pattern_.getBuffer(), m_pattern_.length(),
m_text_.getBuffer(), m_text_.length(),
locale.getName(), (UBreakIterator *)breakiter,
&status);
uprv_free(m_search_);
m_search_ = NULL;
if (U_SUCCESS(status)) {
// m_search_ has been created by the base SearchIterator class
m_search_ = m_strsrch_->search;
}
}
StringSearch::StringSearch(const UnicodeString &pattern,
const UnicodeString &text,
RuleBasedCollator *coll,
BreakIterator *breakiter,
UErrorCode &status) :
SearchIterator(text, breakiter),
m_pattern_(pattern)
{
if (U_FAILURE(status)) {
m_strsrch_ = NULL;
return;
}
if (coll == NULL) {
status = U_ILLEGAL_ARGUMENT_ERROR;
m_strsrch_ = NULL;
return;
}
m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
m_pattern_.length(),
m_text_.getBuffer(),
m_text_.length(), coll->toUCollator(),
(UBreakIterator *)breakiter,
&status);
uprv_free(m_search_);
m_search_ = NULL;
if (U_SUCCESS(status)) {
// m_search_ has been created by the base SearchIterator class
m_search_ = m_strsrch_->search;
}
}
StringSearch::StringSearch(const UnicodeString &pattern,
CharacterIterator &text,
const Locale &locale,
BreakIterator *breakiter,
UErrorCode &status) :
SearchIterator(text, breakiter),
m_pattern_(pattern)
{
if (U_FAILURE(status)) {
m_strsrch_ = NULL;
return;
}
m_strsrch_ = usearch_open(m_pattern_.getBuffer(), m_pattern_.length(),
m_text_.getBuffer(), m_text_.length(),
locale.getName(), (UBreakIterator *)breakiter,
&status);
uprv_free(m_search_);
m_search_ = NULL;
if (U_SUCCESS(status)) {
// m_search_ has been created by the base SearchIterator class
m_search_ = m_strsrch_->search;
}
}
StringSearch::StringSearch(const UnicodeString &pattern,
CharacterIterator &text,
RuleBasedCollator *coll,
BreakIterator *breakiter,
UErrorCode &status) :
SearchIterator(text, breakiter),
m_pattern_(pattern)
{
if (U_FAILURE(status)) {
m_strsrch_ = NULL;
return;
}
if (coll == NULL) {
status = U_ILLEGAL_ARGUMENT_ERROR;
m_strsrch_ = NULL;
return;
}
m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
m_pattern_.length(),
m_text_.getBuffer(),
m_text_.length(), coll->toUCollator(),
(UBreakIterator *)breakiter,
&status);
uprv_free(m_search_);
m_search_ = NULL;
if (U_SUCCESS(status)) {
// m_search_ has been created by the base SearchIterator class
m_search_ = m_strsrch_->search;
}
}
StringSearch::StringSearch(const StringSearch &that) :
SearchIterator(that.m_text_, that.m_breakiterator_),
m_pattern_(that.m_pattern_)
{
UErrorCode status = U_ZERO_ERROR;
// Free m_search_ from the superclass
uprv_free(m_search_);
m_search_ = NULL;
if (that.m_strsrch_ == NULL) {
// This was not a good copy
m_strsrch_ = NULL;
}
else {
// Make a deep copy
m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
m_pattern_.length(),
m_text_.getBuffer(),
m_text_.length(),
that.m_strsrch_->collator,
(UBreakIterator *)that.m_breakiterator_,
&status);
if (U_SUCCESS(status)) {
// m_search_ has been created by the base SearchIterator class
m_search_ = m_strsrch_->search;
}
}
}
StringSearch::~StringSearch()
{
if (m_strsrch_ != NULL) {
usearch_close(m_strsrch_);
m_search_ = NULL;
}
}
StringSearch *
StringSearch::clone() const {
return new StringSearch(*this);
}
// operator overloading ---------------------------------------------
StringSearch & StringSearch::operator=(const StringSearch &that)
{
if ((*this) != that) {
UErrorCode status = U_ZERO_ERROR;
m_text_ = that.m_text_;
m_breakiterator_ = that.m_breakiterator_;
m_pattern_ = that.m_pattern_;
// all m_search_ in the parent class is linked up with m_strsrch_
usearch_close(m_strsrch_);
m_strsrch_ = usearch_openFromCollator(m_pattern_.getBuffer(),
m_pattern_.length(),
m_text_.getBuffer(),
m_text_.length(),
that.m_strsrch_->collator,
NULL, &status);
// Check null pointer
if (m_strsrch_ != NULL) {
m_search_ = m_strsrch_->search;
}
}
return *this;
}
UBool StringSearch::operator==(const SearchIterator &that) const
{
if (this == &that) {
return TRUE;
}
if (SearchIterator::operator ==(that)) {
StringSearch &thatsrch = (StringSearch &)that;
return (this->m_pattern_ == thatsrch.m_pattern_ &&
this->m_strsrch_->collator == thatsrch.m_strsrch_->collator);
}
return FALSE;
}
// public get and set methods ----------------------------------------
void StringSearch::setOffset(int32_t position, UErrorCode &status)
{
// status checked in usearch_setOffset
usearch_setOffset(m_strsrch_, position, &status);
}
int32_t StringSearch::getOffset(void) const
{
return usearch_getOffset(m_strsrch_);
}
void StringSearch::setText(const UnicodeString &text, UErrorCode &status)
{
if (U_SUCCESS(status)) {
m_text_ = text;
usearch_setText(m_strsrch_, text.getBuffer(), text.length(), &status);
}
}
void StringSearch::setText(CharacterIterator &text, UErrorCode &status)
{
if (U_SUCCESS(status)) {
text.getText(m_text_);
usearch_setText(m_strsrch_, m_text_.getBuffer(), m_text_.length(), &status);
}
}
RuleBasedCollator * StringSearch::getCollator() const
{
// Note the const_cast. It would be cleaner if this const method returned a const collator.
return RuleBasedCollator::rbcFromUCollator(const_cast<UCollator *>(m_strsrch_->collator));
}
void StringSearch::setCollator(RuleBasedCollator *coll, UErrorCode &status)
{
if (U_SUCCESS(status)) {
usearch_setCollator(m_strsrch_, coll->toUCollator(), &status);
}
}
void StringSearch::setPattern(const UnicodeString &pattern,
UErrorCode &status)
{
if (U_SUCCESS(status)) {
m_pattern_ = pattern;
usearch_setPattern(m_strsrch_, m_pattern_.getBuffer(), m_pattern_.length(),
&status);
}
}
const UnicodeString & StringSearch::getPattern() const
{
return m_pattern_;
}
// public methods ----------------------------------------------------
void StringSearch::reset()
{
usearch_reset(m_strsrch_);
}
SearchIterator * StringSearch::safeClone(void) const
{
UErrorCode status = U_ZERO_ERROR;
StringSearch *result = new StringSearch(m_pattern_, m_text_,
getCollator(),
m_breakiterator_,
status);
/* test for NULL */
if (result == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
return 0;
}
result->setOffset(getOffset(), status);
result->setMatchStart(m_strsrch_->search->matchedIndex);
result->setMatchLength(m_strsrch_->search->matchedLength);
if (U_FAILURE(status)) {
return NULL;
}
return result;
}
// protected method -------------------------------------------------
int32_t StringSearch::handleNext(int32_t position, UErrorCode &status)
{
// values passed here are already in the pre-shift position
if (U_SUCCESS(status)) {
if (m_strsrch_->pattern.cesLength == 0) {
m_search_->matchedIndex =
m_search_->matchedIndex == USEARCH_DONE ?
getOffset() : m_search_->matchedIndex + 1;
m_search_->matchedLength = 0;
ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex,
&status);
if (m_search_->matchedIndex == m_search_->textLength) {
m_search_->matchedIndex = USEARCH_DONE;
}
}
else {
// looking at usearch.cpp, this part is shifted out to
// StringSearch instead of SearchIterator because m_strsrch_ is
// not accessible in SearchIterator
#if 0
if (position + m_strsrch_->pattern.defaultShiftSize
> m_search_->textLength) {
setMatchNotFound();
return USEARCH_DONE;
}
#endif
if (m_search_->matchedLength <= 0) {
// the flipping direction issue has already been handled
// in next()
// for boundary check purposes. this will ensure that the
// next match will not preceed the current offset
// note search->matchedIndex will always be set to something
// in the code
m_search_->matchedIndex = position - 1;
}
ucol_setOffset(m_strsrch_->textIter, position, &status);
#if 0
for (;;) {
if (m_search_->isCanonicalMatch) {
// can't use exact here since extra accents are allowed.
usearch_handleNextCanonical(m_strsrch_, &status);
}
else {
usearch_handleNextExact(m_strsrch_, &status);
}
if (U_FAILURE(status)) {
return USEARCH_DONE;
}
if (m_breakiterator_ == NULL
#if !UCONFIG_NO_BREAK_ITERATION
||
m_search_->matchedIndex == USEARCH_DONE ||
(m_breakiterator_->isBoundary(m_search_->matchedIndex) &&
m_breakiterator_->isBoundary(m_search_->matchedIndex +
m_search_->matchedLength))
#endif
) {
if (m_search_->matchedIndex == USEARCH_DONE) {
ucol_setOffset(m_strsrch_->textIter,
m_search_->textLength, &status);
}
else {
ucol_setOffset(m_strsrch_->textIter,
m_search_->matchedIndex, &status);
}
return m_search_->matchedIndex;
}
}
#else
// if m_strsrch_->breakIter is always the same as m_breakiterator_
// then we don't need to check the match boundaries here because
// usearch_handleNextXXX will already have done it.
if (m_search_->isCanonicalMatch) {
// *could* actually use exact here 'cause no extra accents allowed...
usearch_handleNextCanonical(m_strsrch_, &status);
} else {
usearch_handleNextExact(m_strsrch_, &status);
}
if (U_FAILURE(status)) {
return USEARCH_DONE;
}
if (m_search_->matchedIndex == USEARCH_DONE) {
ucol_setOffset(m_strsrch_->textIter, m_search_->textLength, &status);
} else {
ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex, &status);
}
return m_search_->matchedIndex;
#endif
}
}
return USEARCH_DONE;
}
int32_t StringSearch::handlePrev(int32_t position, UErrorCode &status)
{
// values passed here are already in the pre-shift position
if (U_SUCCESS(status)) {
if (m_strsrch_->pattern.cesLength == 0) {
m_search_->matchedIndex =
(m_search_->matchedIndex == USEARCH_DONE ? getOffset() :
m_search_->matchedIndex);
if (m_search_->matchedIndex == 0) {
setMatchNotFound();
}
else {
m_search_->matchedIndex --;
ucol_setOffset(m_strsrch_->textIter, m_search_->matchedIndex,
&status);
m_search_->matchedLength = 0;
}
}
else {
// looking at usearch.cpp, this part is shifted out to
// StringSearch instead of SearchIterator because m_strsrch_ is
// not accessible in SearchIterator
#if 0
if (!m_search_->isOverlap &&
position - m_strsrch_->pattern.defaultShiftSize < 0) {
setMatchNotFound();
return USEARCH_DONE;
}
for (;;) {
if (m_search_->isCanonicalMatch) {
// can't use exact here since extra accents are allowed.
usearch_handlePreviousCanonical(m_strsrch_, &status);
}
else {
usearch_handlePreviousExact(m_strsrch_, &status);
}
if (U_FAILURE(status)) {
return USEARCH_DONE;
}
if (m_breakiterator_ == NULL
#if !UCONFIG_NO_BREAK_ITERATION
||
m_search_->matchedIndex == USEARCH_DONE ||
(m_breakiterator_->isBoundary(m_search_->matchedIndex) &&
m_breakiterator_->isBoundary(m_search_->matchedIndex +
m_search_->matchedLength))
#endif
) {
return m_search_->matchedIndex;
}
}
#else
ucol_setOffset(m_strsrch_->textIter, position, &status);
if (m_search_->isCanonicalMatch) {
// *could* use exact match here since extra accents *not* allowed!
usearch_handlePreviousCanonical(m_strsrch_, &status);
} else {
usearch_handlePreviousExact(m_strsrch_, &status);
}
if (U_FAILURE(status)) {
return USEARCH_DONE;
}
return m_search_->matchedIndex;
#endif
}
return m_search_->matchedIndex;
}
return USEARCH_DONE;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_COLLATION */