• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.unittest;
2 
3 import com.google.common.collect.ImmutableSet;
4 import com.ibm.icu.dev.test.TestFmwk;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.LinkedHashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.Set;
16 import java.util.TreeSet;
17 import java.util.stream.Collectors;
18 import org.unicode.cldr.test.ExampleGenerator;
19 import org.unicode.cldr.test.ExampleGenerator.UnitLength;
20 import org.unicode.cldr.util.CLDRConfig;
21 import org.unicode.cldr.util.CLDRFile;
22 import org.unicode.cldr.util.CldrUtility;
23 import org.unicode.cldr.util.Factory;
24 import org.unicode.cldr.util.GrammarInfo;
25 import org.unicode.cldr.util.GrammarInfo.CaseValues;
26 import org.unicode.cldr.util.GrammarInfo.GrammaticalFeature;
27 import org.unicode.cldr.util.GrammarInfo.GrammaticalScope;
28 import org.unicode.cldr.util.GrammarInfo.GrammaticalTarget;
29 import org.unicode.cldr.util.Pair;
30 import org.unicode.cldr.util.PathStarrer;
31 import org.unicode.cldr.util.SupplementalDataInfo;
32 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo;
33 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count;
34 import org.unicode.cldr.util.SupplementalDataInfo.PluralType;
35 import org.unicode.cldr.util.UnitPathType;
36 import org.unicode.cldr.util.With;
37 
38 public class TestExampleGenerator extends TestFmwk {
39 
40     boolean showTranslationPaths =
41             CldrUtility.getProperty("TestExampleGenerator:showTranslationPaths", false);
42 
43     private static final SupplementalDataInfo SDI = SupplementalDataInfo.getInstance();
44     CLDRConfig info = CLDRConfig.getInstance();
45 
main(String[] args)46     public static void main(String[] args) {
47         new TestExampleGenerator().run(args);
48     }
49 
testCurrency()50     public void testCurrency() {
51         String[][] tests = {
52             {
53                 "fr",
54                 "one",
55                 "〖❬1,23 ❭value-one〗〖❬0,00 ❭value-one〗",
56                 "〖❬1,23❭_❬dollar des États-Unis❭〗〖❬1,23❭_❬euro❭〗〖❬0,00❭_❬dollar des États-Unis❭〗〖❬0,00❭_❬euro❭〗"
57             },
58             {
59                 "fr",
60                 "other",
61                 "〖❬2,34 ❭value-other〗〖❬3,45 ❭value-other〗",
62                 "〖❬2,34❭_❬dollars des États-Unis❭〗〖❬2,34❭_❬euros❭〗〖❬3,45❭_❬dollars des États-Unis❭〗〖❬3,45❭_❬euros❭〗"
63             },
64             {"en", "one", "〖❬1 ❭Bermudan dollar〗", "〖❬1❭ ❬US dollar❭〗〖❬1❭ ❬euro❭〗"},
65             {
66                 "en",
67                 "other",
68                 "〖❬1.23 ❭Bermudan dollars〗〖❬0.00 ❭Bermudan dollars〗",
69                 "〖❬1.23❭ ❬US dollars❭〗〖❬1.23❭ ❬euros❭〗〖❬0.00❭ ❬US dollars❭〗〖❬0.00❭ ❬euros❭〗"
70             },
71         };
72         String sampleCurrencyPatternPrefix =
73                 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/unitPattern[@count=\"";
74         String sampleCurrencyPrefix =
75                 "//ldml/numbers/currencies/currency[@type=\"BMD\"]/displayName[@count=\"";
76         String sampleTemplateSuffix = "\"]";
77 
78         for (String[] row : tests) {
79             ExampleGenerator exampleGenerator = getExampleGenerator(row[0]);
80             String value = "value-" + row[1];
81 
82             String path = sampleCurrencyPrefix + row[1] + sampleTemplateSuffix;
83             String result =
84                     ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false);
85             assertEquals(row[0] + "-" + row[1] + "-BMD", row[2], result);
86 
87             value = "{0}_{1}";
88             path = sampleCurrencyPatternPrefix + row[1] + sampleTemplateSuffix;
89             result = ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false);
90             assertEquals(row[0] + "-" + row[1] + "-pat", row[3], result);
91         }
92     }
93 
94     /**
95      * Only add to this if the example should NEVER appear. <br>
96      * WARNING - do not disable the test by putting in too broad a match. Make sure the paths are
97      * reasonably granular.
98      */
99     static final Set<String> DELIBERATE_EXCLUDED_EXAMPLES =
100             ImmutableSet.of(
101                     "//ldml/layout/orientation/characterOrder",
102                     "//ldml/layout/orientation/lineOrder",
103                     "//ldml/characters/moreInformation",
104                     "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/infinity",
105                     "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/list",
106                     "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/nan",
107                     "//ldml/numbers/currencies/currency[@type=\"([^\"]*+)\"]/displayName",
108                     "//ldml/localeDisplayNames/measurementSystemNames/measurementSystemName[@type=\"([^\"]*+)\"]",
109                     // old format
110                     "//ldml/numbers/symbols/infinity",
111                     "//ldml/numbers/symbols/list",
112                     "//ldml/numbers/symbols/nan",
113                     "//ldml/posix/messages/nostr",
114                     "//ldml/posix/messages/yesstr",
115                     "//ldml/contextTransforms/contextTransformUsage[@type=\"([^\"]*+)\"]/contextTransform[@type=\"([^\"]*+)\"]",
116                     "//ldml/characters/exemplarCharacters",
117                     "//ldml/characters/exemplarCharacters[@type=\"([^\"]*+)\"]",
118                     "//ldml/characters/parseLenients.*",
119                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"]",
120                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/days/dayContext[@type=\"([^\"]*+)\"]/dayWidth[@type=\"([^\"]*+)\"]/day[@type=\"([^\"]*+)\"]",
121                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/quarters/quarterContext[@type=\"([^\"]*+)\"]/quarterWidth[@type=\"([^\"]*+)\"]/quarter[@type=\"([^\"]*+)\"]",
122                     "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/displayName",
123                     "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relative[@type=\"([^\"]*+)\"]",
124                     "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relativeTime[@type=\"([^\"]*+)\"]/relativeTimePattern[@count=\"([^\"]*+)\"]",
125                     "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relativePeriod",
126                     "//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/displayName[@alt=\"([^\"]*+)\"]",
127                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/cyclicNameSets/cyclicNameSet[@type=\"([^\"]*+)\"]/cyclicNameContext[@type=\"([^\"]*+)\"]/cyclicNameWidth[@type=\"([^\"]*+)\"]/cyclicName[@type=\"([^\"]*+)\"]",
128                     "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"([^\"]*+)\"]",
129                     "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"([^\"]*+)\"]",
130                     "//ldml/characters/parseLenients[@scope=\"([^\"]*+)\"][@level=\"([^\"]*+)\"]/parseLenient[@sample=\"([^\"]*+)\"]");
131     // Only add to above if the example should NEVER appear.
132 
133     /**
134      * Add to this if the example SHOULD appear, but we don't have it yet. <br>
135      * TODO Add later
136      */
137     static final Set<String> TEMPORARY_EXCLUDED_EXAMPLES =
138             ImmutableSet.of(
139                     "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/currencyMatch",
140                     "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/surroundingMatch",
141                     "//ldml/numbers/currencyFormats/currencySpacing/beforeCurrency/insertBetween",
142                     "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/currencyMatch",
143                     "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/surroundingMatch",
144                     "//ldml/numbers/currencyFormats/currencySpacing/afterCurrency/insertBetween",
145                     "//ldml/numbers/currencyFormats/currencyPatternAppendISO", // TODO see
146                     // CLDR-14831
147                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/currencyMatch",
148                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/surroundingMatch",
149                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/beforeCurrency/insertBetween",
150                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/currencyMatch",
151                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/surroundingMatch",
152                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencySpacing/afterCurrency/insertBetween",
153                     "//ldml/numbers/currencyFormats[@numberSystem=\"([^\"]*+)\"]/currencyPatternAppendISO", // TODO see CLDR-14831
154                     "//ldml/localeDisplayNames/variants/variant[@type=\"([^\"]*+)\"]",
155                     "//ldml/localeDisplayNames/keys/key[@type=\"([^\"]*+)\"]",
156                     "//ldml/localeDisplayNames/types/type[@key=\"([^\"]*+)\"][@type=\"([^\"]*+)\"]",
157                     "//ldml/localeDisplayNames/types/type[@key=\"([^\"]*+)\"][@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
158                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"]",
159                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraAbbr/era[@type=\"([^\"]*+)\"]",
160                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNarrow/era[@type=\"([^\"]*+)\"]",
161                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/dateFormat[@type=\"([^\"]*+)\"]/datetimeSkeleton",
162                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/datetimeSkeleton",
163                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/datetimeSkeleton",
164                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/datetimeSkeleton",
165                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/appendItems/appendItem[@request=\"([^\"]*+)\"]",
166                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatFallback",
167                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatItem[@id=\"([^\"]*+)\"]/greatestDifference[@id=\"([^\"]*+)\"]",
168                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
169                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraAbbr/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
170                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNarrow/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
171                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"][@yeartype=\"([^\"]*+)\"]",
172                     "//ldml/dates/timeZoneNames/gmtZeroFormat",
173                     "//ldml/numbers/minimumGroupingDigits",
174                     "//ldml/numbers/symbols/timeSeparator",
175                     "//ldml/numbers/symbols[@numberSystem=\"([^\"]*+)\"]/timeSeparator",
176                     "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/unit[@type=\"([^\"]*+)\"]/displayName",
177                     "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/unit[@type=\"([^\"]*+)\"]/perUnitPattern",
178                     "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/coordinateUnit/coordinateUnitPattern[@type=\"([^\"]*+)\"]",
179                     "//ldml/units/unitLength[@type=\"([^\"]*+)\"]/coordinateUnit/displayName",
180                     "//ldml/characterLabels/characterLabelPattern[@type=\"([^\"]*+)\"]",
181                     "//ldml/characterLabels/characterLabelPattern[@type=\"([^\"]*+)\"][@count=\"([^\"]*+)\"]",
182                     "//ldml/characterLabels/characterLabel[@type=\"([^\"]*+)\"]",
183                     "//ldml/typographicNames/axisName[@type=\"([^\"]*+)\"]",
184                     "//ldml/typographicNames/styleName[@type=\"([^\"]*+)\"][@subtype=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
185                     "//ldml/typographicNames/styleName[@type=\"([^\"]*+)\"][@subtype=\"([^\"]*+)\"]",
186                     "//ldml/typographicNames/featureName[@type=\"([^\"]*+)\"]",
187                     "//ldml/localeDisplayNames/subdivisions/subdivision[@type=\"([^\"]*+)\"]",
188                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/standard", // Error:
189                     // (TestExampleGenerator.java:245) No background:   <Coordinated Universal Time>
190                     //    〖Coordinated Universal Time〗
191                     "//ldml/personNames/nameOrderLocales[@order=\"([^\"]*+)\"]", // TODO CLDR-15384
192                     "//ldml/personNames/foreignSpaceReplacement[@xml:space=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384
193                     "//ldml/personNames/foreignSpaceReplacement[@xml:space=\"([^\"]*+)\"]", // TODO
194                     "//ldml/personNames/foreignSpaceReplacement[@alt=\"([^\"]*+)\"]",
195                     "//ldml/personNames/foreignSpaceReplacement", // TODO CLDR-15384
196                     "//ldml/personNames/nativeSpaceReplacement[@xml:space=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384
197                     "//ldml/personNames/nativeSpaceReplacement[@xml:space=\"([^\"]*+)\"]", // TODO
198                     "//ldml/personNames/nativeSpaceReplacement[@alt=\"([^\"]*+)\"]",
199                     "//ldml/personNames/nativeSpaceReplacement", // TODO CLDR-15384
200                     "//ldml/personNames/initialPattern[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384
201                     "//ldml/personNames/initialPattern[@type=\"([^\"]*+)\"]", // TODO CLDR-15384
202                     "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@usage=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern[@alt=\"([^\"]*+)\"]", // TODO CLDR-15384
203                     "//ldml/personNames/sampleName[@item=\"([^\"]*+)\"]/nameField[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // TODO CLDR-15384
204                     "//ldml/personNames/sampleName[@item=\"([^\"]*+)\"]/nameField[@type=\"([^\"]*+)\"]", // TODO CLDR-15384
205                     "//ldml/personNames/parameterDefault[@parameter=\"([^\"]*+)\"]" // TODO
206                     // CLDR-15384
207                     );
208     // Add to above if the example SHOULD appear, but we don't have it yet. TODO Add later
209 
210     /**
211      * Only add to this if the background should NEVER appear. <br>
212      * The background is used when the element is used as part of another format. <br>
213      * WARNING - do not disable the test by putting in too broad a match. Make sure the paths are
214      * reasonably granular.
215      */
216     static final Set<String> DELIBERATE_OK_TO_MISS_BACKGROUND =
217             ImmutableSet.of(
218                     "//ldml/numbers/defaultNumberingSystem",
219                     "//ldml/numbers/otherNumberingSystems/native",
220                     // TODO fix formatting
221                     "//ldml/characters/exemplarCharacters",
222                     "//ldml/characters/exemplarCharacters[@type=\"([^\"]*+)\"]",
223                     // TODO Add background
224                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // CLDR-16606
225                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // CLDR-16606
226                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateFormats/dateFormatLength[@type=\"([^\"]*+)\"]/dateFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"]",
227                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/timeFormats/timeFormatLength[@type=\"([^\"]*+)\"]/timeFormat[@type=\"([^\"]*+)\"]/pattern[@type=\"([^\"]*+)\"]",
228                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"]",
229                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/exemplarCity",
230                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/exemplarCity[@alt=\"([^\"]*+)\"]",
231                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/daylight",
232                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/generic",
233                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/standard",
234                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/short/daylight",
235                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/generic",
236                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/standard",
237                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/long/daylight",
238                     "//ldml/units/durationUnit[@type=\"([^\"]*+)\"]/durationUnitPattern");
239     // Only add to above if the background should NEVER appear.
240 
241     /**
242      * Add to this if the background SHOULD appear, but we don't have them yet. <br>
243      * The background is used when the element is used as part of another format. <br>
244      * TODO Add later
245      */
246     static final Set<String> TEMPORARY_OK_TO_MISS_BACKGROUND =
247             ImmutableSet.of(
248                     "//ldml/numbers/defaultNumberingSystem",
249                     "//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"([^\"]*+)\"][@count=\"([^\"]*+)\"]",
250                     "//ldml/dates/timeZoneNames/zone[@type=\"([^\"]*+)\"]/long/standard",
251                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/generic",
252                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/standard",
253                     "//ldml/dates/timeZoneNames/metazone[@type=\"([^\"]*+)\"]/short/daylight",
254                     "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern",
255                     "//ldml/personNames/personName[@order=\"([^\"]*+)\"][@length=\"([^\"]*+)\"][@usage=\"([^\"]*+)\"][@formality=\"([^\"]*+)\"]/namePattern"); // CLDR-15384
256     // Add to above if the background SHOULD appear, but we don't have them yet. TODO Add later
257 
TestAllPaths()258     public void TestAllPaths() {
259         ExampleGenerator exampleGenerator = getExampleGenerator("en");
260         PathStarrer ps = new PathStarrer();
261         Set<String> seen = new HashSet<>();
262         CLDRFile cldrFile = exampleGenerator.getCldrFile();
263         TreeSet<String> target = new TreeSet<>(cldrFile.getComparator());
264         cldrFile.fullIterable().forEach(target::add);
265         for (String path : target) {
266             String plainStarred = ps.set(path);
267             String value = cldrFile.getStringValue(path);
268             if (value == null
269                     || path.endsWith("/alias")
270                     || path.startsWith("//ldml/identity")
271                     || DELIBERATE_EXCLUDED_EXAMPLES.contains(plainStarred)) {
272                 continue;
273             }
274             if (TEMPORARY_EXCLUDED_EXAMPLES.contains(plainStarred)) {
275                 if (logKnownIssue(
276                         "Cldrbug:6342",
277                         "Need an example for each path used in context: " + plainStarred)) {
278                     continue;
279                 }
280                 continue;
281             }
282             String example = exampleGenerator.getExampleHtml(path, value);
283             String javaEscapedStarred = "\"" + plainStarred.replace("\"", "\\\"") + "\",";
284             if (example == null) {
285                 if (!seen.contains(javaEscapedStarred)) {
286                     errln("No example:\t<" + value + ">\t" + javaEscapedStarred);
287                 }
288             } else {
289                 String simplified = ExampleGenerator.simplify(example, false);
290 
291                 if (simplified.contains("null")) {
292                     if (true || !seen.contains(javaEscapedStarred)) {
293                         // debug
294                         exampleGenerator.getExampleHtml(path, value);
295                         ExampleGenerator.simplify(example, false);
296 
297                         errln(
298                                 "'null' in message:\t<"
299                                         + value
300                                         + ">\t"
301                                         + simplified
302                                         + "\t"
303                                         + javaEscapedStarred);
304                         // String example2 =
305                         // exampleGenerator.getExampleHtml(path, value); // for
306                         // debugging
307                     }
308                 } else if (!simplified.startsWith("〖")) {
309                     if (!seen.contains(javaEscapedStarred)) {
310                         errln(
311                                 "Funny HTML:\t<"
312                                         + value
313                                         + ">\t"
314                                         + simplified
315                                         + "\t"
316                                         + javaEscapedStarred);
317                     }
318                 } else if (!simplified.contains("❬")
319                         && !DELIBERATE_OK_TO_MISS_BACKGROUND.contains(plainStarred)) {
320                     if (!seen.contains(javaEscapedStarred)) {
321 
322                         if (TEMPORARY_OK_TO_MISS_BACKGROUND.contains(plainStarred)
323                                 && logKnownIssue(
324                                         "Cldrbug:6342",
325                                         "Make sure that background appears: "
326                                                 + simplified
327                                                 + "; "
328                                                 + plainStarred)) {
329                             continue;
330                         }
331 
332                         errln(
333                                 "No background:\t<"
334                                         + value
335                                         + ">\t"
336                                         + simplified
337                                         + "\t"
338                                         + javaEscapedStarred);
339                     }
340                 }
341             }
342             seen.add(javaEscapedStarred);
343         }
344     }
345 
TestUnits()346     public void TestUnits() {
347         ExampleGenerator exampleGenerator = getExampleGenerator("en");
348         String staticMeterExample =
349                 "〖1 meter ≡ 1,000 millimeter〗〖1 meter ≈ 1.0936 yard (US/UK)〗〖1 meter ≡ 1/1000 kilometer〗〖1 meter ≈ 621.37×10ˆ-6 mile (US/UK)〗";
350         String staticMeterExampleJp =
351                 "〖1 meter ≡ 1,000 millimeter〗〖1 meter ≡ 3.025 jo-jp (JP)〗〖1 meter ≈ 1.0936 yard (US/UK)〗〖1 meter ≈ 0.0023341 ri-jp (JP)〗〖1 meter ≡ 1/1000 kilometer〗〖1 meter ≈ 621.37×10ˆ-6 mile (US/UK)〗";
352         String staticMeterExampleRi =
353                 "〖1 ri-jp (JP) ≡ 1,296 jo-jp (JP)〗〖1 ri-jp (JP) ≈ 468.54 yard (US/UK)〗〖1 ri-jp (JP) ≈ 428.43 meter〗〖1 ri-jp (JP) ≈ 0.42843 kilometer〗〖1 ri-jp (JP) ≈ 0.26621 mile (US/UK)〗";
354 
355         checkValue(
356                 "Length m",
357                 staticMeterExample,
358                 exampleGenerator,
359                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/displayName");
360         checkValue(
361                 "Duration hm",
362                 "〖5:37〗",
363                 exampleGenerator,
364                 "//ldml/units/durationUnit[@type=\"hm\"]/durationUnitPattern");
365         checkValue(
366                 "Length m",
367                 "〖❬1❭ meter〗〖〗" + staticMeterExample,
368                 exampleGenerator,
369                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"one\"]");
370         checkValue(
371                 "Length m",
372                 "〖❬1.5❭ meters〗〖〗" + staticMeterExample,
373                 exampleGenerator,
374                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]");
375         checkValue(
376                 "Length m",
377                 "〖❬1.5❭ m〗〖〗" + staticMeterExample,
378                 exampleGenerator,
379                 "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]");
380         checkValue(
381                 "Length m",
382                 "〖❬1.5❭m〗〖〗" + staticMeterExample,
383                 exampleGenerator,
384                 "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]");
385 
386         // The following are to ensure that we properly generate an example when we have a
387         // non-winning value
388         checkValue(
389                 "Length m",
390                 "〖❬1.5❭ badmeter〗〖〗" + staticMeterExample,
391                 exampleGenerator,
392                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]",
393                 "{0} badmeter");
394 
395         ExampleGenerator exampleGeneratorDe = getExampleGenerator("de");
396         checkValue(
397                 "Length m",
398                 "〖❬1,5❭ badmeter〗〖❬Anstatt 1,5❭ badmeter❬ …❭〗〖❌  ❬… für 1,5❭ badmeter❬ …❭〗〖〗"
399                         + staticMeterExample,
400                 exampleGeneratorDe,
401                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"][@case=\"genitive\"]",
402                 "{0} badmeter");
403 
404         ExampleGenerator exampleGeneratorJa = getExampleGenerator("ja");
405         checkValue(
406                 "Length m",
407                 "〖❬1.5❭m〗〖〗" + staticMeterExampleJp,
408                 exampleGeneratorJa,
409                 "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"other\"]");
410         checkValue(
411                 "Length ri",
412                 "〖❬1.5❭ 里〗〖〗" + staticMeterExampleRi,
413                 exampleGeneratorJa,
414                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-ri-jp\"]/unitPattern[@count=\"other\"]");
415     }
416 
417     /**
418      * Check that the expected exampleGenerator example is produced for the parameters, with the
419      * value coming from the file.
420      */
checkValue( String message, String expected, ExampleGenerator exampleGenerator, String path)421     private void checkValue(
422             String message, String expected, ExampleGenerator exampleGenerator, String path) {
423         checkValue(message, expected, exampleGenerator, path, null);
424     }
425 
426     /** Check that the expected exampleGenerator example is produced for the parameters */
checkValue( String message, String expected, ExampleGenerator exampleGenerator, String path, String value)427     private void checkValue(
428             String message,
429             String expected,
430             ExampleGenerator exampleGenerator,
431             String path,
432             String value) {
433         final CLDRFile cldrFile = exampleGenerator.getCldrFile();
434         value = value != null ? value : cldrFile.getStringValue(path);
435         String actual = exampleGenerator.getExampleHtml(path, value);
436         assertEquals(
437                 cldrFile.getLocaleID() + ": " + message,
438                 expected,
439                 ExampleGenerator.simplify(actual, false));
440     }
441 
TestCompoundUnit()442     public void TestCompoundUnit() {
443         String[][] tests = {
444             {"per", "LONG", "one", "〖❬1 meter❭ per ❬second❭〗"},
445             {"per", "SHORT", "one", "〖❬1 m❭/❬sec❭〗"},
446             {"per", "NARROW", "one", "〖❬1m❭/❬s❭〗"},
447             {"per", "LONG", "other", "〖❬1.5 meters❭ per ❬second❭〗"},
448             {"per", "SHORT", "other", "〖❬1.5 m❭/❬sec❭〗"},
449             {"per", "NARROW", "other", "〖❬1.5m❭/❬s❭〗"},
450             {"times", "LONG", "one", "〖❬1 newton❭-❬meter❭〗"},
451             {"times", "SHORT", "one", "〖❬1 N❭⋅❬m❭〗"},
452             {"times", "NARROW", "one", "〖❬1N❭⋅❬m❭〗"},
453             {"times", "LONG", "other", "〖❬1.5 newton❭-❬meters❭〗"},
454             {"times", "SHORT", "other", "〖❬1.5 N❭⋅❬m❭〗"},
455             {"times", "NARROW", "other", "〖❬1.5N❭⋅❬m❭〗"},
456         };
457         checkCompoundUnits("en", tests);
458         // reenable these after Arabic has meter translated
459         // String[][] tests2 = {
460         // {"LONG", "few", "〖❬1 meter❭ per ❬second❭〗"},
461         // };
462         // checkCompoundUnits("ar", tests2);
463     }
464 
checkCompoundUnits(String locale, String[][] tests)465     private void checkCompoundUnits(String locale, String[][] tests) {
466         ExampleGenerator exampleGenerator = getExampleGenerator(locale);
467         for (String[] test : tests) {
468             String actual =
469                     exampleGenerator.handleCompoundUnit(
470                             UnitLength.valueOf(test[1]), test[0], Count.valueOf(test[2]));
471             assertEquals("CompoundUnit", test[3], ExampleGenerator.simplify(actual, true));
472         }
473     }
474 
TestTranslationPaths()475     public void TestTranslationPaths() {
476         for (String locale : Arrays.asList("en", "el", "ru")) {
477             CLDRFile cldrFile = CLDRConfig.getInstance().getCldrFactory().make(locale, true);
478             ExampleGenerator exampleGenerator = getExampleGenerator(locale);
479 
480             for (UnitPathType pathType : UnitPathType.values()) {
481                 for (String width : Arrays.asList("long", "short", "narrow")) {
482                     if (pathType == UnitPathType.gender && !width.equals("long")) {
483                         continue;
484                     }
485                     for (String unit : pathType.sampleShortUnitType) {
486                         String path =
487                                 pathType.getTranslationPath(
488                                         cldrFile, width, unit, "one", "nominative", null);
489                         String value = cldrFile.getStringValue(path);
490                         if (value != null) {
491                             String example = exampleGenerator.getExampleHtml(path, value);
492                             if (assertNotNull(locale + "/" + path, example)) {
493                                 String simplified = ExampleGenerator.simplify(example, false);
494                                 if (showTranslationPaths) {
495                                     warnln(
496                                             locale
497                                                     + ", "
498                                                     + width
499                                                     + ", "
500                                                     + pathType.toString()
501                                                     + " ==>"
502                                                     + simplified);
503                                 }
504                             } else {
505                                 // for debugging
506                                 example = exampleGenerator.getExampleHtml(path, value);
507                             }
508                         }
509                     }
510                 }
511             }
512         }
513     }
514 
TestCompoundUnit2()515     public void TestCompoundUnit2() {
516         String[][] tests = {
517             {"de", "LONG", "other", "Quadrat{0}", "〖❬1,5 ❭Quadrat❬meter❭〗"},
518             {"en", "SHORT", "one", "z{0}", "〖❬1 ❭z❬m❭〗"},
519             {"en", "LONG", "other", "zetta{0}", "〖❬1.5 ❭zetta❬meters❭〗"},
520             {"en", "SHORT", "one", "{0}²", "〖❬1 m❭²〗"},
521             {"en", "LONG", "other", "square {0}", "〖❬1.5 ❭square ❬meters❭〗"},
522             {"de", "SHORT", "one", "z{0}", "〖❬1 ❭z❬m❭〗"},
523             {"de", "LONG", "other", "Zetta{0}", "〖❬1,5 ❭Zetta❬meter❭〗"},
524             {"de", "SHORT", "one", "{0}²", "〖❬1 m❭²〗"},
525             {"de", "LONG", "other", "Quadrat{0}", "〖❬1,5 ❭Quadrat❬meter❭〗"},
526         };
527         for (String[] test : tests) {
528 
529             ExampleGenerator exampleGenerator = getExampleGenerator(test[0]);
530 
531             String actual =
532                     exampleGenerator.handleCompoundUnit1(
533                             UnitLength.valueOf(test[1]), Count.valueOf(test[2]), test[3]);
534             assertEquals("CompoundUnit", test[4], ExampleGenerator.simplify(actual, true));
535         }
536     }
537 
TestCompoundUnit3()538     public void TestCompoundUnit3() {
539         final Factory cldrFactory = CLDRConfig.getInstance().getCldrFactory();
540         String[][] tests = {
541             // locale, path, value, expected-example
542             {
543                 "en",
544                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",
545                 "LOCALE",
546                 "〖square ❬meters❭〗"
547             }, //
548             {
549                 "en",
550                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",
551                 "LOCALE",
552                 "〖❬1 ❭square ❬meter❭〗"
553             }, //
554             {
555                 "en",
556                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",
557                 "LOCALE",
558                 "〖❬1.5 ❭square ❬meters❭〗"
559             }, //
560             {
561                 "en",
562                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",
563                 "LOCALE",
564                 "〖❬m❭²〗"
565             },
566             {
567                 "en",
568                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",
569                 "LOCALE",
570                 "〖❬1m❭²〗"
571             },
572             {
573                 "en",
574                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",
575                 "LOCALE",
576                 "〖❬1.5m❭²〗"
577             },
578 
579             // warning, french patterns has U+00A0 in them
580             {
581                 "fr",
582                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",
583                 "Square {0}",
584                 "〖Square ❬mètres❭〗"
585             },
586             {
587                 "fr",
588                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",
589                 "square {0}",
590                 "〖❬1,5 ❭square ❬mètre❭〗"
591             },
592             {
593                 "fr",
594                 "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",
595                 "squares {0}",
596                 "〖❬3,5 ❭squares ❬mètres❭〗"
597             },
598             {
599                 "fr",
600                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",
601                 "LOCALE",
602                 "〖❬m❭²〗"
603             },
604             {
605                 "fr",
606                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",
607                 "LOCALE",
608                 "〖❬1,5m❭²〗"
609             },
610             {
611                 "fr",
612                 "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",
613                 "LOCALE",
614                 "〖❬3,5m❭²〗"
615             },
616         };
617 
618         int lineCount = 0;
619         for (String[] test : tests) {
620 
621             final String localeID = test[0];
622             final String xpath = test[1];
623             String value = test[2];
624             String expected = test[3];
625 
626             ExampleGenerator exampleGenerator = getExampleGenerator(localeID);
627 
628             if (value.equals("LOCALE")) {
629                 value = cldrFactory.make(localeID, true).getStringValue(xpath);
630             }
631             String actual = exampleGenerator.getExampleHtml(xpath, value);
632             assertEquals(
633                     ++lineCount + ") " + localeID + ", CompoundUnit3",
634                     expected,
635                     ExampleGenerator.simplify(actual, false));
636         }
637     }
638 
639     HashMap<String, ExampleGenerator> ExampleGeneratorCache = new HashMap<>();
640 
getExampleGenerator(String locale)641     private ExampleGenerator getExampleGenerator(String locale) {
642         ExampleGenerator result = ExampleGeneratorCache.get(locale);
643         if (result == null) {
644             final CLDRFile nativeCldrFile = info.getCLDRFile(locale, true);
645             result = new ExampleGenerator(nativeCldrFile, info.getEnglish());
646             ExampleGeneratorCache.put(locale, result);
647         }
648         return result;
649     }
650 
TestEllipsis()651     public void TestEllipsis() {
652         ExampleGenerator exampleGenerator = getExampleGenerator("it");
653         String[][] tests = {
654             {"initial", "〖…❬iappone❭〗"},
655             {"medial", "〖❬Svizzer❭…❬iappone❭〗"},
656             {"final", "〖❬Svizzer❭…〗"},
657             {"word-initial", "〖… ❬Giappone❭〗"},
658             {"word-medial", "〖❬Svizzera❭ … ❬Giappone❭〗"},
659             {"word-final", "〖❬Svizzera❭ …〗"},
660         };
661         for (String[] pair : tests) {
662             checkValue(
663                     exampleGenerator,
664                     "//ldml/characters/ellipsis[@type=\"" + pair[0] + "\"]",
665                     pair[1]);
666         }
667     }
668 
checkValue(ExampleGenerator exampleGenerator, String path, String expected)669     private void checkValue(ExampleGenerator exampleGenerator, String path, String expected) {
670         String value = exampleGenerator.getCldrFile().getStringValue(path);
671         String result =
672                 ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value), false);
673         assertEquals("Ellipsis", expected, result);
674     }
675 
simplify(String exampleHtml)676     public static String simplify(String exampleHtml) {
677         return ExampleGenerator.simplify(exampleHtml, false);
678     }
679 
TestClip()680     public void TestClip() {
681         assertEquals("Clipping", "bc", ExampleGenerator.clip("abc", 1, 0));
682         assertEquals("Clipping", "ab", ExampleGenerator.clip("abc", 0, 1));
683         assertEquals(
684                 "Clipping", "b\u0308c\u0308", ExampleGenerator.clip("a\u0308b\u0308c\u0308", 1, 0));
685         assertEquals(
686                 "Clipping", "a\u0308b\u0308", ExampleGenerator.clip("a\u0308b\u0308c\u0308", 0, 1));
687     }
688 
TestPaths()689     public void TestPaths() {
690         showCldrFile(info.getEnglish());
691         showCldrFile(info.getCLDRFile("fr", true));
692     }
693 
TestMiscPatterns()694     public void TestMiscPatterns() {
695         ExampleGenerator exampleGenerator = getExampleGenerator("it");
696         checkValue(
697                 "At least",
698                 "〖≥❬99❭〗",
699                 exampleGenerator,
700                 "//ldml/numbers/miscPatterns[@numberSystem=\"latn\"]/pattern[@type=\"atLeast\"]");
701         checkValue(
702                 "Range",
703                 "〖❬99❭-❬144❭〗",
704                 exampleGenerator,
705                 "//ldml/numbers/miscPatterns[@numberSystem=\"latn\"]/pattern[@type=\"range\"]");
706         // String actual = exampleGenerator.getExampleHtml(
707         // "//ldml/numbers/miscPatterns[@type=\"arab\"]/pattern[@type=\"atLeast\"]",
708         // "at least {0}", Zoomed.IN);
709         // assertEquals("Invalid format",
710         // "<div class='cldr_example'>at least 99</div>", actual);
711     }
712 
TestPluralSamples()713     public void TestPluralSamples() {
714         ExampleGenerator exampleGenerator = getExampleGenerator("sv");
715         String[][] tests = {
716             {
717                 "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"length-centimeter\"]/unitPattern[@count=\"one\"]",
718                 "Number should be one",
719                 "〖❬1❭ cm〗〖❬Jag tror att 1❭ cm❬ är tillräckligt.❭〗"
720             },
721             {
722                 "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"one\"]",
723                 "Ordinal one",
724                 "〖Ta ❬1❭:a svängen till höger〗〖❌  Ta ❬3❭:a svängen till höger〗"
725             },
726             {
727                 "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"other\"]",
728                 "Ordinal other",
729                 "〖Ta ❬3❭:e svängen till höger〗〖❌  Ta ❬1❭:e svängen till höger〗"
730             },
731         };
732         for (String[] row : tests) {
733             String path = row[0];
734             String message = row[1];
735             String expected = row[2];
736             checkValue(message, expected, exampleGenerator, path);
737         }
738     }
739 
TestLocaleDisplayPatterns()740     public void TestLocaleDisplayPatterns() {
741         ExampleGenerator exampleGenerator = getExampleGenerator("it");
742         String actual =
743                 exampleGenerator.getExampleHtml(
744                         "//ldml/localeDisplayNames/localeDisplayPattern/localePattern",
745                         "{0} [{1}]");
746         assertEquals(
747                 "localePattern example faulty",
748                 "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>Afghanistan</span>]</div>"
749                         + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>arabo, Afghanistan</span>]</div>"
750                         + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco</span> [<span class='cldr_substituted'>arabo, Afghanistan, Cifre indo-arabe, Fuso orario: Ora Etiopia</span>]</div>",
751                 actual);
752         actual =
753                 exampleGenerator.getExampleHtml(
754                         "//ldml/localeDisplayNames/localeDisplayPattern/localeSeparator",
755                         "{0}. {1}");
756         assertEquals(
757                 "localeSeparator example faulty",
758                 "<div class='cldr_example'><span class='cldr_substituted'>uzbeco (arabo</span>. <span class='cldr_substituted'>Afghanistan)</span></div>"
759                         + "<div class='cldr_example'><span class='cldr_substituted'>uzbeco (arabo</span>. <span class='cldr_substituted'>Afghanistan</span>. <span class='cldr_substituted'>Cifre indo-arabe</span>. <span class='cldr_substituted'>Fuso orario: Ora Etiopia)</span></div>",
760                 actual);
761     }
762 
TestCurrencyFormats()763     public void TestCurrencyFormats() {
764         ExampleGenerator exampleGenerator = getExampleGenerator("it");
765         String actual =
766                 simplify(
767                         exampleGenerator.getExampleHtml(
768                                 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/currencyFormatLength/currencyFormat[@type=\"standard\"]/pattern[@type=\"standard\"]",
769                                 "¤ #0.00"));
770         assertEquals("Currency format example faulty", "〖€ ❬1295,00❭〗〖-€ ❬1295,00❭〗", actual);
771     }
772 
TestCurrencyFormatsWithContext()773     public void TestCurrencyFormatsWithContext() {
774         ExampleGenerator exampleGenerator = getExampleGenerator("he");
775         String actual =
776                 simplify(
777                         exampleGenerator.getExampleHtml(
778                                 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/currencyFormatLength/currencyFormat[@type=\"standard\"]/pattern[@type=\"standard\"]",
779                                 "‏#,##0.00 ¤;‏-#,##0.00 ¤"));
780         assertEquals(
781                 "Currency format example faulty",
782                 "【‏❬1,295❭.❬00❭ ₪〗【⃪‏❬1,295❭.❬00❭ ₪〗【‏‎-❬1,295❭.❬00❭ ₪〗【⃪‏‎-❬1,295❭.❬00❭ ₪〗【‏❬1,295❭.❬00❭ ILS〗【⃪‏❬1,295❭.❬00❭ ILS〗【‏‎-❬1,295❭.❬00❭ ILS〗【⃪‏‎-❬1,295❭.❬00❭ ILS〗",
783                 actual);
784     }
785 
TestDateFormatsWithContext()786     public void TestDateFormatsWithContext() {
787         ExampleGenerator exampleGenerator = getExampleGenerator("ar");
788         String actual =
789                 simplify(
790                         exampleGenerator.getExampleHtml(
791                                 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateFormats/dateFormatLength[@type=\"short\"]/dateFormat[@type=\"standard\"]/pattern[@type=\"standard\"]",
792                                 "d‏/M‏/y"));
793         assertEquals("Currency format example faulty", "【٥‏/٩‏/١٩٩٩〗【⃪٥‏/٩‏/١٩٩٩〗", actual);
794     }
795 
TestDateTimeComboFormats()796     public void TestDateTimeComboFormats() {
797         ExampleGenerator exampleGenerator = getExampleGenerator("en");
798         checkValue(
799                 "DateTimeCombo long std",
800                 "〖❬September 5, 1999❭, ❬1:25:59 PM Eastern Standard Time❭〗〖❬September 5, 1999❭, ❬1:25 PM❭〗〖❬September 5, 1999❭, ❬7:00 AM – 1:25 PM❭〗〖❬today❭, ❬7:00 AM – 1:25 PM❭〗",
801                 exampleGenerator,
802                 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"long\"]/dateTimeFormat[@type=\"standard\"]/pattern[@type=\"standard\"]");
803         checkValue(
804                 "DateTimeCombo short std",
805                 "〖❬9/5/99❭, ❬1:25:59 PM Eastern Standard Time❭〗〖❬9/5/99❭, ❬1:25 PM❭〗〖❬9/5/99❭, ❬7:00 AM – 1:25 PM❭〗〖❬today❭, ❬7:00 AM – 1:25 PM❭〗",
806                 exampleGenerator,
807                 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"short\"]/dateTimeFormat[@type=\"standard\"]/pattern[@type=\"standard\"]");
808         checkValue(
809                 "DateTimeCombo long std",
810                 "〖❬September 5, 1999❭ at ❬1:25:59 PM Eastern Standard Time❭〗〖❬September 5, 1999❭ at ❬1:25 PM❭〗〖❬today❭ at ❬1:25 PM❭〗",
811                 exampleGenerator,
812                 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/dateTimeFormatLength[@type=\"long\"]/dateTimeFormat[@type=\"atTime\"]/pattern[@type=\"standard\"]");
813     }
814 
TestSymbols()815     public void TestSymbols() {
816         CLDRFile english = info.getEnglish();
817         ExampleGenerator exampleGenerator = new ExampleGenerator(english, english);
818         String actual =
819                 exampleGenerator.getExampleHtml(
820                         "//ldml/numbers/symbols[@numberSystem=\"latn\"]/superscriptingExponent",
821                         "x");
822 
823         assertEquals(
824                 "superscriptingExponent faulty",
825                 "<div class='cldr_example'><span class='cldr_substituted'>1.23456789</span>x10<span class='cldr_substituted'><sup>5</sup></span></div>",
826                 actual);
827     }
828 
TestFallbackFormat()829     public void TestFallbackFormat() {
830         ExampleGenerator exampleGenerator =
831                 new ExampleGenerator(info.getEnglish(), info.getEnglish());
832         String actual =
833                 exampleGenerator.getExampleHtml(
834                         "//ldml/dates/timeZoneNames/fallbackFormat", "{1} [{0}]");
835         assertEquals(
836                 "fallbackFormat faulty",
837                 "〖❬Central Time❭ [❬Cancún❭]〗",
838                 ExampleGenerator.simplify(actual, false));
839     }
840 
Test4897()841     public void Test4897() {
842         ExampleGenerator exampleGenerator = getExampleGenerator("it");
843         final CLDRFile cldrFile = exampleGenerator.getCldrFile();
844         for (String xpath :
845                 With.in(
846                         cldrFile.iterator(
847                                 "//ldml/dates/timeZoneNames", cldrFile.getComparator()))) {
848             String value = cldrFile.getStringValue(xpath);
849             String actual = exampleGenerator.getExampleHtml(xpath, value);
850             if (actual == null) {
851                 if (!xpath.contains("singleCountries") && !xpath.contains("gmtZeroFormat")) {
852                     errln("Null value for " + value + "\t" + xpath);
853                     // for debugging
854                     exampleGenerator.getExampleHtml(xpath, value);
855                 }
856             } else {
857                 logln(actual + "\t" + value + "\t" + xpath);
858             }
859         }
860     }
861 
Test4528()862     public void Test4528() {
863         String[][] testPairs = {
864             {
865                 "//ldml/numbers/currencies/currency[@type=\"BMD\"]/displayName[@count=\"other\"]",
866                 "〖❬1,23 ❭dollari delle Bermuda〗〖❬0,00 ❭dollari delle Bermuda〗"
867             },
868             {
869                 "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/unitPattern[@count=\"other\"]",
870                 "〖❬1,23❭ ❬dollari statunitensi❭〗〖❬1,23❭ ❬euro❭〗〖❬0,00❭ ❬dollari statunitensi❭〗〖❬0,00❭ ❬euro❭〗"
871             },
872             {"//ldml/numbers/currencies/currency[@type=\"BMD\"]/symbol", "〖❬123.456,79 ❭BMD〗"},
873         };
874 
875         ExampleGenerator exampleGenerator = getExampleGenerator("it");
876         for (String[] testPair : testPairs) {
877             String xpath = testPair[0];
878             String expected = testPair[1];
879             String value = exampleGenerator.getCldrFile().getStringValue(xpath);
880             String actual = simplify(exampleGenerator.getExampleHtml(xpath, value));
881             assertEquals("specifics", expected, actual);
882         }
883     }
884 
Test4607()885     public void Test4607() {
886         String[][] testPairs = {
887             {
888                 "//ldml/numbers/decimalFormats[@numberSystem=\"latn\"]/decimalFormatLength[@type=\"long\"]/decimalFormat[@type=\"standard\"]/pattern[@type=\"1000\"][@count=\"one\"]",
889                 "<div class='cldr_example'><span class='cldr_substituted'>1</span> thousand</div>"
890             },
891             {
892                 "//ldml/numbers/percentFormats[@numberSystem=\"latn\"]/percentFormatLength/percentFormat[@type=\"standard\"]/pattern[@type=\"standard\"]",
893                 "<div class='cldr_example'><span class='cldr_substituted'>5</span>%</div>"
894                         + "<div class='cldr_example'><span class='cldr_substituted'>12,345</span>,<span class='cldr_substituted'>679</span>%</div>"
895                         + "<div class='cldr_example'>-<span class='cldr_substituted'>12,345</span>,<span class='cldr_substituted'>679</span>%</div>"
896             }
897         };
898         final CLDRFile nativeCldrFile = info.getEnglish();
899         ExampleGenerator exampleGenerator =
900                 new ExampleGenerator(info.getEnglish(), info.getEnglish());
901         for (String[] testPair : testPairs) {
902             String xpath = testPair[0];
903             String expected = testPair[1];
904             String value = nativeCldrFile.getStringValue(xpath);
905             String actual = exampleGenerator.getExampleHtml(xpath, value);
906             assertEquals("specifics", expected, actual);
907         }
908     }
909 
showCldrFile(final CLDRFile cldrFile)910     private void showCldrFile(final CLDRFile cldrFile) {
911         ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish());
912         checkPathValue(
913                 exampleGenerator,
914                 "//ldml/dates/calendars/calendar[@type=\"chinese\"]/dateFormats/dateFormatLength[@type=\"full\"]/dateFormat[@type=\"standard\"]/pattern[@type=\"standard\"][@draft=\"unconfirmed\"]",
915                 "EEEE d MMMMl y'x'G",
916                 null);
917 
918         for (String xpath : cldrFile.fullIterable()) {
919             if (xpath.endsWith("/alias")) {
920                 continue;
921             }
922             String value = cldrFile.getStringValue(xpath);
923             checkPathValue(exampleGenerator, xpath, value, null);
924         }
925     }
926 
checkPathValue( ExampleGenerator exampleGenerator, String xpath, String value, String expected)927     private void checkPathValue(
928             ExampleGenerator exampleGenerator, String xpath, String value, String expected) {
929         Set<String> alreadySeen = new HashSet<>();
930         try {
931             String text = exampleGenerator.getExampleHtml(xpath, value);
932             if (text == null) {
933                 // skip
934             } else if (text.contains("Exception")) {
935                 errln("getExampleHtml\t" + text);
936             } else if (!alreadySeen.contains(text)) {
937                 if (text.contains("n/a")) {
938                     if (text.contains("&lt;")) {
939                         errln("Text not quoted correctly:" + "\t" + text + "\t" + xpath);
940                     }
941                 }
942                 boolean skipLog = false;
943                 if (expected != null) {
944                     String simplified = ExampleGenerator.simplify(text, false);
945                     // redo for debugging
946                     text = exampleGenerator.getExampleHtml(xpath, value);
947                     skipLog =
948                             !assertEquals("Example text for «" + value + "»", expected, simplified);
949                 }
950                 if (!skipLog) {
951                     logln("getExampleHtml\t" + text + "\t" + xpath);
952                 }
953                 alreadySeen.add(text);
954             }
955         } catch (Exception e) {
956             errln("getExampleHtml\t" + e.getMessage());
957         }
958 
959         try {
960             String text = exampleGenerator.getHelpHtml(xpath, value);
961             if (text == null) {
962                 // skip
963             } else if (text.contains("Exception")) {
964                 errln("getHelpHtml\t" + text);
965             } else {
966                 logln("getExampleHtml(help)\t" + "\t" + text + "\t" + xpath);
967             }
968         } catch (Exception e) {
969             if (false) {
970                 e.printStackTrace();
971             }
972             errln("getHelpHtml\t" + e.getMessage());
973         }
974     }
975 
TestCompactPlurals()976     public void TestCompactPlurals() {
977         checkCompactExampleFor("de", Count.one, "〖❬1❭ Mio. €〗", "short", "currency", "000000");
978         checkCompactExampleFor("de", Count.other, "〖❬2❭ Mio. €〗", "short", "currency", "000000");
979         checkCompactExampleFor("de", Count.one, "〖❬12❭ Mio. €〗", "short", "currency", "0000000");
980         checkCompactExampleFor("de", Count.other, "〖❬10❭ Mio. €〗", "short", "currency", "0000000");
981 
982         checkCompactExampleFor("cs", Count.many, "〖❬1,1❭ milionu〗", "long", "decimal", "000000");
983         checkCompactExampleFor("pl", Count.other, "〖❬1,1❭ miliona〗", "long", "decimal", "000000");
984     }
985 
checkCompactExampleFor( String localeID, Count many, String expected, String longVsShort, String decimalVsCurrency, String zeros)986     private void checkCompactExampleFor(
987             String localeID,
988             Count many,
989             String expected,
990             String longVsShort,
991             String decimalVsCurrency,
992             String zeros) {
993         CLDRFile cldrFile = info.getCLDRFile(localeID, true);
994         ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish());
995         String path =
996                 "//ldml/numbers/"
997                         + decimalVsCurrency
998                         + "Formats[@numberSystem=\"latn\"]"
999                         + "/"
1000                         + decimalVsCurrency
1001                         + "FormatLength[@type=\""
1002                         + longVsShort
1003                         + "\"]"
1004                         + "/"
1005                         + decimalVsCurrency
1006                         + "Format[@type=\"standard\"]"
1007                         + "/pattern[@type=\"1"
1008                         + zeros
1009                         + "\"][@count=\""
1010                         + many
1011                         + "\"]";
1012         checkPathValue(exampleGenerator, path, cldrFile.getStringValue(path), expected);
1013     }
1014 
1015     // ldml/numbers/currencyFormats[@numberSystem="latn"]/currencyFormatLength[@type="short"]/currencyFormat[@type="standard"]/pattern[@type="1000"][@count="one"]
1016 
TestDayPeriods()1017     public void TestDayPeriods() {
1018         // checkDayPeriod("da", "format", "morning1", "〖05:00 – 10:00〗〖❬7:30❭ morgens〗");
1019         checkDayPeriod("zh", "format", "morning1", "〖05:00 – 08:00⁻〗〖清晨❬6:30❭〗");
1020 
1021         checkDayPeriod("de", "format", "morning1", "〖05:00 – 10:00⁻〗〖❬7:30 ❭morgens〗");
1022         checkDayPeriod("de", "stand-alone", "morning1", "〖05:00 – 10:00⁻〗");
1023         checkDayPeriod("de", "format", "morning2", "〖10:00 – 12:00⁻〗〖❬11:00 ❭vormittags〗");
1024         checkDayPeriod("de", "stand-alone", "morning2", "〖10:00 – 12:00⁻〗");
1025         checkDayPeriod("pl", "format", "morning1", "〖06:00 – 10:00⁻〗〖❬8:00 ❭rano〗");
1026         checkDayPeriod("pl", "stand-alone", "morning1", "〖06:00 – 10:00⁻〗");
1027 
1028         checkDayPeriod(
1029                 "en", "format", "night1", "〖00:00 – 06:00⁻; 21:00 – 24:00⁻〗〖❬3:00 ❭at night〗");
1030         checkDayPeriod("en", "stand-alone", "night1", "〖00:00 – 06:00⁻; 21:00 – 24:00⁻〗");
1031 
1032         checkDayPeriod("en", "format", "noon", "〖12:00〗〖❬12:00 ❭noon〗");
1033         checkDayPeriod("en", "format", "midnight", "〖00:00〗〖❬12:00 ❭midnight〗");
1034         checkDayPeriod("en", "format", "am", "〖00:00 – 12:00⁻〗〖❬6:00 ❭AM〗");
1035         checkDayPeriod("en", "format", "pm", "〖12:00 – 24:00⁻〗〖❬6:00 ❭PM〗");
1036     }
1037 
checkDayPeriod( String localeId, String type, String dayPeriodCode, String expected)1038     private void checkDayPeriod(
1039             String localeId, String type, String dayPeriodCode, String expected) {
1040         CLDRFile cldrFile = info.getCLDRFile(localeId, true);
1041         ExampleGenerator exampleGenerator = new ExampleGenerator(cldrFile, info.getEnglish());
1042         String prefix =
1043                 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/dayPeriodContext[@type=\"";
1044         String suffix =
1045                 "\"]/dayPeriodWidth[@type=\"wide\"]/dayPeriod[@type=\"" + dayPeriodCode + "\"]";
1046         String path = prefix + type + suffix;
1047         checkPathValue(exampleGenerator, path, cldrFile.getStringValue(path), expected);
1048     }
1049 
1050     /**
1051      * Test that getExampleHtml returns same output for same input regardless of order in which it
1052      * is called with different inputs.
1053      *
1054      * <p>Calling getExampleHtml with a particular path and value presumably should NOT depend on
1055      * the history of paths and/or values it has been called with previously.
1056      *
1057      * <p>We formerly got different examples for SPECIAL_PATH depending on whether an example was
1058      * first gotten for USE_EVIL_PATH.
1059      *
1060      * <p>Without EVIL_PATH, got right value for SPECIAL_PATH: <div class='cldr_example'><span
1061      * class='cldr_substituted'>123 456,79 </span>€</div>
1062      *
1063      * <p>With EVIL_PATH, got wrong value for SPECIAL_PATH: <div class='cldr_example'><span
1064      * class='cldr_substituted'>123457 k </span>€</div>
1065      *
1066      * <p>This was fixed by doing clone() before returning a DecimalFormat in ICUServiceBuilder.
1067      * Reference: https://unicode-org.atlassian.net/browse/CLDR-13375.
1068      *
1069      * <p>Subsequently, DAIP changed to normalize NNBSP to NBSP for some paths, so this test was
1070      * revised not to depend on that distinction, only to expect an example containing "456,79" as a
1071      * substring (DAIP has its own unit tests). Ideally this test might be improved so as not to
1072      * depend on actual values at all, but would call getExampleHtml repeatedly for the same set of
1073      * paths but in different orders and confirm the example is the same regardless of the order;
1074      * that would require disabling the cache.
1075      *
1076      * @throws IOException
1077      */
TestExampleGeneratorConsistency()1078     public void TestExampleGeneratorConsistency() throws IOException {
1079         final String EVIL_PATH =
1080                 "//ldml/numbers/currencyFormats/currencyFormatLength[@type=\"short\"]/currencyFormat[@type=\"standard\"]/pattern[@type=\"10000\"][@count=\"one\"]";
1081         final String SPECIAL_PATH = "//ldml/numbers/currencies/currency[@type=\"EUR\"]/symbol";
1082         final String EXPECTED_TO_CONTAIN = "456,79";
1083 
1084         final CLDRFile cldrFile = info.getCLDRFile("fr", true);
1085         final ExampleGenerator eg = new ExampleGenerator(cldrFile, info.getEnglish());
1086 
1087         final String evilValue = cldrFile.getStringValue(EVIL_PATH);
1088         final String specialValue = cldrFile.getStringValue(SPECIAL_PATH);
1089 
1090         eg.getExampleHtml(EVIL_PATH, evilValue);
1091         final String specialExample = eg.getExampleHtml(SPECIAL_PATH, specialValue);
1092         if (!specialExample.contains(EXPECTED_TO_CONTAIN)) {
1093             errln("Expected example to contain " + EXPECTED_TO_CONTAIN + "; got " + specialExample);
1094         }
1095     }
1096 
TestInflectedUnitExamples()1097     public void TestInflectedUnitExamples() {
1098         String[][] deTests = {
1099             {
1100                 "one",
1101                 "accusative",
1102                 "〖❬1❭ Tag〗〖❬… für 1❭ Tag❬ …❭〗〖❌  ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1103             },
1104             {
1105                 "one",
1106                 "dative",
1107                 "〖❬1❭ Tag〗〖❬… mit 1❭ Tag❬ …❭〗〖❌  ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1108             },
1109             {
1110                 "one",
1111                 "genitive",
1112                 "〖❬1❭ Tages〗〖❬Anstatt 1❭ Tages❬ …❭〗〖❌  ❬… für 1❭ Tages❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1113             },
1114             {
1115                 "one",
1116                 "nominative",
1117                 "〖❬1❭ Tag〗〖❬1❭ Tag❬ kostet (kosten) € 3,50.❭〗〖❌  ❬Anstatt 1❭ Tag❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1118             },
1119             {
1120                 "other",
1121                 "accusative",
1122                 "〖❬1,5❭ Tage〗〖❬… für 1,5❭ Tage❬ …❭〗〖❌  ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1123             },
1124             {
1125                 "other",
1126                 "dative",
1127                 "〖❬1,5❭ Tagen〗〖❬… mit 1,5❭ Tagen❬ …❭〗〖❌  ❬… für 1,5❭ Tagen❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1128             },
1129             {
1130                 "other",
1131                 "genitive",
1132                 "〖❬1,5❭ Tage〗〖❬Anstatt 1,5❭ Tage❬ …❭〗〖❌  ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1133             },
1134             {
1135                 "other",
1136                 "nominative",
1137                 "〖❬1,5❭ Tage〗〖❬1,5❭ Tage❬ kostet (kosten) € 3,50.❭〗〖❌  ❬… mit 1,5❭ Tage❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1138             },
1139         };
1140         checkInflectedUnitExamples("de", deTests);
1141         String[][] elTests = {
1142             {
1143                 "one",
1144                 "accusative",
1145                 "〖❬1❭ ημέρα〗〖❬… ανά 1❭ ημέρα❬ …❭〗〖❌  ❬… αξίας 1❭ ημέρα❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1146             },
1147             {
1148                 "one",
1149                 "genitive",
1150                 "〖❬1❭ ημέρας〗〖❬… αξίας 1❭ ημέρας❬ …❭〗〖❌  ❬… ανά 1❭ ημέρας❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1151             },
1152             {
1153                 "one",
1154                 "nominative",
1155                 "〖❬1❭ ημέρα〗〖❬Η απόσταση είναι 1❭ ημέρα❬ …❭〗〖❌  ❬… αξίας 1❭ ημέρα❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1156             },
1157             {
1158                 "other",
1159                 "accusative",
1160                 "〖❬0,9❭ ημέρες〗〖❬… ανά 0,9❭ ημέρες❬ …❭〗〖❌  ❬… αξίας 0,9❭ ημέρες❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1161             },
1162             {
1163                 "other",
1164                 "genitive",
1165                 "〖❬0,9❭ ημερών〗〖❬… αξίας 0,9❭ ημερών❬ …❭〗〖❌  ❬… ανά 0,9❭ ημερών❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1166             },
1167             {
1168                 "other",
1169                 "nominative",
1170                 "〖❬0,9❭ ημέρες〗〖❬Η απόσταση είναι 0,9❭ ημέρες❬ …❭〗〖❌  ❬… αξίας 0,9❭ ημέρες❬ …❭〗〖〗〖1 day ≡ 24 hour〗〖1 day ≡ 1/7 week〗"
1171             },
1172         };
1173         checkInflectedUnitExamples("el", elTests);
1174     }
1175 
checkInflectedUnitExamples(final String locale, String[][] tests)1176     private void checkInflectedUnitExamples(final String locale, String[][] tests) {
1177         final CLDRFile cldrFile = info.getCLDRFile(locale, true);
1178         ExampleGenerator exampleGenerator = getExampleGenerator(locale);
1179         String pattern =
1180                 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"duration-day\"]/unitPattern[@count=\"COUNT\"][@case=\"CASE\"]";
1181         boolean showWorkingExamples = false;
1182         for (String[] row : tests) {
1183             String path = pattern.replace("COUNT", row[0]).replace("CASE", row[1]);
1184             String expected = row[2];
1185             String value = cldrFile.getStringValue(path);
1186             String actualRaw = exampleGenerator.getExampleHtml(path, value);
1187             String actual = ExampleGenerator.simplify(actualRaw, false);
1188             showWorkingExamples |= !assertEquals(row[0] + ", " + row[1], expected, actual);
1189         }
1190 
1191         // If a test fails, verbose will regenerate what the code thinks they should be.
1192         // Review for correctness, and then replace the test cases
1193 
1194         if (showWorkingExamples) {
1195             System.out.println(
1196                     "## The following would satisfy the test, but check to make sure the expected values are all correct!");
1197             PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, locale);
1198             GrammarInfo grammarInfo = SDI.getGrammarInfo(locale);
1199             final Collection<String> grammaticalValues2 =
1200                     grammarInfo.get(
1201                             GrammaticalTarget.nominal,
1202                             GrammaticalFeature.grammaticalCase,
1203                             GrammaticalScope.units);
1204 
1205             for (Count plural : pluralInfo.getCounts()) {
1206                 for (String grammaticalCase : grammaticalValues2) {
1207                     String path =
1208                             pattern.replace("COUNT", plural.toString())
1209                                     .replace("CASE", grammaticalCase);
1210                     String value = cldrFile.getStringValue(path);
1211                     String actualRaw = exampleGenerator.getExampleHtml(path, value);
1212                     String actual = ExampleGenerator.simplify(actualRaw, false);
1213                     System.out.println(
1214                             "{\""
1215                                     + plural
1216                                     + "\", "
1217                                     + "\""
1218                                     + grammaticalCase
1219                                     + "\", "
1220                                     + "\""
1221                                     + actual
1222                                     + "\"},");
1223                 }
1224             }
1225         }
1226     }
1227 
TestMinimalPairExamples()1228     public void TestMinimalPairExamples() {
1229         String[][] tests = {
1230             {
1231                 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"one\"]",
1232                 "〖❬1❭ Tag〗〖❌  ❬2❭ Tag〗"
1233             },
1234             {
1235                 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"other\"]",
1236                 "〖❬2❭ Tage〗〖❌  ❬1❭ Tage〗"
1237             },
1238             {
1239                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"accusative\"]",
1240                 "〖… für ❬1 metrische Pint❭ …〗〖❌  … für ❬1 metrischen Pint❭ …〗"
1241             },
1242             {
1243                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"dative\"]",
1244                 "〖… mit ❬1 metrischen Pint❭ …〗〖❌  … mit ❬1 metrische Pint❭ …〗"
1245             },
1246             {
1247                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"genitive\"]",
1248                 "〖Anstatt ❬1 metrischen Pints❭ …〗〖❌  Anstatt ❬1 metrische Pint❭ …〗"
1249             },
1250             {
1251                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"nominative\"]",
1252                 "〖❬2 metrische Pints❭ kostet (kosten) € 3,50.〗〖❌  ❬1 metrische Pint❭ kostet (kosten) € 3,50.〗"
1253             },
1254             {
1255                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"feminine\"]",
1256                 "〖Die ❬Stunde❭ ist …〗〖❌  Die ❬Tag❭ ist …〗"
1257             },
1258             {
1259                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"masculine\"]",
1260                 "〖Der ❬Tag❭ ist …〗〖❌  Der ❬Stunde❭ ist …〗"
1261             },
1262             {
1263                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"neuter\"]",
1264                 "〖Das ❬Jahr❭ ist …〗〖❌  Das ❬Stunde❭ ist …〗"
1265             },
1266         };
1267         checkMinimalPairExamples("de", tests);
1268 
1269         String[][] elTests = {
1270             {
1271                 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"one\"]",
1272                 "〖❬1❭ ημέρα〗〖❌  ❬2❭ ημέρα〗"
1273             },
1274             {
1275                 "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"other\"]",
1276                 "〖❬2❭ ημέρες〗〖❌  ❬1❭ ημέρες〗"
1277             },
1278             {
1279                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"accusative\"]",
1280                 "〖… ανά ❬1 τόνο❭ …〗〖❌  … ανά ❬1 τόνου❭ …〗"
1281             },
1282             {
1283                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"genitive\"]",
1284                 "〖… αξίας ❬1 τόνου❭ …〗〖❌  … αξίας ❬1 τόνο❭ …〗"
1285             },
1286             {
1287                 "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"nominative\"]",
1288                 "〖Η απόσταση είναι ❬2 τόνοι❭ …〗〖❌  Η απόσταση είναι ❬1 τόνο❭ …〗"
1289             },
1290             {
1291                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"feminine\"]",
1292                 "〖Η ❬ημέρα❭ είναι〗〖❌  Η ❬μήνας❭ είναι〗"
1293             },
1294             {
1295                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"masculine\"]",
1296                 "〖Ο ❬μήνας❭ θα είναι〗〖❌  Ο ❬ημέρα❭ θα είναι〗"
1297             },
1298             {
1299                 "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"neuter\"]",
1300                 "〖Το ❬λεπτό❭ ήταν〗〖❌  Το ❬ημέρα❭ ήταν〗"
1301             },
1302         };
1303         checkMinimalPairExamples("el", elTests);
1304     }
1305 
checkMinimalPairExamples(final String locale, String[][] tests)1306     private void checkMinimalPairExamples(final String locale, String[][] tests) {
1307         final CLDRFile cldrFile = info.getCLDRFile(locale, true);
1308         ExampleGenerator exampleGenerator = getExampleGenerator(locale);
1309         boolean showWorkingExamples = false;
1310         for (String[] row : tests) {
1311             String path = row[0];
1312             String expected = row[1];
1313             String value = cldrFile.getStringValue(path);
1314             String actualRaw = exampleGenerator.getExampleHtml(path, value);
1315             String actual = ExampleGenerator.simplify(actualRaw, false);
1316             showWorkingExamples |= !assertEquals(row[0] + ", " + row[1], expected, actual);
1317         }
1318 
1319         // If a test fails, verbose will regenerate what the code thinks they should be.
1320         // Review for correctness, and then replace the test cases
1321 
1322         if (showWorkingExamples) {
1323             System.out.println(
1324                     "## The following would satisfy the test, but check to make sure the expected values are all correct!");
1325             PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, locale);
1326             ArrayList<String> paths = new ArrayList<>();
1327 
1328             for (Count plural : pluralInfo.getCounts()) {
1329                 paths.add(
1330                         "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\""
1331                                 + plural
1332                                 + "\"]");
1333             }
1334             GrammarInfo grammarInfo = SDI.getGrammarInfo(locale);
1335             for (String grammaticalValues :
1336                     grammarInfo.get(
1337                             GrammaticalTarget.nominal,
1338                             GrammaticalFeature.grammaticalCase,
1339                             GrammaticalScope.units)) {
1340                 paths.add(
1341                         "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\""
1342                                 + grammaticalValues
1343                                 + "\"]");
1344             }
1345             for (String grammaticalValues :
1346                     grammarInfo.get(
1347                             GrammaticalTarget.nominal,
1348                             GrammaticalFeature.grammaticalGender,
1349                             GrammaticalScope.units)) {
1350                 paths.add(
1351                         "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\""
1352                                 + grammaticalValues
1353                                 + "\"]");
1354             }
1355             for (String path : paths) {
1356                 String value = cldrFile.getStringValue(path);
1357                 String actualRaw = exampleGenerator.getExampleHtml(path, value);
1358                 String actual = ExampleGenerator.simplify(actualRaw, false);
1359                 System.out.println("{\"" + path.replace("\"", "\\\"") + "\", \"" + actual + "\"},");
1360             }
1361         }
1362     }
1363 
1364     /**
1365      * Test the production of minimal pair examples, to make sure we get no exceptions. If -v, then
1366      * generates lines for spreadsheet survey
1367      */
TestListMinimalPairExamples()1368     public void TestListMinimalPairExamples() {
1369         Set<String> localesWithGrammar = SDI.hasGrammarInfo();
1370         if (isVerbose()) {
1371             System.out.println(
1372                     "\nLC\tLocale\tType\tCode\tCurrent Pattern\tVerify this is correct!\tVerify this is wrong!");
1373         }
1374         final String unused = "∅";
1375         List<String> pluralSheet = new ArrayList();
1376         for (String locale : localesWithGrammar) {
1377             final CLDRFile cldrFile = info.getCLDRFile(locale, true);
1378             ExampleGenerator exampleGenerator = getExampleGenerator(locale);
1379 
1380             PluralInfo pluralInfo = SDI.getPlurals(PluralType.cardinal, cldrFile.getLocaleID());
1381             Map<String, Pair<String, String>> paths = new LinkedHashMap<>();
1382 
1383             Set<Count> counts = pluralInfo.getCounts();
1384             if (counts.size() > 1) {
1385                 for (Count plural : counts) {
1386                     paths.put(
1387                             "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\""
1388                                     + plural
1389                                     + "\"]",
1390                             Pair.of("plural", plural.toString()));
1391                 }
1392             }
1393             GrammarInfo grammarInfo = SDI.getGrammarInfo(locale);
1394             Collection<String> unitCases =
1395                     grammarInfo.get(
1396                             GrammaticalTarget.nominal,
1397                             GrammaticalFeature.grammaticalCase,
1398                             GrammaticalScope.units);
1399             Collection<String> generalCasesRaw =
1400                     grammarInfo.get(
1401                             GrammaticalTarget.nominal,
1402                             GrammaticalFeature.grammaticalCase,
1403                             GrammaticalScope.general);
1404             Collection<CaseValues> generalCases =
1405                     generalCasesRaw.stream()
1406                             .map(x -> CaseValues.valueOf(x))
1407                             .collect(Collectors.toCollection(TreeSet::new));
1408             for (CaseValues unitCase0 : generalCases) {
1409                 String unitCase = unitCase0.toString();
1410                 paths.put(
1411                         (unitCases.contains(unitCase) ? "" : unused)
1412                                 + "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\""
1413                                 + unitCase
1414                                 + "\"]",
1415                         Pair.of("case", unitCase));
1416             }
1417             Collection<String> unitGenders =
1418                     grammarInfo.get(
1419                             GrammaticalTarget.nominal,
1420                             GrammaticalFeature.grammaticalGender,
1421                             GrammaticalScope.units);
1422             Collection<String> generalGenders =
1423                     grammarInfo.get(
1424                             GrammaticalTarget.nominal,
1425                             GrammaticalFeature.grammaticalGender,
1426                             GrammaticalScope.general);
1427             for (String unitGender : generalGenders) {
1428                 paths.put(
1429                         (unitGenders.contains(unitGender) ? "" : unused)
1430                                 + "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\""
1431                                 + unitGender
1432                                 + "\"]",
1433                         Pair.of("gender", unitGender));
1434             }
1435             String localeName = CLDRConfig.getInstance().getEnglish().getName(locale);
1436             boolean pluralOnly = true;
1437             if (paths.isEmpty()) {
1438                 pluralSheet.add(
1439                         locale + "\t" + localeName + "\t" + "N/A" + "\t" + "N/A" + "\t" + "N/A");
1440             } else {
1441                 for (Entry<String, Pair<String, String>> pathAndLabel : paths.entrySet()) {
1442                     String path = pathAndLabel.getKey();
1443                     String label = pathAndLabel.getValue().getFirst();
1444                     String code = pathAndLabel.getValue().getSecond();
1445                     if (!label.equals("plural")) {
1446                         pluralOnly = false;
1447                     }
1448                 }
1449                 String lastLabel = "";
1450                 for (Entry<String, Pair<String, String>> pathAndLabel : paths.entrySet()) {
1451                     String path = pathAndLabel.getKey();
1452                     String label = pathAndLabel.getValue().getFirst();
1453                     String code = pathAndLabel.getValue().getSecond();
1454                     String pattern = "";
1455                     String examples = "";
1456                     if (!label.equals(lastLabel)) {
1457                         lastLabel = label;
1458                         if (!pluralOnly) {
1459                             if (isVerbose()) {
1460                                 System.out.println();
1461                             }
1462                         }
1463                     }
1464                     if (path.startsWith(unused)) {
1465                         pattern = "��  Not used with formatted units";
1466                     } else {
1467                         pattern = cldrFile.getStringValue(path);
1468                         if (pattern == null) {
1469                             warnln(
1470                                     "Missing ExampleGenerator html example for "
1471                                             + locale
1472                                             + "("
1473                                             + localeName
1474                                             + "): "
1475                                             + path);
1476                             continue;
1477                         }
1478                         String actualRaw = exampleGenerator.getExampleHtml(path, pattern);
1479                         String actualSimplified = ExampleGenerator.simplify(actualRaw, false);
1480                         examples =
1481                                 actualSimplified
1482                                         .replace("〗〖", "\t")
1483                                         .replace("〗", "")
1484                                         .replace("〖", "");
1485                         List<String> exampleList =
1486                                 com.google.common.base.Splitter.on('\t')
1487                                         .trimResults()
1488                                         .splitToList(examples);
1489                         final int exampleListSize = exampleList.size();
1490                         switch (exampleListSize) {
1491                             case 2: // ok
1492                                 break;
1493                             case 1:
1494                                 warnln(
1495                                         "Expecting exactly 2 examples: "
1496                                                 + exampleList
1497                                                 + ", but got "
1498                                                 + exampleListSize);
1499                                 break;
1500                             default:
1501                                 errln(
1502                                         "Expecting exactly 2 examples: "
1503                                                 + exampleList
1504                                                 + ", but got "
1505                                                 + exampleListSize);
1506                                 break;
1507                         }
1508                         StringBuilder exampleBuffer = new StringBuilder();
1509                         for (String exampleItem : exampleList) {
1510                             if (exampleItem.contains("❬null❭") || exampleItem.contains("❬n/a❭")) {
1511                                 boolean bad = (exampleItem.contains("❌"));
1512                                 exampleItem = "��  No unit available";
1513                                 if (bad) {
1514                                     exampleItem = "❌  " + exampleItem;
1515                                 }
1516                             }
1517                             if (exampleBuffer.length() != 0) {
1518                                 exampleBuffer.append('\t');
1519                             }
1520                             exampleBuffer.append(exampleItem);
1521                         }
1522                         examples = exampleBuffer.toString();
1523                     }
1524                     String line =
1525                             (locale
1526                                     + "\t"
1527                                     + localeName
1528                                     + "\t"
1529                                     + label
1530                                     + "\t"
1531                                     + code
1532                                     + "\t"
1533                                     + pattern
1534                                     + "\t"
1535                                     + examples);
1536                     if (pluralOnly) {
1537                         pluralSheet.add(line);
1538                     } else {
1539                         if (isVerbose()) {
1540                             System.out.println(line);
1541                         }
1542                     }
1543                 }
1544             }
1545             if (pluralOnly) {
1546                 pluralSheet.add("");
1547             } else if (isVerbose()) {
1548                 System.out.println();
1549             }
1550         }
1551         if (isVerbose()) {
1552             System.out.println("#################### Plural Only ###################");
1553             for (String line : pluralSheet) {
1554                 System.out.println(line);
1555             }
1556         }
1557     }
1558 
TestUnicodeSetExamples()1559     public void TestUnicodeSetExamples() {
1560         String[][] tests = {
1561             {
1562                 "hi",
1563                 "//ldml/characters/exemplarCharacters[@type=\"auxiliary\"]",
1564                 "[ॄ‌‍]",
1565                 "〖‎��️ ॑ ॒ ॠ ॡ ॻ ॼ ॾ ॿ ऱ ॢ ॣ〗〖❰ZWNJ❱ ≡ cursive non-joiner〗〖❰ZWJ❱ ≡ cursive joiner〗〖❬internal: ❭[ॄ‌‍]〗"
1566             },
1567             {
1568                 "hu",
1569                 "//ldml/characters/exemplarCharacters[@type=\"auxiliary\"]",
1570                 "[qw-yàâ-èê-ìîïñòôøùûÿāăēĕīĭōŏœūŭ]",
1571                 "〖‎��️ · ắ ằ ẵ ẳ ấ ầ ẫ ẩ ǎ a̧ ą ą́ a᷆ a᷇ ả ạ ặ ậ a̱ aː áː àː ɓ ć ĉ č ċ ď ḑ đ ḍ ḓ ð ɖ ɗ ế ề ễ ể ě ẽ ė ę ę́ e᷆ e᷇ ẻ ẹ ẹ́ ẹ̀ ệ e̱ eː éː èː ǝ ǝ́ ǝ̀ ǝ̂ ǝ̌ ǝ̄ ə ə́ ə̀ ə̂ ə̌ ə̄ ɛ ɛ́ ɛ̀ ɛ̂ ɛ̌ ɛ̈ ɛ̃ ɛ̧ ɛ̄ ɛ᷆ ɛ᷇ ɛ̱ ɛ̱̈ ƒ ğ ĝ ǧ g̃ ġ ģ g̱ gʷ ǥ ɣ ĥ ȟ ħ ḥ ʻ ǐ ĩ İ i̧ į į́ i᷆ i᷇ ỉ ị i̱ iː íː ìː íj́ ı ɨ ɨ́ ɨ̀ ɨ̂ ɨ̌ ɨ̄ ɩ ɩ́ ɩ̀ ɩ̂ ĵ ǩ ķ ḵ kʷ ƙ ĺ ľ ļ ł ḷ ḽ ḻ ḿ m̀ m̄ ń ǹ ň ṅ ņ n̄ ṇ ṋ ṉ ɲ ŋ ŋ́ ŋ̀ ŋ̄ ố ồ ỗ ổ ǒ õ ǫ ǫ́ o᷆ o᷇ ỏ ơ ớ ờ ỡ ở ợ ọ ọ́ ọ̀ ộ o̱ oː óː òː ɔ ɔ́ ɔ̀ ɔ̂ ɔ̌ ɔ̈ ɔ̃ ɔ̧ ɔ̄ ɔ᷆ ɔ᷇ ɔ̱ ŕ ř ŗ ṛ ś ŝ š ş ṣ ș ß ť ṭ ț ṱ ṯ ŧ ǔ ů ũ u̧ ų u᷆ u᷇ ủ ư ứ ừ ữ ử ự ụ uː úː ùː ʉ ʉ́ ʉ̀ ʉ̂ ʉ̌ ʉ̈ ʉ̄ ʊ ʊ́ ʊ̀ ʊ̂ ṽ ʋ ẃ ẁ ŵ ẅ ý ỳ ŷ ỹ ỷ ỵ y̱ ƴ ź ž ż ẓ ʒ ǯ þ ʔ ˀ ʼ ꞌ ǀ ǁ ǂ ǃ〗〖❬internal: ❭[qw-yàâ-èê-ìîïñòôøùûÿāăēĕīĭōŏœūŭ]〗"
1572             },
1573             {
1574                 "de",
1575                 "//ldml/characters/parseLenients[@scope=\"date\"][@level=\"lenient\"]/parseLenient[@sample=\"-\"]",
1576                 "[\\u200B \\- . ๎ ็]",
1577                 "〖‎➕ ❰WNJ❱ ๎ ็〗〖‎➖ ‑ /〗〖❰WNJ❱ ≡ allow line wrap after, aka ZWSP〗〖❬internal: ❭[\\-.็๎​]〗"
1578             },
1579             {
1580                 "de",
1581                 "//ldml/characters/exemplarCharacters",
1582                 "[\\u200B a-z ๎ ็]",
1583                 "〖‎➕ ❰WNJ❱ ๎ ็〗〖‎➖ ä ö ß ü〗〖❰WNJ❱ ≡ allow line wrap after, aka ZWSP〗〖❬internal: ❭[a-z็๎​]〗"
1584             },
1585             {"de", "//ldml/characters/exemplarCharacters", "a-z ❰ZWSP❱", null},
1586         };
1587 
1588         for (String[] test : tests) {
1589             final String locale = test[0];
1590             final String path = test[1];
1591             final String value = test[2];
1592             final String expected = test[3];
1593             ExampleGenerator exampleGenerator = getExampleGenerator(locale);
1594             String actual =
1595                     ExampleGenerator.simplify( //
1596                             exampleGenerator.getExampleHtml(path, value));
1597             assertEquals(locale + path + "=" + value, expected, actual);
1598         }
1599     }
1600 }
1601