• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2020 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 package org.unicode.icu.tool.cldrtoicu;
4 
5 import static com.google.common.truth.Truth.assertThat;
6 
7 import java.util.Arrays;
8 import java.util.LinkedHashMap;
9 import java.util.Map;
10 import java.util.Objects;
11 
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 import org.junit.runners.JUnit4;
15 import org.unicode.cldr.api.AttributeKey;
16 import org.unicode.cldr.api.CldrData;
17 import org.unicode.cldr.api.CldrDataSupplier;
18 import org.unicode.cldr.api.CldrValue;
19 
20 import com.google.common.collect.ImmutableMap;
21 
22 @RunWith(JUnit4.class)
23 public class CldrDataProcessorTest {
24 
25     private static final AttributeKey TERRITORY_TYPE = AttributeKey.keyOf("territory", "type");
26     private static final AttributeKey CURRENCY_TYPE = AttributeKey.keyOf("currency", "type");
27 
28     // An overly simplistic value type for currency for testing purposes. In real code you would
29     // probably want an immutable type and a separate builder, or a mutable type just to collect
30     // values that doesn't need equals/hashcode (this class serves 2 purposes in the test).
31     private static final class CurrencyData {
32         final String key;
33         String name = "";
34         String symbol = "";
35 
CurrencyData(String key)36         CurrencyData(String key) {
37             this.key = key;
38         }
39 
CurrencyData(String key, String name, String symbol)40         CurrencyData(String key, String name, String symbol) {
41             this.key = key;
42             this.name = name;
43             this.symbol = symbol;
44         }
45 
equals(Object o)46         @Override public boolean equals(Object o) {
47             if (o instanceof CurrencyData) {
48                 CurrencyData that = (CurrencyData) o;
49                 return key.equals(that.key) && name.equals(that.name) && symbol.equals(that.symbol);
50             }
51             return false;
52         }
53 
hashCode()54         @Override public int hashCode() {
55             return Objects.hash(key, name, symbol);
56         }
57 
toString()58         @Override public String toString() {
59             return String.format("CurrencyData{name=%s, symbol='%s'}", name, symbol);
60         }
61     }
62 
63     // For collecting processed values.
64     private static final class State {
65         ImmutableMap<String, String> names = ImmutableMap.of();
66         ImmutableMap<String, CurrencyData> currencies = ImmutableMap.of();
67 
setNames(Map<String, String> map)68         void setNames(Map<String, String> map) {
69             names = ImmutableMap.copyOf(map);
70         }
71 
setCurrencies(Map<String, CurrencyData> map)72         void setCurrencies(Map<String, CurrencyData> map) {
73             currencies = ImmutableMap.copyOf(map);
74         }
75     }
76 
77     private static final CldrDataProcessor<State> VISITOR = createTestVisitor();
78 
createTestVisitor()79     private static CldrDataProcessor<State> createTestVisitor() {
80         // Note that this is deliberately doing things the "messy" way by creating and then copying
81         // a map. This is to show an extra level of processing in tests. You could just have a
82         // value action which adds the territory to a map in the State object.
83         CldrDataProcessor.Builder<State> builder = CldrDataProcessor.builder();
84         builder
85             .addAction(
86                 "//ldml/localeDisplayNames/territories",
87                 () -> new LinkedHashMap<String, String>(),
88                 State::setNames)
89             .addValueAction(
90                 "territory[@type=*]",
91                 (map, value) -> map.put(value.getPath().get(TERRITORY_TYPE), value.getValue()));
92 
93         // Another convoluted example for testing. This has the same additional level for a map
94         // just so we can show a 3-level processor. In real code this wouldn't look so messy.
95         CldrDataProcessor.SubProcessor<CurrencyData> currencyProcessor = builder
96             .addAction(
97                 "//ldml/numbers/currencies",
98                 () -> new LinkedHashMap<String, CurrencyData>(),
99                 State::setCurrencies)
100             .addAction(
101                 "currency[@type=*]",
102                 (map, path) -> new CurrencyData(path.get(CURRENCY_TYPE)),
103                 (map, data) -> map.put(data.key, data));
104         currencyProcessor.addValueAction(
105             "displayName",
106             (data, value) -> data.name = value.getValue());
107         currencyProcessor.addValueAction(
108             "symbol",
109             (data, value) -> data.symbol = value.getValue());
110 
111         return builder.build();
112     }
113 
114     @Test
testTwoLevelProcessing()115     public void testTwoLevelProcessing() {
116         CldrData data = CldrDataSupplier.forValues(Arrays.asList(
117             ldml("localeDisplayNames/territories/territory[@type=\"BE\"]", "Belgium"),
118             ldml("localeDisplayNames/territories/territory[@type=\"CH\"]", "Switzerland"),
119             ldml("localeDisplayNames/territories/territory[@type=\"IN\"]", "India")));
120 
121         State state = VISITOR.process(data, new State(), CldrData.PathOrder.DTD);
122 
123         assertThat(state.names)
124             .containsExactly(
125                 "BE", "Belgium",
126                 "CH", "Switzerland",
127                 "IN", "India")
128             .inOrder();
129     }
130 
131     @Test
testThreeLevelProcessing()132     public void testThreeLevelProcessing() {
133         CldrData data = CldrDataSupplier.forValues(Arrays.asList(
134             ldml("numbers/currencies/currency[@type=\"EUR\"]/displayName", "euro"),
135             ldml("numbers/currencies/currency[@type=\"EUR\"]/symbol", "€"),
136             ldml("numbers/currencies/currency[@type=\"CHF\"]/displayName", "Swiss franc"),
137             ldml("numbers/currencies/currency[@type=\"CHF\"]/symbol", "Fr."),
138             ldml("numbers/currencies/currency[@type=\"INR\"]/displayName", "Indian rupee"),
139             ldml("numbers/currencies/currency[@type=\"INR\"]/symbol", "₹")));
140 
141         State state = VISITOR.process(data, new State(), CldrData.PathOrder.DTD);
142 
143         assertThat(state.currencies)
144             .containsExactly(
145                 "CHF", new CurrencyData("CHF", "Swiss franc", "Fr."),
146                 "EUR", new CurrencyData("EUR", "euro", "€"),
147                 "INR", new CurrencyData("INR", "Indian rupee", "₹"))
148             .inOrder();
149     }
150 
ldml(String path, String value)151     private static CldrValue ldml(String path, String value) {
152         return CldrValue.parseValue("//ldml/" + path, value);
153     }
154 }
155