Skip to content

Commit a3b6d31

Browse files
committed
Add Factory for MF2
Also add unit test and data driven unit test Test data based on inflection/test/resources/inflection/dialog/inflection/*.xml Test formatter while the <result> is not empty Test Selector while the <result> is empty, and there are one attribute which is not "exists".
1 parent d9fc548 commit a3b6d31

File tree

3 files changed

+519
-0
lines changed

3 files changed

+519
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright 2025 Google Inc. All rights reserved.
3+
*/
4+
#include <iostream>
5+
#include "inflection/message2/MF2Factory.hpp"
6+
7+
#include "inflection/dialog/InflectableStringConcept.hpp"
8+
#include "inflection/dialog/LocalizedCommonConceptFactoryProvider.hpp"
9+
#include "inflection/dialog/SemanticFeatureModel.hpp"
10+
#include "inflection/dialog/SpeakableString.hpp"
11+
#include "inflection/lang/features/LanguageGrammarFeatures.hpp"
12+
#include "inflection/util/ULocale.hpp"
13+
14+
#include <unicode/locid.h>
15+
#include <unicode/messageformat2.h>
16+
#include <unicode/messageformat2_function_registry.h>
17+
#include <unicode/messageformat2_formattable.h>
18+
19+
using U_ICU_NAMESPACE::Locale;
20+
using U_ICU_NAMESPACE::UnicodeString;
21+
using U_ICU_NAMESPACE::message2::Formatter;
22+
using U_ICU_NAMESPACE::message2::Formattable;
23+
using U_ICU_NAMESPACE::message2::FormattedValue;
24+
using U_ICU_NAMESPACE::message2::FormattedPlaceholder;
25+
using U_ICU_NAMESPACE::message2::FormatterFactory;
26+
using U_ICU_NAMESPACE::message2::FunctionOptions;
27+
using U_ICU_NAMESPACE::message2::FunctionOptionsMap;
28+
using U_ICU_NAMESPACE::message2::MessageArguments;
29+
using U_ICU_NAMESPACE::message2::MessageFormatter;
30+
using U_ICU_NAMESPACE::message2::MFFunctionRegistry;
31+
using U_ICU_NAMESPACE::message2::Selector;
32+
using U_ICU_NAMESPACE::message2::SelectorFactory;
33+
34+
namespace inflection::message2 {
35+
36+
class InflectionFormatterFactory : public FormatterFactory {
37+
public:
38+
Formatter* createFormatter(const Locale&, UErrorCode&) override;
39+
};
40+
41+
class InflectionSelectorFactory : public SelectorFactory {
42+
public:
43+
Selector* createSelector(const Locale&, UErrorCode&) const override;
44+
};
45+
46+
icu::message2::FormatterFactory* createFormatterFactory() {
47+
return new InflectionFormatterFactory();
48+
}
49+
50+
icu::message2::SelectorFactory* createSelectorFactory() {
51+
return new InflectionSelectorFactory();
52+
}
53+
54+
const inflection::dialog::SemanticFeatureModel* GetSemanticFeatureModel(
55+
const Locale& locale) {
56+
return ::inflection::dialog::LocalizedCommonConceptFactoryProvider
57+
::getDefaultCommonConceptFactoryProvider()
58+
->getCommonConceptFactory(
59+
inflection::util::ULocale(locale.getLanguage(), locale.getCountry()))
60+
->getSemanticFeatureModel();
61+
}
62+
63+
class InflectionFormatter : public Formatter {
64+
public:
65+
FormattedPlaceholder format(FormattedPlaceholder&&, FunctionOptions&& opts,
66+
UErrorCode& errorCode) const override;
67+
InflectionFormatter(const inflection::dialog::SemanticFeatureModel* model)
68+
: model(model) {
69+
}
70+
private:
71+
const ::inflection::dialog::SemanticFeatureModel* model;
72+
};
73+
74+
Formatter* InflectionFormatterFactory::createFormatter(
75+
const Locale& locale, UErrorCode& errorCode) {
76+
if (U_FAILURE(errorCode)) { return nullptr; }
77+
78+
Formatter* result = new InflectionFormatter(GetSemanticFeatureModel(locale));
79+
if (result == nullptr) {
80+
errorCode = U_MEMORY_ALLOCATION_ERROR;
81+
}
82+
return result;
83+
}
84+
85+
FormattedPlaceholder InflectionFormatter::format(
86+
FormattedPlaceholder&& arg, FunctionOptions&& options,
87+
UErrorCode& errorCode) const {
88+
if (U_FAILURE(errorCode)) { return {}; }
89+
90+
// Argument must be present
91+
if (!arg.canFormat()) {
92+
errorCode = U_MF_FORMATTING_ERROR;
93+
return FormattedPlaceholder("inflection");
94+
}
95+
96+
// Assumes the argument is not-yet-formatted
97+
const Formattable& toFormat = arg.asFormattable();
98+
UnicodeString result;
99+
100+
switch (toFormat.getType()) {
101+
case UFMT_STRING: {
102+
inflection::dialog::SpeakableString input(toFormat.getString(errorCode));
103+
inflection::dialog::InflectableStringConcept stringConcept(model, input);
104+
for (const auto& [key, value] : options.getOptions()) {
105+
auto constraint = model->getFeature(key);
106+
if (constraint != nullptr) {
107+
stringConcept.putConstraint(*constraint, value.getString(errorCode));
108+
}
109+
}
110+
result += stringConcept.toSpeakableString()->getPrint();
111+
break;
112+
}
113+
default: {
114+
result += toFormat.getString(errorCode);
115+
break;
116+
}
117+
}
118+
119+
return FormattedPlaceholder(arg, FormattedValue(std::move(result)));
120+
}
121+
122+
class InflectionSelector : public Selector {
123+
public:
124+
void selectKey(FormattedPlaceholder &&arg, FunctionOptions &&options,
125+
const UnicodeString *keys, int32_t keysLen,
126+
UnicodeString *prefs, int32_t &prefsLen, UErrorCode &status) const override;
127+
128+
InflectionSelector(const inflection::dialog::SemanticFeatureModel* model)
129+
: model(model) {
130+
}
131+
132+
private:
133+
const ::inflection::dialog::SemanticFeatureModel* model;
134+
};
135+
136+
Selector* InflectionSelectorFactory::createSelector(
137+
const Locale& locale, UErrorCode& errorCode) const {
138+
if (U_FAILURE(errorCode)) { return nullptr; }
139+
140+
Selector* result = new InflectionSelector(GetSemanticFeatureModel(locale));
141+
if (result == nullptr) {
142+
errorCode = U_MEMORY_ALLOCATION_ERROR;
143+
}
144+
return result;
145+
}
146+
147+
void InflectionSelector::selectKey(
148+
FormattedPlaceholder &&arg, FunctionOptions &&options,
149+
const UnicodeString *keys, int32_t keysLen,
150+
UnicodeString *prefs, int32_t &prefsLen, UErrorCode &errorCode) const {
151+
if (U_FAILURE(errorCode)) { return; }
152+
// Argument must be present
153+
if (!arg.canFormat()) {
154+
errorCode = U_MF_SELECTOR_ERROR;
155+
return;
156+
}
157+
158+
// Assumes the argument is not-yet-formatted
159+
const Formattable& toFormat = arg.asFormattable();
160+
prefsLen = 0;
161+
auto opt = options.getOptions();
162+
if (toFormat.getType() == UFMT_STRING) {
163+
inflection::dialog::SpeakableString input(toFormat.getString(errorCode));
164+
inflection::dialog::InflectableStringConcept stringConcept(model, input);
165+
if (!opt.contains(u"select")) {
166+
errorCode = U_MF_SELECTOR_ERROR;
167+
return;
168+
}
169+
for (const auto& [key, value] : options.getOptions()) {
170+
auto constraint = model->getFeature(key);
171+
if (constraint != nullptr) {
172+
stringConcept.putConstraint(*constraint, value.getString(errorCode));
173+
}
174+
}
175+
auto value = model->getFeature(opt.at(u"select").getString(errorCode));
176+
UnicodeString feature;
177+
if (value != nullptr) {
178+
auto result = stringConcept.getFeatureValue(*value);
179+
if (result != nullptr) {
180+
feature = result->getPrint();
181+
}
182+
}
183+
184+
for (int i = 0; i < keysLen; i++) {
185+
if (feature == keys[i]) {
186+
prefs[prefsLen++] = keys[i];
187+
}
188+
}
189+
}
190+
return;
191+
}
192+
193+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2025 Google Inc. All rights reserved.
3+
*/
4+
#pragma once
5+
6+
#include <inflection/api.h>
7+
// INFLECTION_CLASS_API
8+
9+
#include <unicode/uversion.h>
10+
11+
namespace U_ICU_NAMESPACE::message2 {
12+
class FormatterFactory;
13+
class SelectorFactory;
14+
}
15+
16+
namespace inflection::message2 {
17+
18+
INFLECTION_CLASS_API icu::message2::FormatterFactory* createFormatterFactory();
19+
INFLECTION_CLASS_API icu::message2::SelectorFactory* createSelectorFactory();
20+
21+
}

0 commit comments

Comments
 (0)