Skip to content

Commit d02d43b

Browse files
committed
ICU-23202 MF2 to LDML 48: update tests and :number, :integer, :offset, :percent functions
1 parent 678a05c commit d02d43b

File tree

16 files changed

+375
-215
lines changed

16 files changed

+375
-215
lines changed

icu4j/main/core/src/main/java/com/ibm/icu/message2/DateTimeFunctionFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,9 @@ public String formatToString(Object toFormat, Map<String, Object> variableOption
399399
"^(([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])){1}(T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]{1,3})?(Z|[+-]((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?$");
400400

401401
private static Integer safeParse(String str) {
402-
if (str == null || str.isEmpty())
402+
if (str == null || str.isEmpty()) {
403403
return null;
404+
}
404405
return Integer.parseInt(str);
405406
}
406407

icu4j/main/core/src/main/java/com/ibm/icu/message2/MFDataModelFormatter.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ class MFDataModelFormatter {
8888
.setFunction("number", new NumberFunctionFactory("number"))
8989
.setFunction("integer", new NumberFunctionFactory("integer"))
9090
.setFunction("currency", new NumberFunctionFactory("currency"))
91-
.setFunction("math", new NumberFunctionFactory("math"))
91+
.setFunction("percent", new NumberFunctionFactory("percent"))
92+
.setFunction("offset", new NumberFunctionFactory("offset"))
9293
.setDefaultFunctionNameForType(Integer.class, "number")
9394
.setDefaultFunctionNameForType(Double.class, "number")
9495
.setDefaultFunctionNameForType(Number.class, "number")
@@ -188,7 +189,16 @@ private Pattern findBestMatchingPattern(
188189
// spec: For each _selector_ `sel`, in source order,
189190
for (Expression sel : selectors) {
190191
// spec: Let `rv` be the resolved value of `sel`.
191-
FormattedPlaceholder fph = formatExpression(sel, variables, arguments);
192+
FormattedPlaceholder fph = null;
193+
if (sel instanceof MFDataModel.VariableExpression) {
194+
// If it is a `VariableExpression` then it is already resolved and in `variables`
195+
String key = ((MFDataModel.VariableExpression) sel).arg.name;
196+
fph = (FormattedPlaceholder) variables.get(key);
197+
}
198+
// Was not a `VariableExpression` or in `variables`
199+
if (fph == null) {
200+
fph = formatExpression(sel, variables, arguments);
201+
}
192202
String functionName = null;
193203
Object argument = null;
194204
MapWithNfcKeys options = new MapWithNfcKeys();

icu4j/main/core/src/main/java/com/ibm/icu/message2/NumberFunctionFactory.java

Lines changed: 164 additions & 104 deletions
Large diffs are not rendered by default.

icu4j/main/core/src/main/java/com/ibm/icu/message2/OptUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.util.Locale;
77
import java.util.Map;
8+
import java.util.Objects;
89
import java.util.regex.Matcher;
910
import java.util.regex.Pattern;
1011

@@ -85,7 +86,7 @@ static String getString(Map<String, Object> options, String key) {
8586

8687
static boolean reportErrors(Map<String, Object> options) {
8788
String reportErrors = getString(options, "icu:impl:errorPolicy");
88-
return "STRICT".equals(reportErrors);
89+
return Objects.equals(reportErrors, "STRICT");
8990
}
9091

9192
static boolean reportErrors(Map<String, Object> fixedOptions, Map<String, Object> variableOptions) {

icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/CoreTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
@SuppressWarnings({"static-method", "javadoc"})
1515
@RunWith(JUnit4.class)
1616
public class CoreTest extends CoreTestFmwk {
17+
private static final boolean DEBUG = false;
1718

1819
private static final String[] JSON_FILES = {
1920
"alias-selector-annotations.json",
@@ -39,8 +40,9 @@ public class CoreTest extends CoreTestFmwk {
3940
"spec/functions/date.json",
4041
"spec/functions/datetime.json",
4142
"spec/functions/integer.json",
42-
"spec/functions/math.json", // FAILS 2 / 16, chaining to select
43+
"spec/functions/offset.json",
4344
"spec/functions/number.json",
45+
"spec/functions/percent.json",
4446
"spec/functions/string.json",
4547
"spec/functions/time.json",
4648
"spec/pattern-selection.json",
@@ -59,8 +61,14 @@ public class CoreTest extends CoreTestFmwk {
5961
public void test() throws Exception {
6062
for (String jsonFile : JSON_FILES) {
6163
try (Reader reader = TestUtils.jsonReader(jsonFile)) {
64+
if (DEBUG) {
65+
System.out.println("==== " + jsonFile);
66+
}
6267
MF2Test tests = TestUtils.GSON.fromJson(reader, MF2Test.class);
6368
for (Unit unit : tests.tests) {
69+
if (DEBUG) {
70+
System.out.println(" " + unit);
71+
}
6472
TestUtils.runTestCase(tests.defaultTestProperties, unit);
6573
}
6674
}

icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/CustomFunctionPersonTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static class PersonNameFunctionImpl implements Function {
5353
final String length;
5454

5555
public PersonNameFunctionImpl(Object level, Object length) {
56-
this.useFormal = "formal".equals(level);
56+
this.useFormal = Objects.equals(level, "formal");
5757
this.length = Objects.toString(length);
5858
}
5959

icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/MessageFormat2Test.java

Lines changed: 40 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -334,41 +334,13 @@ public void testFormatterIsCreatedOnce() {
334334
@Test
335335
public void testPluralWithOffset() {
336336
String message = ""
337-
+ ".input {$count :number icu:offset=2}\n"
338-
+ ".match $count\n"
339-
+ " 1 {{Anna}}\n"
340-
+ " 2 {{Anna and Bob}}\n"
341-
+ " one {{Anna, Bob, and {$count :number icu:offset=2} other guest}}\n"
342-
+ " * {{Anna, Bob, and {$count :number icu:offset=2} other guests}}";
343-
MessageFormatter mf2 = MessageFormatter.builder()
344-
.setPattern(message)
345-
.build();
346-
assertEquals("plural with offset",
347-
"Anna",
348-
mf2.formatToString(Args.of("count", 1)));
349-
assertEquals("plural with offset",
350-
"Anna and Bob",
351-
mf2.formatToString(Args.of("count", 2)));
352-
assertEquals("plural with offset",
353-
"Anna, Bob, and 1 other guest",
354-
mf2.formatToString(Args.of("count", 3)));
355-
assertEquals("plural with offset",
356-
"Anna, Bob, and 2 other guests",
357-
mf2.formatToString(Args.of("count", 4)));
358-
assertEquals("plural with offset",
359-
"Anna, Bob, and 10 other guests",
360-
mf2.formatToString(Args.of("count", 12)));
361-
}
362-
363-
@Test
364-
public void testPluralWithOffsetAndLocalVar() {
365-
String message = ""
366-
+ ".local $foo = {$count :number icu:offset=2}"
367-
+ ".match $foo\n" // should "inherit" the offset
368-
+ " 1 {{Anna}}\n"
369-
+ " 2 {{Anna and Bob}}\n"
370-
+ " one {{Anna, Bob, and {$foo} other guest}}\n"
371-
+ " * {{Anna, Bob, and {$foo} other guests}}";
337+
+ ".input {$count :number}\n"
338+
+ ".local $offsetCount = {$count :offset subtract=2}\n"
339+
+ ".match $count $offsetCount\n"
340+
+ " 1 * {{Anna}}\n"
341+
+ " 2 * {{Anna and Bob}}\n"
342+
+ " * one {{Anna, Bob, and {$offsetCount} other guest}}\n"
343+
+ " * * {{Anna, Bob, and {$offsetCount} other guests}}";
372344
MessageFormatter mf2 = MessageFormatter.builder()
373345
.setPattern(message)
374346
.build();
@@ -450,12 +422,13 @@ public void testLoopOnLocalVars() {
450422
@Test
451423
public void testVariableOptionsInSelector() {
452424
String messageVar = ""
453-
+ ".input {$count :number icu:offset=$delta}\n"
454-
+ ".match $count\n"
455-
+ " 1 {{A}}\n"
456-
+ " 2 {{A and B}}\n"
457-
+ " one {{A, B, and {$count :number icu:offset=$delta} more character}}\n"
458-
+ " * {{A, B, and {$count :number icu:offset=$delta} more characters}}";
425+
+ ".input {$count :number}\n"
426+
+ ".local $offsetCount = {$count :offset subtract=$delta}\n"
427+
+ ".match $count $offsetCount\n"
428+
+ " 1 * {{A}}\n"
429+
+ " 2 * {{A and B}}\n"
430+
+ " * one {{A, B, and {$offsetCount} more character}}\n"
431+
+ " * * {{A, B, and {$offsetCount} more characters}}";
459432
MessageFormatter mfVar = MessageFormatter.builder()
460433
.setPattern(messageVar)
461434
.build();
@@ -469,11 +442,12 @@ public void testVariableOptionsInSelector() {
469442
mfVar.formatToString(Args.of("count", 7, "delta", 2)));
470443

471444
String messageVar2 = ""
472-
+ ".input {$count :number icu:offset=$delta}\n"
473-
+ ".match $count\n"
474-
+ " 1 {{Exactly 1}}\n"
475-
+ " 2 {{Exactly 2}}\n"
476-
+ " * {{Count = {$count :number icu:offset=$delta} and delta={$delta}.}}";
445+
+ ".input {$count :number}\n"
446+
+ ".local $offsetCount = {$count :offset subtract=$delta}\n"
447+
+ ".match $count $offsetCount\n"
448+
+ " 1 * {{Exactly 1}}\n"
449+
+ " 2 * {{Exactly 2}}\n"
450+
+ " * * {{Count = {$count :number icu:offset=$delta} and delta={$delta}.}}";
477451
MessageFormatter mfVar2 = MessageFormatter.builder()
478452
.setPattern(messageVar2)
479453
.build();
@@ -509,12 +483,13 @@ public void testVariableOptionsInSelector() {
509483
@Test
510484
public void testVariableOptionsInSelectorWithLocalVar() {
511485
String messageFix = ""
512-
+ ".local $offCount = {$count :number icu:offset=2}"
513-
+ ".match $offCount\n"
514-
+ " 1 {{A}}\n"
515-
+ " 2 {{A and B}}\n"
516-
+ " one {{A, B, and {$offCount} more character}}\n"
517-
+ " * {{A, B, and {$offCount} more characters}}";
486+
+ ".input {$count :integer}"
487+
+ ".local $offCount = {$count :offset subtract=2}"
488+
+ ".match $count $offCount\n"
489+
+ " 1 * {{A}}\n"
490+
+ " 2 * {{A and B}}\n"
491+
+ " * one {{A, B, and {$offCount} more character}}\n"
492+
+ " * * {{A, B, and {$offCount} more characters}}";
518493
MessageFormatter mfFix = MessageFormatter.builder()
519494
.setPattern(messageFix)
520495
.build();
@@ -524,12 +499,13 @@ public void testVariableOptionsInSelectorWithLocalVar() {
524499
assertEquals("test local vars loop", "A, B, and 5 more characters", mfFix.formatToString(Args.of("count", 7)));
525500

526501
String messageVar = ""
527-
+ ".local $offCount = {$count :number icu:offset=$delta}"
528-
+ ".match $offCount\n"
529-
+ " 1 {{A}}\n"
530-
+ " 2 {{A and B}}\n"
531-
+ " one {{A, B, and {$offCount} more character}}\n"
532-
+ " * {{A, B, and {$offCount} more characters}}";
502+
+ ".input {$count :number}"
503+
+ ".local $offCount = {$count :offset subtract=$delta}"
504+
+ ".match $count $offCount\n"
505+
+ " 1 * {{A}}\n"
506+
+ " 2 * {{A and B}}\n"
507+
+ " * one {{A, B, and {$offCount} more character}}\n"
508+
+ " * * {{A, B, and {$offCount} more characters}}";
533509
MessageFormatter mfVar = MessageFormatter.builder()
534510
.setPattern(messageVar)
535511
.build();
@@ -543,11 +519,12 @@ public void testVariableOptionsInSelectorWithLocalVar() {
543519
mfVar.formatToString(Args.of("count", 7, "delta", 2)));
544520

545521
String messageVar2 = ""
546-
+ ".local $offCount = {$count :number icu:offset=$delta}"
547-
+ ".match $offCount\n"
548-
+ " 1 {{Exactly 1}}\n"
549-
+ " 2 {{Exactly 2}}\n"
550-
+ " * {{Count = {$count}, OffCount = {$offCount}, and delta={$delta}.}}";
522+
+ ".input {$count :number}"
523+
+ ".local $offCount = {$count :offset subtract=$delta}"
524+
+ ".match $count $offCount\n"
525+
+ " 1 * {{Exactly 1}}\n"
526+
+ " 2 * {{Exactly 2}}\n"
527+
+ " * * {{Count = {$count}, OffCount = {$offCount}, and delta={$delta}.}}";
551528
MessageFormatter mfVar2 = MessageFormatter.builder()
552529
.setPattern(messageVar2)
553530
.build();

icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestFunctionFactory.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.List;
99
import java.util.Locale;
1010
import java.util.Map;
11+
import java.util.Objects;
1112

1213
import com.ibm.icu.message2.FormattedPlaceholder;
1314
import com.ibm.icu.message2.Function;
@@ -45,7 +46,7 @@ public TestFunctionImpl(String kind, Map<String, Object> fixedOptions) {
4546

4647
@Override
4748
public String formatToString(Object toFormat, Map<String, Object> variableOptions) {
48-
if (!"select".equals(kind) && parsedOptions.failsFormat) {
49+
if (!Objects.equals(kind, "select") && parsedOptions.failsFormat) {
4950
throw new InvalidParameterException("ALWAYS FAIL");
5051
}
5152
return format(toFormat, variableOptions).toString();
@@ -59,7 +60,7 @@ public FormattedPlaceholder format(Object toFormat, Map<String, Object> variable
5960
@Override
6061
public List<String> matches(Object value, List<String> keys, Map<String, Object> variableOptions) {
6162
// ParsedOptions parsedOptions = ParsedOptions.of(variableOptions);
62-
if (kind.equals("format")) {
63+
if (Objects.equals(kind, "format")) {
6364
// Can't do selection on the `format` only function
6465
return null;
6566
}
@@ -93,10 +94,10 @@ private static int testComparator(String o1, String o2) {
9394
return -1;
9495
}
9596
// * sorts last
96-
if ("*".equals(o1)) {
97+
if (Objects.equals(o1, "*")) {
9798
return 1;
9899
}
99-
if ("*".equals(o2)) {
100+
if (Objects.equals(o2, "*")) {
100101
return -1;
101102
}
102103
// At this point they are both strings
@@ -129,7 +130,7 @@ static ParsedOptions of(Map<String, Object> options) {
129130
}
130131

131132
String option = getStringOption(options, "icu:impl:errorPolicy", null);
132-
reportErrors= "STRICT".equals(option);
133+
reportErrors = Objects.equals(option, "STRICT");
133134

134135
option = getStringOption(options, "fails", "never");
135136
switch (option) {

icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/TestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ static void rewriteDecimals(Param[] params) {
118118
@SuppressWarnings("unchecked")
119119
Map<String, Object> innerMap = (Map<String, Object>) pair.value;
120120
if (innerMap.size() == 1 && innerMap.containsKey("decimal")
121-
&& innerMap.get("decimal") instanceof String) {
121+
&& innerMap.get("decimal") instanceof String) {
122122
String decimalValue = (String) innerMap.get("decimal");
123123
params[i] = new Param(pair.name, new com.ibm.icu.math.BigDecimal(decimalValue));
124124
}

icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/message2/spec/functions/date.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@
3333
"src": "{|2006-01-02T15:04:06| :date}"
3434
},
3535
{
36-
"src": "{|2006-01-02| :date style=long}"
36+
"src": "{|2006-01-02| :date length=long}"
3737
},
3838
{
39-
"src": ".local $d = {|2006-01-02| :date style=long} {{{$d}}}"
39+
"src": ".local $d = {|2006-01-02| :date length=long} {{{$d}}}"
4040
},
4141
{
42-
"src": ".local $d = {|2006-01-02| :datetime dateStyle=long timeStyle=long} {{{$d :date}}}"
42+
"src": ".local $d = {|2006-01-02| :datetime dateLength=long timePrecision=second} {{{$d :date}}}"
4343
}
4444
]
4545
}

0 commit comments

Comments
 (0)