mirror of
https://github.com/nodejs/node.git
synced 2025-04-29 06:19:07 +00:00

Some checks failed
Coverage Linux (without intl) / coverage-linux-without-intl (push) Waiting to run
Coverage Linux / coverage-linux (push) Waiting to run
Coverage Windows / coverage-windows (push) Waiting to run
Test and upload documentation to artifacts / build-docs (push) Waiting to run
Linters / lint-addon-docs (push) Waiting to run
Linters / lint-cpp (push) Waiting to run
Linters / format-cpp (push) Waiting to run
Linters / lint-js-and-md (push) Waiting to run
Linters / lint-py (push) Waiting to run
Linters / lint-yaml (push) Waiting to run
Linters / lint-sh (push) Waiting to run
Linters / lint-codeowners (push) Waiting to run
Linters / lint-pr-url (push) Waiting to run
Linters / lint-readme (push) Waiting to run
Notify on Push / Notify on Force Push on `main` (push) Waiting to run
Notify on Push / Notify on Push on `main` that lacks metadata (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
License update / update_license (push) Has been cancelled
Find inactive collaborators / find (push) Has been cancelled
PR-URL: https://github.com/nodejs/node/pull/57455 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Steven R Loomis <srl295@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com>
925 lines
25 KiB
C++
925 lines
25 KiB
C++
// © 2024 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
|
|
#include "unicode/utypes.h"
|
|
|
|
#if !UCONFIG_NO_NORMALIZATION
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
#if !UCONFIG_NO_MF2
|
|
|
|
#include "unicode/messageformat2_data_model.h"
|
|
#include "messageformat2_allocation.h"
|
|
#include "messageformat2_macros.h"
|
|
#include "uvector.h"
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
namespace message2 {
|
|
|
|
// Implementation
|
|
|
|
//------------------ SelectorKeys
|
|
|
|
const Key* SelectorKeys::getKeysInternal() const {
|
|
return keys.getAlias();
|
|
}
|
|
|
|
// Lexically order key lists
|
|
bool SelectorKeys::operator<(const SelectorKeys& other) const {
|
|
// Handle key lists of different sizes first --
|
|
// this case does have to be handled (even though it would
|
|
// reflect a data model error) because of the need to produce
|
|
// partial output
|
|
if (len < other.len) {
|
|
return true;
|
|
}
|
|
if (len > other.len) {
|
|
return false;
|
|
}
|
|
|
|
for (int32_t i = 0; i < len; i++) {
|
|
if (keys[i] < other.keys[i]) {
|
|
return true;
|
|
}
|
|
if (!(keys[i] == other.keys[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
// If we've reached here, all keys must be equal
|
|
return false;
|
|
}
|
|
|
|
SelectorKeys::Builder::Builder(UErrorCode& status) {
|
|
keys = createUVector(status);
|
|
}
|
|
|
|
SelectorKeys::Builder& SelectorKeys::Builder::add(Key&& key, UErrorCode& status) noexcept {
|
|
U_ASSERT(keys != nullptr);
|
|
if (U_SUCCESS(status)) {
|
|
Key* k = create<Key>(std::move(key), status);
|
|
keys->adoptElement(k, status);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
SelectorKeys SelectorKeys::Builder::build(UErrorCode& status) const {
|
|
if (U_FAILURE(status)) {
|
|
return {};
|
|
}
|
|
U_ASSERT(keys != nullptr);
|
|
return SelectorKeys(*keys, status);
|
|
}
|
|
|
|
SelectorKeys::Builder::~Builder() {
|
|
if (keys != nullptr) {
|
|
delete keys;
|
|
}
|
|
}
|
|
|
|
SelectorKeys::SelectorKeys(const UVector& ks, UErrorCode& status) : len(ks.size()) {
|
|
Key* result = copyVectorToArray<Key>(ks, status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
keys.adoptInstead(result);
|
|
}
|
|
|
|
SelectorKeys& SelectorKeys::operator=(SelectorKeys other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
SelectorKeys::SelectorKeys(const SelectorKeys& other) : len(other.len) {
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
if (len != 0) {
|
|
keys.adoptInstead(copyArray(other.keys.getAlias(), len, localErrorCode));
|
|
}
|
|
if (U_FAILURE(localErrorCode)) {
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
SelectorKeys::~SelectorKeys() {
|
|
len = 0;
|
|
}
|
|
|
|
//------------------ Literal
|
|
|
|
bool Literal::operator<(const Literal& other) const {
|
|
// Ignore quoting for the purposes of ordering
|
|
return contents < other.contents;
|
|
}
|
|
|
|
bool Literal::operator==(const Literal& other) const {
|
|
// Ignore quoting for the purposes of ordering
|
|
return contents == other.contents;
|
|
}
|
|
|
|
UnicodeString Literal::quoted() const {
|
|
UnicodeString result(PIPE);
|
|
result += unquoted();
|
|
result += PIPE;
|
|
return result;
|
|
}
|
|
|
|
const UnicodeString& Literal::unquoted() const { return contents; }
|
|
|
|
Literal& Literal::operator=(Literal other) noexcept {
|
|
swap(*this, other);
|
|
|
|
return *this;
|
|
}
|
|
|
|
Literal::~Literal() {
|
|
thisIsQuoted = false;
|
|
}
|
|
|
|
//------------------ Operand
|
|
|
|
Operand::Operand(const Operand& other) : contents(other.contents) {}
|
|
|
|
Operand& Operand::operator=(Operand other) noexcept {
|
|
swap(*this, other);
|
|
|
|
return *this;
|
|
}
|
|
|
|
UBool Operand::isVariable() const {
|
|
return (contents.has_value() && std::holds_alternative<VariableName>(*contents));
|
|
}
|
|
UBool Operand::isLiteral() const {
|
|
return (contents.has_value() && std::holds_alternative<Literal>(*contents));
|
|
}
|
|
UBool Operand::isNull() const { return !contents.has_value(); }
|
|
|
|
const Literal& Operand::asLiteral() const {
|
|
U_ASSERT(isLiteral());
|
|
return *(std::get_if<Literal>(&(*contents)));
|
|
}
|
|
|
|
const VariableName& Operand::asVariable() const {
|
|
U_ASSERT(isVariable());
|
|
return *(std::get_if<VariableName>(&(*contents)));
|
|
}
|
|
|
|
Operand::~Operand() {}
|
|
|
|
//---------------- Key
|
|
|
|
Key& Key::operator=(Key other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
bool Key::operator<(const Key& other) const {
|
|
// Arbitrarily treat * as greater than all concrete keys
|
|
if (isWildcard()) {
|
|
return false;
|
|
}
|
|
if (other.isWildcard()) {
|
|
return true;
|
|
}
|
|
return (asLiteral() < other.asLiteral());
|
|
}
|
|
|
|
bool Key::operator==(const Key& other) const {
|
|
if (isWildcard()) {
|
|
return other.isWildcard();
|
|
}
|
|
if (other.isWildcard()) {
|
|
return false;
|
|
}
|
|
return (asLiteral() == other.asLiteral());
|
|
}
|
|
|
|
const Literal& Key::asLiteral() const {
|
|
U_ASSERT(!isWildcard());
|
|
return *contents;
|
|
}
|
|
|
|
Key::~Key() {}
|
|
|
|
//------------------------ Operator
|
|
|
|
OptionMap::OptionMap(const UVector& opts, UErrorCode& status) : len(opts.size()) {
|
|
Option* result = copyVectorToArray<Option>(opts, status);
|
|
if (U_FAILURE(status)) {
|
|
bogus = true;
|
|
return;
|
|
}
|
|
options.adoptInstead(result);
|
|
bogus = false;
|
|
}
|
|
|
|
OptionMap::OptionMap(const OptionMap& other) : len(other.len) {
|
|
U_ASSERT(!other.bogus);
|
|
if (len == 0) {
|
|
bogus = false;
|
|
return;
|
|
}
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
Option* result = copyArray(other.options.getAlias(), len, localErrorCode);
|
|
if (U_FAILURE(localErrorCode)) {
|
|
bogus = true;
|
|
return;
|
|
}
|
|
bogus = false;
|
|
options.adoptInstead(result);
|
|
}
|
|
|
|
OptionMap& OptionMap::operator=(OptionMap other) {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
const Option& OptionMap::getOption(int32_t i, UErrorCode& status) const {
|
|
if (U_FAILURE(status) || bogus) {
|
|
if (bogus) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
}
|
|
} else {
|
|
U_ASSERT(options.isValid());
|
|
U_ASSERT(i < len);
|
|
}
|
|
return options[i];
|
|
}
|
|
|
|
int32_t OptionMap::size() const {
|
|
U_ASSERT(options.isValid() || len == 0);
|
|
return len;
|
|
}
|
|
|
|
OptionMap::~OptionMap() {}
|
|
|
|
OptionMap OptionMap::Builder::build(UErrorCode& status) {
|
|
return OptionMap(*options, status);
|
|
}
|
|
|
|
OptionMap::Builder::Builder(UErrorCode& status) {
|
|
options = createStringUVector(status);
|
|
}
|
|
|
|
OptionMap::Builder::Builder(OptionMap::Builder&& other) {
|
|
checkDuplicates = other.checkDuplicates;
|
|
options = other.options;
|
|
other.options = nullptr;
|
|
}
|
|
|
|
OptionMap::Builder& OptionMap::Builder::operator=(OptionMap::Builder other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
/* static */ OptionMap::Builder OptionMap::Builder::attributes(UErrorCode& status) {
|
|
Builder b(status);
|
|
// The same code is re-used for representing attributes and options.
|
|
// Duplicate attributes are allowed, while duplicate options are disallowed.
|
|
b.checkDuplicates = false;
|
|
return b;
|
|
}
|
|
|
|
static UBool hasOptionNamed(const UVector& v, const UnicodeString& s) {
|
|
for (int32_t i = 0; i < v.size(); i++) {
|
|
const Option* opt = static_cast<Option*>(v[i]);
|
|
U_ASSERT(opt != nullptr);
|
|
if (opt->getName() == s) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
OptionMap::Builder& OptionMap::Builder::add(Option&& opt, UErrorCode& status) {
|
|
THIS_ON_ERROR(status);
|
|
|
|
// If the option name is already in the map, emit a data model error
|
|
if (checkDuplicates && hasOptionNamed(*options, opt.getName())) {
|
|
status = U_MF_DUPLICATE_OPTION_NAME_ERROR;
|
|
} else {
|
|
Option* newOption = create<Option>(std::move(opt), status);
|
|
options->adoptElement(newOption, status);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
OptionMap::Builder::~Builder() {
|
|
if (options != nullptr) {
|
|
delete options;
|
|
}
|
|
}
|
|
|
|
const OptionMap& Operator::getOptionsInternal() const {
|
|
return options;
|
|
}
|
|
|
|
Option::Option(const Option& other): name(other.name), rand(other.rand) {}
|
|
|
|
Option& Option::operator=(Option other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
Option::~Option() {}
|
|
|
|
Operator::Builder::Builder(UErrorCode& status) : options(OptionMap::Builder(status)) {}
|
|
|
|
Operator::Builder& Operator::Builder::setFunctionName(FunctionName&& func) {
|
|
functionName = std::move(func);
|
|
return *this;
|
|
}
|
|
|
|
const FunctionName& Operator::getFunctionName() const {
|
|
return name;
|
|
}
|
|
|
|
Operator::Builder& Operator::Builder::addOption(const UnicodeString &key, Operand&& value, UErrorCode& errorCode) noexcept {
|
|
THIS_ON_ERROR(errorCode);
|
|
|
|
options.add(Option(key, std::move(value)), errorCode);
|
|
return *this;
|
|
}
|
|
|
|
Operator Operator::Builder::build(UErrorCode& errorCode) {
|
|
return Operator(functionName, options.build(errorCode));
|
|
}
|
|
|
|
Operator::Operator(const Operator& other) noexcept
|
|
: name(other.name), options(other.options) {}
|
|
|
|
Operator& Operator::operator=(Operator other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
// Function call
|
|
|
|
Operator::Operator(const FunctionName& f, const OptionMap& opts) : name(f), options(opts) {}
|
|
|
|
Operator::Builder::~Builder() {}
|
|
|
|
Operator::~Operator() {}
|
|
|
|
// ------------ Markup
|
|
|
|
Markup::Builder::Builder(UErrorCode& status)
|
|
: options(OptionMap::Builder(status)), attributes(OptionMap::Builder::attributes(status)) {}
|
|
|
|
Markup::Markup(UMarkupType ty, UnicodeString n, OptionMap&& o, OptionMap&& a)
|
|
: type(ty), name(n), options(std::move(o)), attributes(std::move(a)) {}
|
|
|
|
Markup::Builder& Markup::Builder::addOption(const UnicodeString &key,
|
|
Operand&& value,
|
|
UErrorCode& errorCode) {
|
|
options.add(Option(key, std::move(value)), errorCode);
|
|
return *this;
|
|
}
|
|
|
|
Markup::Builder& Markup::Builder::addAttribute(const UnicodeString &key,
|
|
Operand&& value,
|
|
UErrorCode& errorCode) {
|
|
attributes.add(Option(key, std::move(value)), errorCode);
|
|
return *this;
|
|
}
|
|
|
|
Markup Markup::Builder::build(UErrorCode& errorCode) {
|
|
Markup result;
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
return result;
|
|
}
|
|
|
|
if (type == UMARKUP_COUNT || name.length() == 0) {
|
|
// One of `setOpen()`, `setClose()`, or `setStandalone()`
|
|
// must be called before calling build()
|
|
// setName() must be called before calling build()
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
} else {
|
|
result = Markup(type,
|
|
name,
|
|
options.build(errorCode),
|
|
attributes.build(errorCode));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Markup::Builder::~Builder() {}
|
|
|
|
Markup::~Markup() {}
|
|
|
|
// ------------ Expression
|
|
|
|
Expression::Builder::Builder(UErrorCode& status)
|
|
: attributes(OptionMap::Builder::attributes(status)) {}
|
|
|
|
UBool Expression::isStandaloneAnnotation() const {
|
|
return rand.isNull();
|
|
}
|
|
|
|
// Returns true for function calls with operands as well as
|
|
// standalone annotations.
|
|
UBool Expression::isFunctionCall() const {
|
|
return rator.has_value();
|
|
}
|
|
|
|
const Operator* Expression::getOperator(UErrorCode& status) const {
|
|
NULL_ON_ERROR(status);
|
|
|
|
if (!isFunctionCall()) {
|
|
status = U_INVALID_STATE_ERROR;
|
|
return nullptr;
|
|
}
|
|
U_ASSERT(rator);
|
|
return &(*rator);
|
|
}
|
|
|
|
// May return null operand
|
|
const Operand& Expression::getOperand() const { return rand; }
|
|
|
|
Expression::Builder& Expression::Builder::setOperand(Operand&& rAnd) {
|
|
hasOperand = true;
|
|
rand = std::move(rAnd);
|
|
return *this;
|
|
}
|
|
|
|
Expression::Builder& Expression::Builder::setOperator(Operator&& rAtor) {
|
|
hasOperator = true;
|
|
rator = std::move(rAtor);
|
|
return *this;
|
|
}
|
|
|
|
Expression::Builder& Expression::Builder::addAttribute(const UnicodeString& k,
|
|
Operand&& v,
|
|
UErrorCode& status) {
|
|
attributes.add(Option(k, std::move(v)), status);
|
|
return *this;
|
|
}
|
|
|
|
Expression Expression::Builder::build(UErrorCode& errorCode) {
|
|
Expression result;
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
return result;
|
|
}
|
|
|
|
if ((!hasOperand || rand.isNull()) && !hasOperator) {
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
return result;
|
|
}
|
|
|
|
OptionMap attributeMap = attributes.build(errorCode);
|
|
if (hasOperand && hasOperator) {
|
|
result = Expression(rator, rand, std::move(attributeMap));
|
|
} else if (hasOperand && !hasOperator) {
|
|
result = Expression(rand, std::move(attributeMap));
|
|
} else {
|
|
// rator is valid, rand is not valid
|
|
result = Expression(rator, std::move(attributeMap));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Expression::Expression() : rator(std::nullopt) {}
|
|
|
|
Expression::Expression(const Expression& other) : rator(other.rator), rand(other.rand), attributes(other.attributes) {}
|
|
|
|
Expression& Expression::operator=(Expression other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
Expression::Builder::~Builder() {}
|
|
|
|
Expression::~Expression() {}
|
|
|
|
// ----------- PatternPart
|
|
|
|
// PatternPart needs a copy constructor in order to make Pattern deeply copyable
|
|
// If !isRawText and the copy of the other expression fails,
|
|
// then isBogus() will be true for this PatternPart
|
|
PatternPart::PatternPart(const PatternPart& other) : piece(other.piece) {}
|
|
|
|
const Expression& PatternPart::contents() const {
|
|
U_ASSERT(isExpression());
|
|
return *std::get_if<Expression>(&piece);
|
|
}
|
|
|
|
const Markup& PatternPart::asMarkup() const {
|
|
U_ASSERT(isMarkup());
|
|
return *std::get_if<Markup>(&piece);
|
|
}
|
|
|
|
// Precondition: isText();
|
|
const UnicodeString& PatternPart::asText() const {
|
|
U_ASSERT(isText());
|
|
return *std::get_if<UnicodeString>(&piece);
|
|
}
|
|
|
|
PatternPart& PatternPart::operator=(PatternPart other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
PatternPart::~PatternPart() {}
|
|
|
|
// ---------------- Pattern
|
|
|
|
Pattern::Pattern(const UVector& ps, UErrorCode& status) : len(ps.size()) {
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
PatternPart* result = copyVectorToArray<PatternPart>(ps, status);
|
|
CHECK_ERROR(status);
|
|
parts.adoptInstead(result);
|
|
}
|
|
|
|
// Copy constructor
|
|
Pattern::Pattern(const Pattern& other) : len(other.len) {
|
|
U_ASSERT(!other.bogus);
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
if (len == 0) {
|
|
parts.adoptInstead(nullptr);
|
|
} else {
|
|
parts.adoptInstead(copyArray(other.parts.getAlias(), len, localErrorCode));
|
|
}
|
|
if (U_FAILURE(localErrorCode)) {
|
|
bogus = true;
|
|
}
|
|
}
|
|
|
|
int32_t Pattern::numParts() const {
|
|
U_ASSERT(!bogus);
|
|
return len;
|
|
}
|
|
|
|
const PatternPart& Pattern::getPart(int32_t i) const {
|
|
U_ASSERT(!bogus && i < numParts());
|
|
return parts[i];
|
|
}
|
|
|
|
Pattern::Builder::Builder(UErrorCode& status) {
|
|
parts = createUVector(status);
|
|
}
|
|
|
|
Pattern Pattern::Builder::build(UErrorCode& status) const noexcept {
|
|
if (U_FAILURE(status)) {
|
|
return {};
|
|
}
|
|
U_ASSERT(parts != nullptr);
|
|
return Pattern(*parts, status);
|
|
}
|
|
|
|
Pattern::Builder& Pattern::Builder::add(Expression&& part, UErrorCode& status) noexcept {
|
|
U_ASSERT(parts != nullptr);
|
|
if (U_SUCCESS(status)) {
|
|
PatternPart* l = create<PatternPart>(PatternPart(std::move(part)), status);
|
|
parts->adoptElement(l, status);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Pattern::Builder& Pattern::Builder::add(Markup&& part, UErrorCode& status) noexcept {
|
|
U_ASSERT(parts != nullptr);
|
|
if (U_SUCCESS(status)) {
|
|
PatternPart* l = create<PatternPart>(PatternPart(std::move(part)), status);
|
|
parts->adoptElement(l, status);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Pattern::Builder& Pattern::Builder::add(UnicodeString&& part, UErrorCode& status) noexcept {
|
|
U_ASSERT(parts != nullptr);
|
|
if (U_SUCCESS(status)) {
|
|
PatternPart* l = create<PatternPart>(PatternPart(std::move(part)), status);
|
|
parts->adoptElement(l, status);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Pattern& Pattern::operator=(Pattern other) noexcept {
|
|
swap(*this, other);
|
|
|
|
return *this;
|
|
}
|
|
|
|
Pattern::Builder::~Builder() {
|
|
if (parts != nullptr) {
|
|
delete parts;
|
|
}
|
|
}
|
|
|
|
Pattern::~Pattern() {}
|
|
|
|
// ---------------- Binding
|
|
|
|
const Expression& Binding::getValue() const {
|
|
return expr;
|
|
}
|
|
|
|
/* static */ Binding Binding::input(UnicodeString&& variableName, Expression&& rhs, UErrorCode& errorCode) {
|
|
Binding b;
|
|
if (U_SUCCESS(errorCode)) {
|
|
const Operand& rand = rhs.getOperand();
|
|
if (!(rand.isVariable() && (rand.asVariable() == variableName))) {
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
} else {
|
|
const Operator* rator = rhs.getOperator(errorCode);
|
|
bool hasOperator = U_SUCCESS(errorCode);
|
|
// Clear error code -- the "error" from the absent operator
|
|
// is handled
|
|
errorCode = U_ZERO_ERROR;
|
|
b = Binding(variableName, std::move(rhs));
|
|
b.local = false;
|
|
if (hasOperator) {
|
|
rator = b.getValue().getOperator(errorCode);
|
|
U_ASSERT(U_SUCCESS(errorCode));
|
|
b.annotation = rator;
|
|
} else {
|
|
b.annotation = nullptr;
|
|
}
|
|
U_ASSERT(!hasOperator || b.annotation != nullptr);
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
const OptionMap& Binding::getOptionsInternal() const {
|
|
U_ASSERT(annotation != nullptr);
|
|
return annotation->getOptionsInternal();
|
|
}
|
|
|
|
void Binding::updateAnnotation() {
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
const Operator* rator = expr.getOperator(localErrorCode);
|
|
if (U_FAILURE(localErrorCode)) {
|
|
return;
|
|
}
|
|
U_ASSERT(U_SUCCESS(localErrorCode));
|
|
annotation = rator;
|
|
}
|
|
|
|
Binding::Binding(const Binding& other) : var(other.var), expr(other.expr), local(other.local) {
|
|
updateAnnotation();
|
|
}
|
|
|
|
Binding& Binding::operator=(Binding other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
Binding::~Binding() {}
|
|
|
|
// --------------- Variant
|
|
|
|
Variant& Variant::operator=(Variant other) noexcept {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
Variant::Variant(const Variant& other) : k(other.k), p(other.p) {}
|
|
|
|
Variant::~Variant() {}
|
|
|
|
// ------------- Matcher
|
|
|
|
Matcher& Matcher::operator=(Matcher other) {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
Matcher::Matcher(const Matcher& other) {
|
|
U_ASSERT(!other.bogus);
|
|
numSelectors = other.numSelectors;
|
|
numVariants = other.numVariants;
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
selectors.adoptInstead(copyArray<VariableName>(other.selectors.getAlias(),
|
|
numSelectors,
|
|
localErrorCode));
|
|
variants.adoptInstead(copyArray<Variant>(other.variants.getAlias(),
|
|
numVariants,
|
|
localErrorCode));
|
|
if (U_FAILURE(localErrorCode)) {
|
|
bogus = true;
|
|
}
|
|
}
|
|
|
|
Matcher::Matcher(VariableName* ss, int32_t ns, Variant* vs, int32_t nv)
|
|
: selectors(ss), numSelectors(ns), variants(vs), numVariants(nv) {}
|
|
|
|
Matcher::~Matcher() {}
|
|
|
|
// --------------- MFDataModel
|
|
|
|
const Pattern& MFDataModel::getPattern() const {
|
|
if (std::holds_alternative<Matcher>(body)) {
|
|
// Return reference to empty pattern if this is a selectors message
|
|
return empty;
|
|
}
|
|
return *(std::get_if<Pattern>(&body));
|
|
}
|
|
|
|
// Returns nullptr if no bindings
|
|
const Binding* MFDataModel::getLocalVariablesInternal() const {
|
|
U_ASSERT(!bogus);
|
|
U_ASSERT(bindingsLen == 0 || bindings.isValid());
|
|
return bindings.getAlias();
|
|
}
|
|
|
|
const VariableName* MFDataModel::getSelectorsInternal() const {
|
|
U_ASSERT(!bogus);
|
|
U_ASSERT(!hasPattern());
|
|
return std::get_if<Matcher>(&body)->selectors.getAlias();
|
|
}
|
|
|
|
const Variant* MFDataModel::getVariantsInternal() const {
|
|
U_ASSERT(!bogus);
|
|
U_ASSERT(!hasPattern());
|
|
return std::get_if<Matcher>(&body)->variants.getAlias();
|
|
}
|
|
|
|
MFDataModel::Builder::Builder(UErrorCode& status) {
|
|
bindings = createUVector(status);
|
|
}
|
|
|
|
// Invalidate pattern and create selectors/variants if necessary
|
|
void MFDataModel::Builder::buildSelectorsMessage(UErrorCode& status) {
|
|
CHECK_ERROR(status);
|
|
|
|
if (hasPattern) {
|
|
selectors = createUVector(status);
|
|
variants = createUVector(status);
|
|
hasPattern = false;
|
|
}
|
|
hasPattern = false;
|
|
hasSelectors = true;
|
|
}
|
|
|
|
void MFDataModel::Builder::checkDuplicate(const VariableName& var, UErrorCode& status) const {
|
|
CHECK_ERROR(status);
|
|
|
|
// This means that handling declarations is quadratic in the number of variables,
|
|
// but the `UVector` of locals in the builder could be changed to a `Hashtable`
|
|
// if that's a problem
|
|
// Note: this also doesn't check _all_ duplicate declaration errors,
|
|
// see MessageFormatter::Checker::checkDeclarations()
|
|
for (int32_t i = 0; i < bindings->size(); i++) {
|
|
if ((static_cast<Binding*>(bindings->elementAt(i)))->getVariable() == var) {
|
|
status = U_MF_DUPLICATE_DECLARATION_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MFDataModel::Builder& MFDataModel::Builder::addBinding(Binding&& b, UErrorCode& status) {
|
|
if (U_SUCCESS(status)) {
|
|
U_ASSERT(bindings != nullptr);
|
|
checkDuplicate(b.getVariable(), status);
|
|
UErrorCode savedStatus = status;
|
|
if (status == U_MF_DUPLICATE_DECLARATION_ERROR) {
|
|
// Want to add the binding anyway even if it's a duplicate
|
|
status = U_ZERO_ERROR;
|
|
}
|
|
bindings->adoptElement(create<Binding>(std::move(b), status), status);
|
|
if (U_SUCCESS(status) || savedStatus == U_MF_DUPLICATE_DECLARATION_ERROR) {
|
|
status = savedStatus;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
MFDataModel::Builder& MFDataModel::Builder::addSelector(VariableName&& selector,
|
|
UErrorCode& status) {
|
|
THIS_ON_ERROR(status);
|
|
|
|
buildSelectorsMessage(status);
|
|
U_ASSERT(selectors != nullptr);
|
|
selectors->adoptElement(create<VariableName>(std::move(selector), status), status);
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*
|
|
`pattern` must be non-null
|
|
*/
|
|
MFDataModel::Builder& MFDataModel::Builder::addVariant(SelectorKeys&& keys, Pattern&& pattern, UErrorCode& errorCode) noexcept {
|
|
buildSelectorsMessage(errorCode);
|
|
Variant* v = create<Variant>(Variant(std::move(keys), std::move(pattern)), errorCode);
|
|
if (U_SUCCESS(errorCode)) {
|
|
variants->adoptElement(v, errorCode);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
MFDataModel::Builder& MFDataModel::Builder::setPattern(Pattern&& pat) {
|
|
pattern = std::move(pat);
|
|
hasPattern = true;
|
|
hasSelectors = false;
|
|
// Invalidate variants
|
|
if (variants != nullptr) {
|
|
variants->removeAllElements();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
MFDataModel::MFDataModel(const MFDataModel& other) : body(Pattern()) {
|
|
U_ASSERT(!other.bogus);
|
|
|
|
UErrorCode localErrorCode = U_ZERO_ERROR;
|
|
|
|
if (other.hasPattern()) {
|
|
body = *std::get_if<Pattern>(&other.body);
|
|
} else {
|
|
const VariableName* otherSelectors = other.getSelectorsInternal();
|
|
const Variant* otherVariants = other.getVariantsInternal();
|
|
int32_t numSelectors = other.numSelectors();
|
|
int32_t numVariants = other.numVariants();
|
|
VariableName* copiedSelectors = copyArray(otherSelectors, numSelectors, localErrorCode);
|
|
Variant* copiedVariants = copyArray(otherVariants, numVariants, localErrorCode);
|
|
if (U_FAILURE(localErrorCode)) {
|
|
bogus = true;
|
|
return;
|
|
}
|
|
body = Matcher(copiedSelectors, numSelectors, copiedVariants, numVariants);
|
|
}
|
|
|
|
bindingsLen = other.bindingsLen;
|
|
if (bindingsLen > 0) {
|
|
bindings.adoptInstead(copyArray(other.bindings.getAlias(), bindingsLen, localErrorCode));
|
|
}
|
|
if (U_FAILURE(localErrorCode)) {
|
|
bogus = true;
|
|
}
|
|
}
|
|
|
|
MFDataModel::MFDataModel(const MFDataModel::Builder& builder, UErrorCode& errorCode) noexcept : body(Pattern()) {
|
|
CHECK_ERROR(errorCode);
|
|
|
|
if (builder.hasPattern) {
|
|
body.emplace<Pattern>(builder.pattern);
|
|
} else {
|
|
U_ASSERT(builder.variants != nullptr);
|
|
U_ASSERT(builder.selectors != nullptr);
|
|
int32_t numVariants = builder.variants->size();
|
|
int32_t numSelectors = builder.selectors->size();
|
|
LocalArray<Variant> variants(copyVectorToArray<Variant>(*builder.variants, errorCode), errorCode);
|
|
LocalArray<VariableName> selectors(copyVectorToArray<VariableName>(*builder.selectors,
|
|
errorCode),
|
|
errorCode);
|
|
if (U_FAILURE(errorCode)) {
|
|
bogus = true;
|
|
return;
|
|
}
|
|
body.emplace<Matcher>(Matcher(selectors.orphan(), numSelectors, variants.orphan(), numVariants));
|
|
}
|
|
|
|
U_ASSERT(builder.bindings != nullptr);
|
|
bindingsLen = builder.bindings->size();
|
|
if (bindingsLen > 0) {
|
|
bindings.adoptInstead(copyVectorToArray<Binding>(*builder.bindings, errorCode));
|
|
}
|
|
if (U_FAILURE(errorCode)) {
|
|
bogus = true;
|
|
}
|
|
}
|
|
|
|
MFDataModel::MFDataModel() : body(Pattern()) {}
|
|
|
|
MFDataModel& MFDataModel::operator=(MFDataModel other) noexcept {
|
|
U_ASSERT(!other.bogus);
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
MFDataModel MFDataModel::Builder::build(UErrorCode& errorCode) const noexcept {
|
|
if (U_FAILURE(errorCode)) {
|
|
return {};
|
|
}
|
|
if (!hasPattern && !hasSelectors) {
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
}
|
|
return MFDataModel(*this, errorCode);
|
|
}
|
|
|
|
MFDataModel::~MFDataModel() {}
|
|
MFDataModel::Builder::~Builder() {
|
|
if (selectors != nullptr) {
|
|
delete selectors;
|
|
}
|
|
if (variants != nullptr) {
|
|
delete variants;
|
|
}
|
|
if (bindings != nullptr) {
|
|
delete bindings;
|
|
}
|
|
}
|
|
} // namespace message2
|
|
|
|
U_NAMESPACE_END
|
|
|
|
#endif /* #if !UCONFIG_NO_MF2 */
|
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
|
|
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|