| // © 2017 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| package com.ibm.icu.dev.test.number; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| import org.junit.Test; |
| |
| import com.ibm.icu.impl.FormattedStringBuilder; |
| import com.ibm.icu.impl.number.AffixUtils; |
| import com.ibm.icu.impl.number.AffixUtils.SymbolProvider; |
| import com.ibm.icu.text.UnicodeSet; |
| |
| public class AffixUtilsTest { |
| |
| private static final SymbolProvider DEFAULT_SYMBOL_PROVIDER = new SymbolProvider() { |
| @Override |
| public CharSequence getSymbol(int type) { |
| // Use interesting symbols where possible. The symbols are from ar_SA but are hard-coded |
| // here to make the test independent of locale data changes. |
| switch (type) { |
| case AffixUtils.TYPE_MINUS_SIGN: |
| return "−"; |
| case AffixUtils.TYPE_PLUS_SIGN: |
| return "\u061C+"; |
| case AffixUtils.TYPE_PERCENT: |
| return "٪\u061C"; |
| case AffixUtils.TYPE_PERMILLE: |
| return "؉"; |
| case AffixUtils.TYPE_CURRENCY_SINGLE: |
| return "$"; |
| case AffixUtils.TYPE_CURRENCY_DOUBLE: |
| return "XXX"; |
| case AffixUtils.TYPE_CURRENCY_TRIPLE: |
| return "long name"; |
| case AffixUtils.TYPE_CURRENCY_QUAD: |
| return "\uFFFD"; |
| case AffixUtils.TYPE_CURRENCY_QUINT: |
| return "@"; |
| case AffixUtils.TYPE_CURRENCY_OVERFLOW: |
| return "\uFFFD"; |
| default: |
| throw new AssertionError(); |
| } |
| } |
| }; |
| |
| @Test |
| public void testEscape() { |
| Object[][] cases = { |
| { "", "" }, |
| { "abc", "abc" }, |
| { "-", "'-'" }, |
| { "-!", "'-'!" }, |
| { "−", "−" }, |
| { "---", "'---'" }, |
| { "-%-", "'-%-'" }, |
| { "'", "''" }, |
| { "-'", "'-'''" }, |
| { "-'-", "'-''-'" }, |
| { "a-'-", "a'-''-'" } }; |
| |
| StringBuilder sb = new StringBuilder(); |
| for (Object[] cas : cases) { |
| String input = (String) cas[0]; |
| String expected = (String) cas[1]; |
| sb.setLength(0); |
| AffixUtils.escape(input, sb); |
| assertEquals(expected, sb.toString()); |
| } |
| } |
| |
| @Test |
| public void testUnescape() { |
| Object[][] cases = { |
| { "", false, 0, "" }, |
| { "abc", false, 3, "abc" }, |
| { "📺", false, 1, "📺" }, |
| { "-", false, 1, "−" }, |
| { "-!", false, 2, "−!" }, |
| { "+", false, 1, "\u061C+" }, |
| { "+!", false, 2, "\u061C+!" }, |
| { "‰", false, 1, "؉" }, |
| { "‰!", false, 2, "؉!" }, |
| { "-x", false, 2, "−x" }, |
| { "'-'x", false, 2, "-x" }, |
| { "'--''-'-x", false, 6, "--'-−x" }, |
| { "''", false, 1, "'" }, |
| { "''''", false, 2, "''" }, |
| { "''''''", false, 3, "'''" }, |
| { "''x''", false, 3, "'x'" }, |
| { "¤", true, 1, "$" }, |
| { "¤¤", true, 2, "XXX" }, |
| { "¤¤¤", true, 3, "long name" }, |
| { "¤¤¤¤", true, 4, "\uFFFD" }, |
| { "¤¤¤¤¤", true, 5, "@" }, |
| { "¤¤¤¤¤¤", true, 6, "\uFFFD" }, |
| { "¤¤¤a¤¤¤¤", true, 8, "long namea\uFFFD" }, |
| { "a¤¤¤¤b¤¤¤¤¤c", true, 12, "a\uFFFDb@c" }, |
| { "¤!", true, 2, "$!" }, |
| { "¤¤!", true, 3, "XXX!" }, |
| { "¤¤¤!", true, 4, "long name!" }, |
| { "-¤¤", true, 3, "−XXX" }, |
| { "¤¤-", true, 3, "XXX−" }, |
| { "'¤'", false, 1, "¤" }, |
| { "%", false, 1, "٪\u061C" }, |
| { "'%'", false, 1, "%" }, |
| { "¤'-'%", true, 3, "$-٪\u061C" }, |
| { "#0#@#*#;#", false, 9, "#0#@#*#;#" } }; |
| |
| for (Object[] cas : cases) { |
| String input = (String) cas[0]; |
| boolean curr = (Boolean) cas[1]; |
| int length = (Integer) cas[2]; |
| String output = (String) cas[3]; |
| |
| assertEquals("Currency on <" + input + ">", curr, AffixUtils.hasCurrencySymbols(input)); |
| assertEquals("Length on <" + input + ">", length, AffixUtils.estimateLength(input)); |
| |
| String actual = unescapeWithDefaults(input); |
| assertEquals("Output on <" + input + ">", output, actual); |
| |
| int ulength = AffixUtils.unescapedCount(input, true, DEFAULT_SYMBOL_PROVIDER); |
| assertEquals("Unescaped length on <" + input + ">", output.length(), ulength); |
| |
| int ucpcount = AffixUtils.unescapedCount(input, false, DEFAULT_SYMBOL_PROVIDER); |
| assertEquals("Unescaped length on <" + input + ">", |
| output.codePointCount(0, output.length()), |
| ucpcount); |
| } |
| } |
| |
| @Test |
| public void testContainsReplaceType() { |
| Object[][] cases = { |
| { "", false, "" }, |
| { "-", true, "+" }, |
| { "-a", true, "+a" }, |
| { "a-", true, "a+" }, |
| { "a-b", true, "a+b" }, |
| { "--", true, "++" }, |
| { "x", false, "x" } }; |
| |
| for (Object[] cas : cases) { |
| String input = (String) cas[0]; |
| boolean hasMinusSign = (Boolean) cas[1]; |
| String output = (String) cas[2]; |
| |
| assertEquals("Contains on input " + input, |
| hasMinusSign, |
| AffixUtils.containsType(input, AffixUtils.TYPE_MINUS_SIGN)); |
| assertEquals("Replace on input" + input, |
| output, |
| AffixUtils.replaceType(input, AffixUtils.TYPE_MINUS_SIGN, '+')); |
| } |
| } |
| |
| @Test |
| public void testInvalid() { |
| String[] invalidExamples = { "'", "x'", "'x", "'x''", "''x'" }; |
| |
| for (String str : invalidExamples) { |
| try { |
| AffixUtils.hasCurrencySymbols(str); |
| fail("No exception was thrown on an invalid string"); |
| } catch (IllegalArgumentException e) { |
| // OK |
| } |
| try { |
| AffixUtils.estimateLength(str); |
| fail("No exception was thrown on an invalid string"); |
| } catch (IllegalArgumentException e) { |
| // OK |
| } |
| try { |
| unescapeWithDefaults(str); |
| fail("No exception was thrown on an invalid string"); |
| } catch (IllegalArgumentException e) { |
| // OK |
| } |
| } |
| } |
| |
| @Test |
| public void testUnescapeWithSymbolProvider() { |
| String[][] cases = { |
| { "", "" }, |
| { "-", "1" }, |
| { "'-'", "-" }, |
| { "- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", "1 2 3 4 5 6 7 8 9" }, |
| { "'¤¤¤¤¤¤'", "¤¤¤¤¤¤" }, |
| { "¤¤¤¤¤¤", "\uFFFD" } }; |
| |
| SymbolProvider provider = new SymbolProvider() { |
| @Override |
| public CharSequence getSymbol(int type) { |
| return Integer.toString(Math.abs(type)); |
| } |
| }; |
| |
| FormattedStringBuilder sb = new FormattedStringBuilder(); |
| for (String[] cas : cases) { |
| String input = cas[0]; |
| String expected = cas[1]; |
| sb.clear(); |
| AffixUtils.unescape(input, sb, 0, provider, null); |
| assertEquals("With symbol provider on <" + input + ">", expected, sb.toString()); |
| } |
| |
| // Test insertion position |
| sb.clear(); |
| sb.append("abcdefg", null); |
| AffixUtils.unescape("-+%", sb, 4, provider, null); |
| assertEquals("Symbol provider into middle", "abcd123efg", sb.toString()); |
| } |
| |
| @Test |
| public void testWithoutSymbolsOrIgnorables() { |
| Object[][] cases = { |
| { "", true }, |
| { "-", true }, |
| { " ", true }, |
| { "'-'", false }, |
| { " a + b ", false }, |
| { "-a+b%c‰d¤e¤¤f¤¤¤g¤¤¤¤h¤¤¤¤¤i", false }, }; |
| |
| UnicodeSet ignorables = new UnicodeSet("[:whitespace:]"); |
| for (Object[] cas : cases) { |
| String input = (String) cas[0]; |
| boolean expected = (Boolean) cas[1]; |
| assertEquals("Contains only symbols and ignorables: " + input, |
| expected, |
| AffixUtils.containsOnlySymbolsAndIgnorables(input, ignorables)); |
| } |
| } |
| |
| private static String unescapeWithDefaults(String input) { |
| FormattedStringBuilder nsb = new FormattedStringBuilder(); |
| int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER, null); |
| assertEquals("Return value of unescape", nsb.length(), length); |
| return nsb.toString(); |
| } |
| } |