blob: 7ba5ef918de249bdbec0498a14468aa1e16449bd [file] [log] [blame]
// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package org.unicode.icu.tool.cldrtoicu.mapper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.joining;
import static org.unicode.icu.tool.cldrtoicu.mapper.TransformsMapperTest.Direction.BACKWARD;
import static org.unicode.icu.tool.cldrtoicu.mapper.TransformsMapperTest.Direction.BOTH;
import static org.unicode.icu.tool.cldrtoicu.mapper.TransformsMapperTest.Direction.FORWARD;
import static org.unicode.icu.tool.cldrtoicu.mapper.TransformsMapperTest.Visibility.EXTERNAL;
import static org.unicode.icu.tool.cldrtoicu.mapper.TransformsMapperTest.Visibility.INTERNAL;
import static org.unicode.icu.tool.cldrtoicu.testing.IcuDataSubjectFactory.assertThat;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.unicode.cldr.api.CldrData;
import org.unicode.cldr.api.CldrDataSupplier;
import org.unicode.cldr.api.CldrValue;
import org.unicode.icu.tool.cldrtoicu.IcuData;
import org.unicode.icu.tool.cldrtoicu.RbPath;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
@RunWith(JUnit4.class)
public class TransformsMapperTest {
private static final ImmutableList<String> FILE_HEADER = ImmutableList.of(
"\uFEFF# © 2016 and later: Unicode, Inc. and others.",
"# License & terms of use: http://www.unicode.org/copyright.html#License",
"#");
private static final int DEFAULT_PATH_COUNT = 7;
enum Direction {
FORWARD, BACKWARD, BOTH;
@Override public String toString() {
return Ascii.toLowerCase(name());
}
}
enum Visibility {
INTERNAL, EXTERNAL;
@Override public String toString() {
return Ascii.toLowerCase(name());
}
}
@Test
public void testDefaultContent() {
Map<String, String> fileMap = new TreeMap<>();
IcuData icuData = TransformsMapper.process(cldrData(), wrap(fileMap));
assertThat(fileMap).isEmpty();
assertThat(icuData).getPaths().hasSize(DEFAULT_PATH_COUNT);
assertThat(icuData).hasValuesFor("/\"%Translit%Hex\"", "%Translit%Hex");
assertThat(icuData).hasValuesFor("/\"%Translit%UnicodeChar\"", "%Translit%UnicodeChar");
assertThat(icuData).hasValuesFor("/\"%Translit%UnicodeName\"", "%Translit%UnicodeName");
assertThat(icuData)
.hasValuesFor("/RuleBasedTransliteratorIDs/Digit-Tone/alias", "NumericPinyin-Pinyin");
assertThat(icuData)
.hasValuesFor("/RuleBasedTransliteratorIDs/Tone-Digit/alias", "Pinyin-NumericPinyin");
assertThat(icuData).hasValuesFor("TransliterateLATIN", "", "");
assertThat(icuData)
.hasValuesFor("TransliteratorNamePattern", "{0,choice,0#|1#{1}|2#{1}-{2}}");
}
@Test
public void testForward() {
int idx = 0;
CldrData cldrData =
cldrData(oneWay("foo", "bar", FORWARD, null, INTERNAL, "first second third", ++idx));
Map<String, String> fileMap = new TreeMap<>();
IcuData icuData = TransformsMapper.process(cldrData, wrap(fileMap));
assertThat(icuData).getPaths().hasSize(DEFAULT_PATH_COUNT + 5);
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/first/alias", "foo-bar");
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/second/alias", "foo-bar");
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/third/alias", "foo-bar");
assertThat(icuData)
.hasValuesFor("RuleBasedTransliteratorIDs/foo-bar/internal/direction", "FORWARD");
assertThat(icuData)
.hasValuesFor(
"RuleBasedTransliteratorIDs/foo-bar/internal/resource:process(transliterator)",
"foo_bar.txt");
assertThat(fileMap).hasSize(1);
assertThat(fileMap).containsEntry("foo_bar.txt", headerPlusLines(
"# File: foo_bar.txt",
"# Generated from CLDR",
"#",
"",
"foo --> bar [internal]:",
"first second third"));
}
@Test
public void testBackward() {
int idx = 0;
CldrData cldrData =
cldrData(oneWay("foo", "bar", BACKWARD, "variant", EXTERNAL, "one two three", ++idx));
Map<String, String> fileMap = new TreeMap<>();
IcuData icuData = TransformsMapper.process(cldrData, wrap(fileMap));
assertThat(icuData).getPaths().hasSize(DEFAULT_PATH_COUNT + 5);
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/one/alias", "bar-foo/variant");
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/two/alias", "bar-foo/variant");
assertThat(icuData).hasValuesFor("RuleBasedTransliteratorIDs/three/alias", "bar-foo/variant");
// Since the variant uses a '/' in the path element (not a path separator) we cannot just
// parse a string to get the expected path, so we do it the "hard way".
RbPath prefix = RbPath.of("RuleBasedTransliteratorIDs", "bar-foo/variant", "file");
assertThat(icuData).hasValuesFor(prefix.extendBy("direction"), "REVERSE");
assertThat(icuData)
.hasValuesFor(prefix.extendBy("resource:process(transliterator)"), "foo_bar_variant.txt");
assertThat(fileMap).hasSize(1);
assertThat(fileMap).containsEntry("foo_bar_variant.txt", headerPlusLines(
"# File: foo_bar_variant.txt",
"# Generated from CLDR",
"#",
"",
"foo <-- bar [external]:",
"one two three"));
}
@Test
public void testBoth() {
int idx = 0;
CldrData cldrData = cldrData(
both("foo", "bar", null, INTERNAL, "forward-alias", "backward-alias", ++idx));
Map<String, String> fileMap = new TreeMap<>();
IcuData icuData = TransformsMapper.process(cldrData, wrap(fileMap));
// 3 for each direction.
assertThat(icuData).getPaths().hasSize(DEFAULT_PATH_COUNT + 6);
// Both directions.
assertThat(icuData)
.hasValuesFor("RuleBasedTransliteratorIDs/foo-bar/internal/direction", "FORWARD");
assertThat(icuData)
.hasValuesFor("RuleBasedTransliteratorIDs/bar-foo/internal/direction", "REVERSE");
// Both aliases.
assertThat(icuData)
.hasValuesFor("RuleBasedTransliteratorIDs/forward-alias/alias", "foo-bar");
assertThat(icuData)
.hasValuesFor("RuleBasedTransliteratorIDs/backward-alias/alias", "bar-foo");
// But the file is the same (obvious really since there's only one).
assertThat(icuData).hasValuesFor(
"RuleBasedTransliteratorIDs/foo-bar/internal/resource:process(transliterator)",
"foo_bar.txt");
assertThat(icuData).hasValuesFor(
"RuleBasedTransliteratorIDs/bar-foo/internal/resource:process(transliterator)",
"foo_bar.txt");
assertThat(fileMap).hasSize(1);
assertThat(fileMap).containsEntry("foo_bar.txt", headerPlusLines(
"# File: foo_bar.txt",
"# Generated from CLDR",
"#",
"",
"foo <-> bar [internal]:",
"forward-alias",
"backward-alias"));
}
private String headerPlusLines(String... lines) {
// For now the files always contain a blank line at the end (to match legacy behaviour) but
// this can, and probably should be changed.
return Stream
.concat(FILE_HEADER.stream(), Arrays.stream(lines))
.collect(joining("\n", "", "\n\n"));
}
private static CldrData cldrData(CldrValue... values) {
return CldrDataSupplier.forValues(Arrays.asList(values));
}
private static CldrValue oneWay(
String src, String dst, Direction dir, String var, Visibility vis, String alias, int idx) {
checkArgument(dir != BOTH, "use both() for bidirectional transforms");
StringBuilder cldrPath = new StringBuilder("//supplementalData/transforms/transform");
appendAttribute(cldrPath, "source", src);
appendAttribute(cldrPath, "target", dst);
appendAttribute(cldrPath, "direction", dir);
if (var != null) {
appendAttribute(cldrPath, "variant", var);
}
appendAttribute(cldrPath, "visibility", vis);
appendAttribute(cldrPath, dir == FORWARD ? "alias" : "backwardAlias", alias);
cldrPath.append("/tRule#").append(idx);
String arrow = dir == FORWARD ? "-->" : "<--";
return CldrValue.parseValue(
cldrPath.toString(),
String.format("%s %s %s [%s]:\n%s", src, arrow, dst, vis, alias));
}
private static CldrValue both(
String src, String dst, String var, Visibility vis, String alias, String backAlias, int idx) {
StringBuilder cldrPath = new StringBuilder("//supplementalData/transforms/transform");
appendAttribute(cldrPath, "source", src);
appendAttribute(cldrPath, "target", dst);
appendAttribute(cldrPath, "direction", BOTH);
if (var != null) {
appendAttribute(cldrPath, "variant", var);
}
appendAttribute(cldrPath, "visibility", vis);
appendAttribute(cldrPath, "alias", alias);
appendAttribute(cldrPath, "backwardAlias", backAlias);
cldrPath.append("/tRule#").append(idx);
return CldrValue.parseValue(
cldrPath.toString(),
String.format("%s <-> %s [%s]:\n%s\n%s", src, dst, vis, alias, backAlias));
}
private static void appendAttribute(StringBuilder out, String k, Object v) {
out.append(String.format("[@%s=\"%s\"]", k, v));
}
private static Function<Path, PrintWriter> wrap(Map<String, String> data) {
return path -> {
Writer writer = new Writer() {
StringWriter buffer = new StringWriter();
@Override public void write(char[] chars, int offset, int length) {
buffer.write(chars, offset, length);
}
@Override public void flush() {
buffer.flush();
}
@Override public void close() throws IOException {
buffer.close();
data.put(path.toString(), buffer.toString());
}
};
return new PrintWriter(writer);
};
}
}