#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif
#include "src/objects/js-break-iterator.h"
#include <memory>
#include <utility>
#include "src/objects/intl-objects.h"
#include "src/objects/js-break-iterator-inl.h"
#include "src/objects/managed-inl.h"
#include "src/objects/option-utils.h"
#include "unicode/brkiter.h"
#include "unicode/unistr.h"
namespace v8 {
namespace internal {
MaybeDirectHandle<JSV8BreakIterator> JSV8BreakIterator::New(
Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales,
DirectHandle<Object> options_obj, const char* service) {
Factory* factory = isolate->factory();
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, MaybeDirectHandle<JSV8BreakIterator>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
DirectHandle<JSReceiver> options;
if (IsUndefined(*options_obj, isolate)) {
options = factory->NewJSObjectWithNullProto();
} else {
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, options_obj, service));
}
Maybe<Intl::MatcherOption> maybe_locale_matcher =
Intl::GetLocaleMatcher(isolate, options, service);
MAYBE_RETURN(maybe_locale_matcher, MaybeDirectHandle<JSV8BreakIterator>());
Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
Intl::ResolvedLocale r;
if (!Intl::ResolveLocale(isolate, JSV8BreakIterator::GetAvailableLocales(),
requested_locales, matcher, {})
.To(&r)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
enum class Type { CHARACTER, WORD, SENTENCE, LINE };
Maybe<Type> maybe_type = GetStringOption<Type>(
isolate, options, isolate->factory()->type_string(), service,
std::to_array<const std::string_view>(
{"word", "character", "sentence", "line"}),
std::array{Type::WORD, Type::CHARACTER, Type::SENTENCE, Type::LINE},
Type::WORD);
MAYBE_RETURN(maybe_type, MaybeDirectHandle<JSV8BreakIterator>());
Type type_enum = maybe_type.FromJust();
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::BreakIterator> break_iterator = nullptr;
switch (type_enum) {
case Type::CHARACTER:
break_iterator.reset(
icu::BreakIterator::createCharacterInstance(icu_locale, status));
break;
case Type::SENTENCE:
break_iterator.reset(
icu::BreakIterator::createSentenceInstance(icu_locale, status));
break;
case Type::LINE:
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kBreakIteratorTypeLine);
break_iterator.reset(
icu::BreakIterator::createLineInstance(icu_locale, status));
break;
default:
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kBreakIteratorTypeWord);
break_iterator.reset(
icu::BreakIterator::createWordInstance(icu_locale, status));
break;
}
if (U_FAILURE(status) || break_iterator == nullptr) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
}
isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator);
auto iterator_with_text =
std::make_shared<IcuBreakIteratorWithText>(std::move(break_iterator));
DirectHandle<Managed<IcuBreakIteratorWithText>> managed =
Managed<IcuBreakIteratorWithText>::From(isolate, 0,
std::move(iterator_with_text));
DirectHandle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
DirectHandle<JSV8BreakIterator> break_iterator_holder =
Cast<JSV8BreakIterator>(
isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
DisallowGarbageCollection no_gc;
break_iterator_holder->set_locale(*locale_str);
break_iterator_holder->set_icu_iterator_with_text(*managed);
return break_iterator_holder;
}
DirectHandle<JSObject> JSV8BreakIterator::ResolvedOptions(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator) {
Factory* factory = isolate->factory();
const auto as_string = [isolate](icu::BreakIterator* break_iterator) {
std::unique_ptr<icu::BreakIterator> cloned_break_iterator(
break_iterator->clone());
icu::UnicodeString data("He is.");
cloned_break_iterator->setText(data);
switch (cloned_break_iterator->next()) {
case 1:
return isolate->factory()->character_string();
case 2:
return isolate->factory()->word_string();
case 3:
return isolate->factory()->line_string();
case 6:
return isolate->factory()->sentence_string();
default:
UNREACHABLE();
}
};
DirectHandle<JSObject> result =
factory->NewJSObject(isolate->object_function());
DirectHandle<String> locale(break_iterator->locale(), isolate);
JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
NONE);
JSObject::AddProperty(
isolate, result, factory->type_string(),
as_string(break_iterator->icu_iterator_with_text()->raw()->iterator()),
NONE);
return result;
}
void JSV8BreakIterator::AdoptText(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator,
DirectHandle<String> text) {
break_iterator->icu_iterator_with_text()->raw()->SetText(isolate, text);
}
DirectHandle<Object> JSV8BreakIterator::Current(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->icu_iterator_with_text()->raw()->iterator()->current());
}
DirectHandle<Object> JSV8BreakIterator::First(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->icu_iterator_with_text()->raw()->iterator()->first());
}
DirectHandle<Object> JSV8BreakIterator::Next(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator) {
return isolate->factory()->NewNumberFromInt(
break_iterator->icu_iterator_with_text()->raw()->iterator()->next());
}
Tagged<String> JSV8BreakIterator::BreakType(
Isolate* isolate, DirectHandle<JSV8BreakIterator> break_iterator) {
int32_t status = break_iterator->icu_iterator_with_text()
->raw()
->iterator()
->getRuleStatus();
if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
return ReadOnlyRoots(isolate).none_string();
}
if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
return ReadOnlyRoots(isolate).number_string();
}
if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
return ReadOnlyRoots(isolate).letter_string();
}
if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
return ReadOnlyRoots(isolate).kana_string();
}
if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
return ReadOnlyRoots(isolate).ideo_string();
}
return ReadOnlyRoots(isolate).unknown_string();
}
const std::set<std::string>& JSV8BreakIterator::GetAvailableLocales() {
return Intl::GetAvailableLocales();
}
}
}