diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/ExecutorUtils.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/ExecutorUtils.java index 30c768ae..3795d27f 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/ExecutorUtils.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/ExecutorUtils.java @@ -15,23 +15,29 @@ public static void printResponseString(String responseString) { System.out.println(responseString); } - public static io.lacuna.bifurcan.Map parseInputLine(String inputLine) { - TypeToken> mapType = new TypeToken>(){}; - Map parsedInputJavaMap = ExecutorUtils.GSON.fromJson(inputLine, mapType); + public static io.lacuna.bifurcan.Map parseInputLine(String inputLine) { + Map parsedInputJavaMap = stringMapFromString(inputLine); - io.lacuna.bifurcan.Map parsedInputPersistentMap = + io.lacuna.bifurcan.Map parsedInputPersistentMap = io.lacuna.bifurcan.Map.from(parsedInputJavaMap); return parsedInputPersistentMap; } - public static String formatAsJson(io.lacuna.bifurcan.IMap mapData) { - java.util.Map jMap = new HashMap<>(); - for (Iterator> it = mapData.stream().iterator(); it.hasNext(); ) { - IEntry entry = it.next(); + public static String formatAsJson(io.lacuna.bifurcan.IMap mapData) { + java.util.Map jMap = new HashMap<>(); + for (Iterator> it = mapData.stream().iterator(); it.hasNext(); ) { + IEntry entry = it.next(); jMap.put(entry.key(), entry.value()); } return GSON.toJson(jMap); } + public static Map stringMapFromString(String s) { + TypeToken> mapType = new TypeToken>(){}; + Map parsedInputJavaMap = ExecutorUtils.GSON.fromJson(s, mapType); + + return parsedInputJavaMap; + } + } diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java index 4c599eb3..30da414c 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/Icu4jExecutor.java @@ -2,6 +2,7 @@ import com.google.gson.reflect.TypeToken; import com.ibm.icu.impl.locale.XCldrStub.ImmutableMap; +import com.ibm.icu.number.NumberFormatter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -9,9 +10,11 @@ import java.util.Map; import java.util.Optional; import org.unicode.conformance.testtype.ITestType; +import org.unicode.conformance.testtype.ITestTypeOutputJson; import org.unicode.conformance.testtype.collator.CollatorTester; import org.unicode.conformance.testtype.langnames.LangNamesTester; import org.unicode.conformance.testtype.likelysubtags.LikelySubtagsTester; +import org.unicode.conformance.testtype.numberformatter.NumberFormatterTester; /** * Hello world! @@ -97,20 +100,20 @@ public static String getVersionResponse() { public static String getTestCaseResponse(String inputLine) throws Exception { - io.lacuna.bifurcan.Map parsedInputPersistentMap = + io.lacuna.bifurcan.Map parsedInputPersistentMap = ExecutorUtils.parseInputLine(inputLine); - Optional testTypeOpt = parsedInputPersistentMap.get("test_type"); + Optional testTypeOpt = parsedInputPersistentMap.get("test_type"); if (!testTypeOpt.isPresent()) { - io.lacuna.bifurcan.IMap response = + io.lacuna.bifurcan.IMap response = parsedInputPersistentMap .put("error", "Error in input") .put("error_msg", "Error in input found in executor before execution"); return ExecutorUtils.formatAsJson(response); } else { - String testTypeStr = testTypeOpt.get(); + String testTypeStr = (String) testTypeOpt.get(); ITestType testType; if (testTypeStr.equals("collation_short")) { testType = CollatorTester.INSTANCE; @@ -118,8 +121,10 @@ public static String getTestCaseResponse(String inputLine) throws Exception { testType = LangNamesTester.INSTANCE; } else if (testTypeStr.equals("likely_subtags")) { testType = LikelySubtagsTester.INSTANCE; + } else if (testTypeStr.equals("number_fmt")) { + testType = NumberFormatterTester.INSTANCE; } else { - io.lacuna.bifurcan.IMap response = + io.lacuna.bifurcan.IMap response = parsedInputPersistentMap .put("error", "Error in input") .put("error_msg", "Error in input found in executor before execution"); @@ -127,7 +132,17 @@ public static String getTestCaseResponse(String inputLine) throws Exception { return ExecutorUtils.formatAsJson(response); } - return testType.getFinalOutputFromInput(parsedInputPersistentMap); + try { + return testType.getFinalOutputFromInput(parsedInputPersistentMap); + } catch (Exception e) { + ITestTypeOutputJson defaultOutput = testType.getDefaultOutputJson(); + return ExecutorUtils.formatAsJson( + testType.convertOutputToMap(defaultOutput) + .put("label", parsedInputPersistentMap.get("label", null)) + .put("error", "Error in input") + .put("error_msg", "Error in handling test case: " + e.getMessage()) + ); + } } } diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/ITestType.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/ITestType.java index 79028a32..97081ec4 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/ITestType.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/ITestType.java @@ -4,14 +4,14 @@ public interface ITestType { - default io.lacuna.bifurcan.Map parseInput(String inputLine) { + default io.lacuna.bifurcan.Map parseInput(String inputLine) { return ExecutorUtils.parseInputLine(inputLine); } - ITestTypeInputJson inputMapToJson(io.lacuna.bifurcan.Map inputMapData); + ITestTypeInputJson inputMapToJson(io.lacuna.bifurcan.Map inputMapData); default ITestTypeInputJson parseInputJson(String inputLine) { - io.lacuna.bifurcan.Map inputMapData = + io.lacuna.bifurcan.Map inputMapData = parseInput(inputLine); ITestTypeInputJson inputJson = inputMapToJson(inputMapData); @@ -20,20 +20,24 @@ default ITestTypeInputJson parseInputJson(String inputLine) { ITestTypeOutputJson execute(ITestTypeInputJson inputJson); + ITestTypeOutputJson getDefaultOutputJson(); + + io.lacuna.bifurcan.IMap convertOutputToMap(ITestTypeOutputJson outputJson); + String formatOutputJson(ITestTypeOutputJson outputJson); default ITestTypeOutputJson getStructuredOutputFromInputStr(String inputLine) { - io.lacuna.bifurcan.Map inputMapData = parseInput(inputLine); + io.lacuna.bifurcan.Map inputMapData = parseInput(inputLine); return getStructuredOutputFromInput(inputMapData); } - default ITestTypeOutputJson getStructuredOutputFromInput(io.lacuna.bifurcan.Map inputMapData) { + default ITestTypeOutputJson getStructuredOutputFromInput(io.lacuna.bifurcan.Map inputMapData) { ITestTypeInputJson inputJson = inputMapToJson(inputMapData); ITestTypeOutputJson outputJson = execute(inputJson); return outputJson; } - default String getFinalOutputFromInput(io.lacuna.bifurcan.Map inputMapData) throws Exception { + default String getFinalOutputFromInput(io.lacuna.bifurcan.Map inputMapData) throws Exception { ITestTypeOutputJson outputJson = getStructuredOutputFromInput(inputMapData); String formattedOutput = formatOutputJson(outputJson); return formattedOutput; diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/collator/CollatorTester.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/collator/CollatorTester.java index 49f7815c..b1b419fd 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/collator/CollatorTester.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/collator/CollatorTester.java @@ -3,6 +3,7 @@ import com.ibm.icu.text.Collator; import com.ibm.icu.text.RuleBasedCollator; import com.ibm.icu.util.ULocale; +import io.lacuna.bifurcan.IMap; import io.lacuna.bifurcan.Map; import java.util.Optional; import org.unicode.conformance.ExecutorUtils; @@ -19,30 +20,30 @@ public class CollatorTester implements ITestType { // @Override - public ITestTypeInputJson inputMapToJson(Map inputMapData) { + public ITestTypeInputJson inputMapToJson(Map inputMapData) { CollatorInputJson result = new CollatorInputJson(); - result.test_type = inputMapData.get("test_type", null); - result.label = inputMapData.get("label", null); + result.test_type = (String) inputMapData.get("test_type", null); + result.label = (String) inputMapData.get("label", null); // TODO: clean up after schema validation gets turned on at runtime - result.s1 = inputMapData.get("s1", null); + result.s1 = (String) inputMapData.get("s1", null); if (result.s1 == null) { - result.s1 = inputMapData.get("string1", null); + result.s1 = (String) inputMapData.get("string1", null); } // TODO: clean up after schema validation gets turned on at runtime - result.s2 = inputMapData.get("s2", null); + result.s2 = (String) inputMapData.get("s2", null); if (result.s2 == null) { - result.s2 = inputMapData.get("string2", null); + result.s2 = (String) inputMapData.get("string2", null); } - result.locale = inputMapData.get("locale", null); + result.locale = (String) inputMapData.get("locale", null); boolean ignorePunctuation = false; - Optional ignorePunctuationStr = inputMapData.get("ignorePunctuation"); + Optional ignorePunctuationStr = inputMapData.get("ignorePunctuation"); try { if (ignorePunctuationStr.isPresent()) { - ignorePunctuation = Boolean.parseBoolean(ignorePunctuationStr.get()); + ignorePunctuation = Boolean.parseBoolean((String) ignorePunctuationStr.get()); } } catch (Exception e) { // do nothing, default is false @@ -50,32 +51,32 @@ public ITestTypeInputJson inputMapToJson(Map inputMapData) { result.ignorePunctuation = ignorePunctuation; int line = 0; - Optional lineStr = inputMapData.get("line"); + Optional lineStr = inputMapData.get("line"); try { if (lineStr.isPresent()) { - line = Integer.parseInt(lineStr.get()); + line = Integer.parseInt((String) lineStr.get()); } } catch (Exception e) { // do nothing, default is 0 } result.line = line; - result.compare_type = inputMapData.get("compare_type", null); - result.test_description = inputMapData.get("test_description", null); + result.compare_type = (String) inputMapData.get("compare_type", null); + result.test_description = (String) inputMapData.get("test_description", null); // TODO: implement this correctly recursively (either using APIs or else DIY) String[] attrs; - Optional attrsString = inputMapData.get("attributes"); + Optional attrsString = inputMapData.get("attributes"); if (attrsString.isPresent()) { - attrs = new String[]{ attrsString.get() }; + attrs = new String[]{ (String) attrsString.get() }; } else { attrs = new String[]{}; } result.attributes = attrs; - result.rules = inputMapData.get("rules", null); - result.compare_comment = inputMapData.get("compare_comment", null); - result.warning = inputMapData.get("warning", null); + result.rules = (String) inputMapData.get("rules", null); + result.compare_comment = (String) inputMapData.get("compare_comment", null); + result.warning = (String) inputMapData.get("warning", null); return result; } @@ -85,7 +86,7 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { CollatorInputJson input = (CollatorInputJson) inputJson; // partially construct output - CollatorOutputJson output = new CollatorOutputJson(); + CollatorOutputJson output = (CollatorOutputJson) getDefaultOutputJson(); output.label = input.label; output.s1 = input.s1; output.s2 = input.s2; @@ -124,6 +125,25 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { // If we get here, it's a pass/fail result (supported options and no runtime errors/exceptions) return output; } + + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + CollatorOutputJson output = new CollatorOutputJson(); + output.result = false; + + return output; + } + + @Override + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + CollatorOutputJson output = (CollatorOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("result", output.result) + .put("s1", output.s1) + .put("s2", output.s2); + } + @Override public String formatOutputJson(ITestTypeOutputJson outputJson) { return ExecutorUtils.GSON.toJson((CollatorOutputJson) outputJson); diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/langnames/LangNamesTester.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/langnames/LangNamesTester.java index c5825b9c..eefce4ea 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/langnames/LangNamesTester.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/langnames/LangNamesTester.java @@ -1,6 +1,7 @@ package org.unicode.conformance.testtype.langnames; import com.ibm.icu.util.ULocale; +import io.lacuna.bifurcan.IMap; import io.lacuna.bifurcan.Map; import org.unicode.conformance.ExecutorUtils; import org.unicode.conformance.testtype.ITestType; @@ -14,14 +15,14 @@ public class LangNamesTester implements ITestType { public static LangNamesTester INSTANCE = new LangNamesTester(); @Override - public ITestTypeInputJson inputMapToJson(Map inputMapData) { + public ITestTypeInputJson inputMapToJson(Map inputMapData) { LangNamesInputJson result = new LangNamesInputJson(); - result.test_type = inputMapData.get("test_type", null); - result.label = inputMapData.get("label", null); + result.test_type = (String) inputMapData.get("test_type", null); + result.label = (String) inputMapData.get("label", null); - result.language_label = inputMapData.get("language_label", null); - result.locale_label = inputMapData.get("locale_label", null); + result.language_label = (String) inputMapData.get("language_label", null); + result.locale_label = (String) inputMapData.get("locale_label", null); return result; } @@ -31,7 +32,7 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { LangNamesInputJson input = (LangNamesInputJson) inputJson; // partially construct output - LangNamesOutputJson output = new LangNamesOutputJson(); + LangNamesOutputJson output = (LangNamesOutputJson) getDefaultOutputJson(); output.label = input.label; try { @@ -47,6 +48,21 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { return output; } + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + return new LangNamesOutputJson(); + } + + @Override + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + LangNamesOutputJson output = (LangNamesOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("result", output.result) + .put("language_label", output.language_label) + .put("local_label", output.locale_label); + } + @Override public String formatOutputJson(ITestTypeOutputJson outputJson) { return ExecutorUtils.GSON.toJson((LangNamesOutputJson) outputJson); diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/likelysubtags/LikelySubtagsTester.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/likelysubtags/LikelySubtagsTester.java index 9b487bbe..49b3dfbe 100644 --- a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/likelysubtags/LikelySubtagsTester.java +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/likelysubtags/LikelySubtagsTester.java @@ -1,6 +1,7 @@ package org.unicode.conformance.testtype.likelysubtags; import com.ibm.icu.util.ULocale; +import io.lacuna.bifurcan.IMap; import io.lacuna.bifurcan.Map; import org.unicode.conformance.ExecutorUtils; import org.unicode.conformance.testtype.ITestType; @@ -12,14 +13,14 @@ public class LikelySubtagsTester implements ITestType { public static LikelySubtagsTester INSTANCE = new LikelySubtagsTester(); @Override - public ITestTypeInputJson inputMapToJson(Map inputMapData) { + public ITestTypeInputJson inputMapToJson(Map inputMapData) { LikelySubtagsInputJson result = new LikelySubtagsInputJson(); - result.test_type = inputMapData.get("test_type", null); - result.label = inputMapData.get("label", null); - result.locale = inputMapData.get("locale", null); + result.test_type = (String) inputMapData.get("test_type", null); + result.label = (String) inputMapData.get("label", null); + result.locale = (String) inputMapData.get("locale", null); result.option = LikelySubtagsTestOption.valueOf( - inputMapData.get("option", null) + (String) inputMapData.get("option", null) ); return result; @@ -45,6 +46,21 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { return output; } + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + return new LikelySubtagsOutputJson(); + } + + @Override + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + LikelySubtagsOutputJson output = (LikelySubtagsOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("locale", output.locale) + .put("result", output.result) + .put("option", output.option); + } + @Override public String formatOutputJson(ITestTypeOutputJson outputJson) { return ExecutorUtils.GSON.toJson((LikelySubtagsOutputJson) outputJson); diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CompactDisplayVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CompactDisplayVal.java new file mode 100644 index 00000000..c224507f --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CompactDisplayVal.java @@ -0,0 +1,32 @@ +package org.unicode.conformance.testtype.numberformatter; + +/** + * This enum uses names different from the string literals coming via the JSON + * because `short`, `long` are reserved keywords in Java. + * Instead, the uppercase versions `SHORT`, `LONG` are used. + */ +public enum CompactDisplayVal { + SHORT, + LONG; + + public static CompactDisplayVal DEFAULT = SHORT; + + /** + * Convenience static constructor for this class's enums that automatically + * uppercases the input string, because the values must be uppercased because + * some of them conflict with Java keywords in lowercase. + * @param s + * @return + */ + public static CompactDisplayVal getFromString(String s) { + try { + return CompactDisplayVal.valueOf(s.toUpperCase()); + } catch (Exception e) { + return DEFAULT; + } + } + + public String toString() { + return this.name().toLowerCase(); + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CurrencyDisplayVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CurrencyDisplayVal.java new file mode 100644 index 00000000..18fd9b82 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/CurrencyDisplayVal.java @@ -0,0 +1,19 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum CurrencyDisplayVal { + NONE, // a fake value to use as default + symbol, + narrowSymbol, + code, + name; + + public static CurrencyDisplayVal DEFAULT = NONE; + + public static CurrencyDisplayVal getFromString(String s) { + try { + return CurrencyDisplayVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NuVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NuVal.java new file mode 100644 index 00000000..dd780a37 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NuVal.java @@ -0,0 +1,81 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum NuVal { + NONE, // a fake value to use as default + adlm, + ahom, + arab, + arabext, + bali, + beng, + bhks, + brah, + cakm, + cham, + deva, + diak, + fullwide, + gong, + gonm, + gujr, + guru, + hanidec, + hmng, + hmnp, + java, + kali, + khmr, + knda, + lana, + lanatham, + laoo, + latn, + lepc, + limb, + mathbold, + mathdbl, + mathmono, + mathsanb, + mathsans, + mlym, + modi, + mong, + mroo, + mtei, + mymr, + mymrshan, + mymrtlng, + newa, + nkoo, + olck, + orya, + osma, + rohg, + saur, + segment, + shrd, + sind, + sinh, + sora, + sund, + takr, + talu, + tamldec, + telu, + thai, + tibt, + tirh, + vaii, + wara, + wcho; + + public static NuVal DEFAULT = NONE; + + public static NuVal getFromString(String s) { + try { + return NuVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterInputJson.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterInputJson.java new file mode 100644 index 00000000..328f3f9c --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterInputJson.java @@ -0,0 +1,21 @@ +package org.unicode.conformance.testtype.numberformatter; + +import java.util.Map; +import org.unicode.conformance.testtype.ITestTypeInputJson; + +public class NumberFormatterInputJson implements ITestTypeInputJson { + + String label; + + String locale; + + String pattern; + + String skeleton; + + String input; + + String op; + + Map options; +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterOutputJson.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterOutputJson.java new file mode 100644 index 00000000..04a32d15 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterOutputJson.java @@ -0,0 +1,14 @@ +package org.unicode.conformance.testtype.numberformatter; + +import java.util.Map; +import org.unicode.conformance.testtype.ITestTypeOutputJson; + +public class NumberFormatterOutputJson implements ITestTypeOutputJson { + public String label; + + public String result; + + public String error; + + public String error_message; +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTestOptionKey.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTestOptionKey.java new file mode 100644 index 00000000..ea7149a1 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTestOptionKey.java @@ -0,0 +1,25 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum NumberFormatterTestOptionKey { + notation, + numberingSystem, + compactDisplay, + currencySign, + signDisplay, + style, + unit, + unitDisplay, + currency, + currencyDisplay, + minimumFractionDigits, + maximumFractionDigits, + minimumIntegerDigits, + minimumSignificantDigits, + maximumSignificantDigits, + nu, + roundingPriority, + roundingMode, + roundingIncrement, + trailingZeroDisplay, + useGrouping +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTester.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTester.java new file mode 100644 index 00000000..cb6faba3 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/NumberFormatterTester.java @@ -0,0 +1,174 @@ +package org.unicode.conformance.testtype.numberformatter; + +import com.ibm.icu.number.FormattedNumber; +import com.ibm.icu.number.LocalizedNumberFormatter; +import com.ibm.icu.number.NumberFormatter; +import com.ibm.icu.util.Currency; +import com.ibm.icu.util.ULocale; +import io.lacuna.bifurcan.IMap; +import io.lacuna.bifurcan.Map; +import java.math.BigDecimal; +import java.util.HashMap; +import org.unicode.conformance.ExecutorUtils; +import org.unicode.conformance.testtype.ITestType; +import org.unicode.conformance.testtype.ITestTypeInputJson; +import org.unicode.conformance.testtype.ITestTypeOutputJson; + +public class NumberFormatterTester implements ITestType { + + public static NumberFormatterTester INSTANCE = new NumberFormatterTester(); + + @Override + public ITestTypeInputJson inputMapToJson(Map inputMapData) { + NumberFormatterInputJson result = new NumberFormatterInputJson(); + + result.label = (String) inputMapData.get("label", null); + result.input = (String) inputMapData.get("input", null); + result.locale = (String) inputMapData.get("locale", null); + result.pattern = (String) inputMapData.get("pattern", null); + result.skeleton = (String) inputMapData.get("skeleton", null); + result.op = (String) inputMapData.get("op", null); + + java.util.Map parsedOptionsMap = + (java.util.Map) inputMapData.get("options", null); + java.util.Map options = + new HashMap<>(); + options.put( + NumberFormatterTestOptionKey.notation, + parsedOptionsMap.get("notation")); + options.put( + NumberFormatterTestOptionKey.numberingSystem, + parsedOptionsMap.get("numberingSystem")); + options.put( + NumberFormatterTestOptionKey.compactDisplay, + CompactDisplayVal.getFromString( + (String) parsedOptionsMap.get("compactDisplay"))); + options.put( + NumberFormatterTestOptionKey.currencySign, + parsedOptionsMap.get("currencySign")); + options.put( + NumberFormatterTestOptionKey.signDisplay, + SignDisplayVal.getFromString( + (String) parsedOptionsMap.get("signDisplay"))); + options.put( + NumberFormatterTestOptionKey.style, + StyleVal.getFromString( + (String) parsedOptionsMap.get("style"))); + options.put( + NumberFormatterTestOptionKey.unit, + parsedOptionsMap.get("unit")); + options.put( + NumberFormatterTestOptionKey.unitDisplay, + UnitDisplayVal.getFromString( + (String) parsedOptionsMap.get("unitDisplay"))); + options.put( + NumberFormatterTestOptionKey.currency, + parsedOptionsMap.get("currency")); + options.put( + NumberFormatterTestOptionKey.currencyDisplay, + CurrencyDisplayVal.getFromString( + (String) parsedOptionsMap.get("currencyDisplay"))); + options.put( + NumberFormatterTestOptionKey.minimumFractionDigits, + parsedOptionsMap.getOrDefault("minimumFractionDigits", -1)); + options.put( + NumberFormatterTestOptionKey.maximumFractionDigits, + parsedOptionsMap.getOrDefault("maximumFractionDigits", -1)); + options.put( + NumberFormatterTestOptionKey.minimumIntegerDigits, + parsedOptionsMap.getOrDefault("minimumIntegerDigits", -1)); + options.put( + NumberFormatterTestOptionKey.minimumSignificantDigits, + parsedOptionsMap.getOrDefault("minimumSignificantDigits", -1)); + options.put( + NumberFormatterTestOptionKey.maximumSignificantDigits, + parsedOptionsMap.getOrDefault("maximumSignificantDigits", -1)); + options.put( + NumberFormatterTestOptionKey.nu, + NuVal.getFromString( + (String) parsedOptionsMap.get("nu"))); + options.put( + NumberFormatterTestOptionKey.roundingPriority, + RoundingPriorityVal.getFromString( + (String) parsedOptionsMap.get("roundingPriority"))); + options.put( + NumberFormatterTestOptionKey.roundingMode, + RoundingModeVal.getFromString( + (String) parsedOptionsMap.get("roundingMode"))); + int roundingIncrement = + (int) parsedOptionsMap.getOrDefault("roundingIncrement", RoundingIncrementUtil.DEFAULT); + assert RoundingIncrementUtil.isValidVal(roundingIncrement); + options.put( + NumberFormatterTestOptionKey.roundingIncrement, + roundingIncrement + ); + options.put( + NumberFormatterTestOptionKey.trailingZeroDisplay, + TrailingZeroDispalyVal.getFromString( + (String) parsedOptionsMap.get("trailingZeroDisplay"))); + options.put( + NumberFormatterTestOptionKey.useGrouping, + UseGroupingVal.getFromString( + (String) parsedOptionsMap.get("useGrouping"))); + result.options = options; + + return result; + } + + @Override + public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) { + NumberFormatterInputJson input = (NumberFormatterInputJson) inputJson; + + // partially construct output + NumberFormatterOutputJson output = new NumberFormatterOutputJson(); + output.label = input.label; + + try { + output.result = getFormattedNumber(input); + } catch (Exception e) { + output.error = "error running test"; + output.error = e.getMessage(); + return output; + } + + // If we get here, it's a pass/fail result (supported options and no runtime errors/exceptions) + return output; + } + + @Override + public ITestTypeOutputJson getDefaultOutputJson() { + return new NumberFormatterOutputJson(); + } + + @Override + public IMap convertOutputToMap(ITestTypeOutputJson outputJson) { + NumberFormatterOutputJson output = (NumberFormatterOutputJson) outputJson; + return new io.lacuna.bifurcan.Map() + .put("label", output.label) + .put("result", output.result); + } + + @Override + public String formatOutputJson(ITestTypeOutputJson outputJson) { + return ExecutorUtils.GSON.toJson((NumberFormatterOutputJson) outputJson); + } + + public String getFormattedNumber(NumberFormatterInputJson input) { + BigDecimal inputVal = new BigDecimal(input.input); + + LocalizedNumberFormatter nf; + if (input.skeleton.isEmpty()) { + nf = NumberFormatter.withLocale(ULocale.forLanguageTag(input.input)); + } else { + nf = NumberFormatter.forSkeleton(input.skeleton) + .locale(ULocale.forLanguageTag(input.input)); + } + + if (input.options.get("style") == StyleVal.currency && input.options.get("currency") != null) { + nf = nf.unit(Currency.getInstance((String) input.options.get("currency"))); + } + + FormattedNumber fn = nf.format(inputVal); + return fn.toString(); + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingIncrementUtil.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingIncrementUtil.java new file mode 100644 index 00000000..500a22ad --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingIncrementUtil.java @@ -0,0 +1,30 @@ +package org.unicode.conformance.testtype.numberformatter; + +import io.lacuna.bifurcan.Set; + +public class RoundingIncrementUtil { + + public static int DEFAULT = 1; + private static final Set validVals = Set.of( + 1, + 2, + 5, + 10, + 20, + 25, + 50, + 100, + 200, + 250, + 500, + 1000, + 2000, + 2500, + 5000 + ); + + public static boolean isValidVal(int n) { + return validVals.contains(n); + } + +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingModeVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingModeVal.java new file mode 100644 index 00000000..7227bede --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingModeVal.java @@ -0,0 +1,25 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum RoundingModeVal { + NONE, // a fake value to use as default + ceil, + floor, + expand, + trunc, + halfCeil, + halfFloor, + halfExpand, + halfTrunc, + halfEven, + unnecessary; + + public static RoundingModeVal DEFAULT = NONE; + + public static RoundingModeVal getFromString(String s) { + try { + return RoundingModeVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingPriorityVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingPriorityVal.java new file mode 100644 index 00000000..b3a17c5c --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/RoundingPriorityVal.java @@ -0,0 +1,18 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum RoundingPriorityVal { + NONE, + auto, + morePrecision, + lessPrecision; + + public static RoundingPriorityVal DEFAULT = NONE; + + public static RoundingPriorityVal getFromString(String s) { + try { + return RoundingPriorityVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/SignDisplayVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/SignDisplayVal.java new file mode 100644 index 00000000..a9ca9e9b --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/SignDisplayVal.java @@ -0,0 +1,19 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum SignDisplayVal { + auto, + always, + exceptZero, + negative, + never; + + public static SignDisplayVal DEFAULT = auto; + + public static SignDisplayVal getFromString(String s) { + try { + return SignDisplayVal.valueOf(s); + } catch (Exception e){ + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/StyleVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/StyleVal.java new file mode 100644 index 00000000..947a8278 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/StyleVal.java @@ -0,0 +1,19 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum StyleVal { + NONE, // a fake value to use as default + decimal, + currency, + percent, + unit; + + public static StyleVal DEFAULT = NONE; + + public static StyleVal getFromString(String s) { + try { + return StyleVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/TrailingZeroDispalyVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/TrailingZeroDispalyVal.java new file mode 100644 index 00000000..6260f724 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/TrailingZeroDispalyVal.java @@ -0,0 +1,17 @@ +package org.unicode.conformance.testtype.numberformatter; + +public enum TrailingZeroDispalyVal { + NONE, // a fake value to use as default + auto, + stringIfInteger; + + public static TrailingZeroDispalyVal DEFAULT = NONE; + + public static TrailingZeroDispalyVal getFromString(String s) { + try { + return TrailingZeroDispalyVal.valueOf(s); + } catch (Exception e) { + return DEFAULT; + } + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UnitDisplayVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UnitDisplayVal.java new file mode 100644 index 00000000..1c63ebe1 --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UnitDisplayVal.java @@ -0,0 +1,34 @@ +package org.unicode.conformance.testtype.numberformatter; + +/** + * This enum uses names different from the string literals coming via the JSON + * because `short`, `long` are reserved keywords in Java. + * Instead, the uppercase versions `SHORT`, `LONG`, etc. are used. + */ +public enum UnitDisplayVal { + NONE, // a fake value to use as default + LONG, + SHORT, + NARROW; + + public static UnitDisplayVal DEFAULT = NONE; + + /** + * Convenience static constructor for this class's enums that automatically + * uppercases the input string, because the values must be uppercased because + * some of them conflict with Java keywords in lowercase. + * @param s + * @return + */ + public static UnitDisplayVal getFromString(String s) { + try { + return UnitDisplayVal.valueOf(s.toUpperCase()); + } catch (Exception e) { + return DEFAULT; + } + } + + public String toString() { + return this.name().toLowerCase(); + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UseGroupingVal.java b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UseGroupingVal.java new file mode 100644 index 00000000..e1c44b4f --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/main/java/org/unicode/conformance/testtype/numberformatter/UseGroupingVal.java @@ -0,0 +1,35 @@ +package org.unicode.conformance.testtype.numberformatter; + +/** + * This enum uses names different from the string literals coming via the JSON + * because `short`, `long` are reserved keywords in Java. + * Instead, the uppercase versions `SHORT`, `LONG` are used. + */ +public enum UseGroupingVal { + FALSE, + TRUE, + ALWAYS, + AUTO, + MIN2; + + public static UseGroupingVal DEFAULT = AUTO; + + /** + * Convenience static constructor for this class's enums that automatically + * uppercases the input string, because the values must be uppercased because + * some of them conflict with Java keywords in lowercase. + * @param s + * @return + */ + public static UseGroupingVal getFromString(String s) { + try { + return UseGroupingVal.valueOf(s.toUpperCase()); + } catch (Exception e) { + return DEFAULT; + } + } + + public String toString() { + return this.name().toLowerCase(); + } +} diff --git a/executors/icu4j/73/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java b/executors/icu4j/73/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java new file mode 100644 index 00000000..20f3d00a --- /dev/null +++ b/executors/icu4j/73/executor-icu4j/src/test/java/org/unicode/conformance/numberformatter/NumberFormatterTest.java @@ -0,0 +1,33 @@ +package org.unicode.conformance.numberformatter; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.unicode.conformance.testtype.numberformatter.NumberFormatterOutputJson; +import org.unicode.conformance.testtype.numberformatter.NumberFormatterTester; + +public class NumberFormatterTest { + + @Test + public void testSkeleton() { + String testInput = + "{\"test_type\": \"number_fmt\", \"label\":\"s\", \"pattern\":\"@@@\", \"skeleton\": \"@@@ group-off\", \"input\":\"123456\", \"options\":{}}"; + + NumberFormatterOutputJson output = + (NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput); + + assertEquals("123000", output.result); + } + + @Test + public void testDebug1() { + String testInput = + "{ \"label\": \"0146\", \"locale\": \"es-MX\", \"skeleton\": \"compact-short currency/EUR precision-integer\", \"input\": \"-0.22222\", \"options\": { \"notation\": \"compact\", \"compactDisplay\": \"short\", \"style\": \"currency\", \"currencyDisplay\": \"symbol\", \"currency\": \"EUR\", \"maximumFractionDigits\": 0, \"minimumFractionDigits\": 0, \"roundingType\": \"fractionDigits\" }, \"test_type\": \"number_fmt\" }"; + + NumberFormatterOutputJson output = + (NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput); + + assertEquals("-€ 0", output.result); + } + +} diff --git a/run_config.json b/run_config.json index e6f31792..858db22c 100644 --- a/run_config.json +++ b/run_config.json @@ -252,7 +252,8 @@ "exec": "icu4j", "test_type": [ "lang_names", - "likely_subtags" + "likely_subtags", + "number_fmt" ], "per_execution": 10000 }