1 package org.unicode.cldr.tool;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.Comparator;
6 import java.util.EnumMap;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.LinkedHashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Objects;
13 import java.util.Set;
14 import java.util.TreeMap;
15 import java.util.TreeSet;
16
17 import org.unicode.cldr.draft.FileUtilities;
18 import org.unicode.cldr.util.Builder;
19 import org.unicode.cldr.util.Builder.CBuilder;
20 import org.unicode.cldr.util.CLDRConfig;
21 import org.unicode.cldr.util.CLDRFile;
22 import org.unicode.cldr.util.CLDRFile.Status;
23 import org.unicode.cldr.util.CLDRPaths;
24 import org.unicode.cldr.util.Counter;
25 import org.unicode.cldr.util.CoverageInfo;
26 import org.unicode.cldr.util.DtdType;
27 import org.unicode.cldr.util.Factory;
28 import org.unicode.cldr.util.LanguageTagParser;
29 import org.unicode.cldr.util.Level;
30 import org.unicode.cldr.util.SupplementalDataInfo;
31 import org.unicode.cldr.util.SupplementalDataInfo.PluralType;
32 import org.unicode.cldr.util.XPathParts;
33
34 import com.ibm.icu.impl.Relation;
35 import com.ibm.icu.impl.Row;
36 import com.ibm.icu.impl.Row.R2;
37 import com.ibm.icu.impl.Row.R3;
38 import com.ibm.icu.impl.Row.R5;
39 import com.ibm.icu.text.NumberFormat;
40 import com.ibm.icu.text.Transform;
41
Pseudolocalizer()42 public class GenerateCoverageLevels {
43 // see ShowLocaleCoverage.java
44 private static boolean SKIP_UNCONFIRMED = true;
45 private static int SHOW_EXAMPLES = 5;
46 private static final String FILES = ".*";
47 private static final String MAIN_DIRECTORY = CLDRPaths.MAIN_DIRECTORY;// CldrUtility.SUPPLEMENTAL_DIRECTORY;
48 // //CldrUtility.MAIN_DIRECTORY;
49 private static final String COLLATION_DIRECTORY = CLDRPaths.COMMON_DIRECTORY + "/collation/";// CldrUtility.SUPPLEMENTAL_DIRECTORY;
50 // //CldrUtility.MAIN_DIRECTORY;
51 private static final String RBNF_DIRECTORY = CLDRPaths.COMMON_DIRECTORY + "/rbnf/";// CldrUtility.SUPPLEMENTAL_DIRECTORY;
52 // //CldrUtility.MAIN_DIRECTORY;
53 private static final String OUT_DIRECTORY = CLDRPaths.GEN_DIRECTORY + "/coverage/"; // CldrUtility.MAIN_DIRECTORY;
54 private static final Factory cldrFactory = Factory.make(MAIN_DIRECTORY, FILES);
55 private static final Comparator<String> attributeComparator = CLDRFile.getAttributeOrdering();
56 private static final CLDRFile english = cldrFactory.make("en", true);
57 private static SupplementalDataInfo supplementalData = CLDRConfig.getInstance().getSupplementalDataInfo();
58 // SupplementalDataInfo.getInstance(english.getSupplementalDirectory());
59 private static Set<String> defaultContents = supplementalData.getDefaultContentLocales();
60 private static Map<String, R2<List<String>, String>> languageAliasInfo = supplementalData.getLocaleAliasInfo().get(
61 "language");
62 private static LocaleFilter localeFilter = new LocaleFilter(true);
63 private static BooleanLocaleFilter nonAliasLocaleFilter = new BooleanLocaleFilter();
64
65 private static final long COLLATION_WEIGHT = 50;
66 private static final Level COLLATION_LEVEL = Level.POSIX;
67 private static final long PLURALS_WEIGHT = 20;
68 private static final Level PLURALS_LEVEL = Level.MINIMAL;
69 private static final long RBNF_WEIGHT = 20;
70 private static final Level RBNF_LEVEL = Level.MODERATE;
71
72 static int totalCount = 0;
73
74 enum Inheritance {
75 actual, inherited
76 }
77
78 public static void main(String[] args) throws IOException {
79 if (true) {
80 throw new IllegalArgumentException("See ShowLocaleCoverage (TODO: merge these).");
81 }
82 PrintWriter out = FileUtilities.openUTF8Writer(OUT_DIRECTORY, "fullpaths.txt");
83 showEnglish(out);
84 out.close();
85
86 System.out.println("*** TODO check collations, RBNF, Transforms (if non-Latin)");
87 PrintWriter summary = FileUtilities.openUTF8Writer(OUT_DIRECTORY, "summary.txt");
88 PrintWriter samples = FileUtilities.openUTF8Writer(OUT_DIRECTORY, "samples.txt");
89 PrintWriter counts = FileUtilities.openUTF8Writer(OUT_DIRECTORY, "counts.txt");
90 summarizeCoverage(summary, samples, counts);
91 summary.close();
92 samples.close();
93 counts.close();
94 }
95
96 private static void showEnglish(PrintWriter out) throws IOException {
97 CLDRFile cldrFile = english;
98 String locale = "en";
99 Set<String> sorted = Builder.with(new TreeSet<String>()).addAll(cldrFile.iterator())
100 .addAll(cldrFile.getExtraPaths()).get();
101 Set<R3<Level, String, Inheritance>> items = new TreeSet<>(new RowComparator());
102 for (String path : sorted) {
103 if (path.endsWith("/alias")) {
104 continue;
105 }
106 String source = cldrFile.getSourceLocaleID(path, null);
107 Inheritance inherited = !source.equals(locale) ? Inheritance.inherited : Inheritance.actual;
108
109 // Level level = supplementalData.getCoverageLevel(path, locale);
110 Level level = CLDRConfig.getInstance().getCoverageInfo().getCoverageLevel(path, locale);
111
112 items.add(Row.of(level, path, inherited));
113 }
114
115 PathStore store = new PathStore();
116 for (R3<Level, String, Inheritance> item : items) {
117 show(out, item, store);
118 }
119 show(out, null, store);
120 }
121
122 private static class RowComparator implements Comparator<R3<Level, String, Inheritance>> {
123
124 @Override
125 public int compare(R3<Level, String, Inheritance> o1, R3<Level, String, Inheritance> o2) {
126 int result = o1.get0().compareTo(o2.get0());
127 if (result != 0) return result;
128 result = CLDRFile.getComparator(DtdType.ldml).compare(o1.get1(), o2.get1());
129 if (result != 0) return result;
130 result = o1.get2().compareTo(o2.get2());
131 return result;
132 }
133 }
134
135 private static void show(PrintWriter out, R3<Level, String, Inheritance> next, PathStore store) {
136 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> results = store.add(next);
137 if (results != null) {
138 Level lastLevel = results.get0();
139 int count = results.get2();
140 String path = results.get3();
141 totalCount += count;
142 try {
143 StringBuilder resultString = new StringBuilder();
144 TreeMap<String, Relation<String, String>> types = results.get4();
145 for (String key : types.keySet()) {
146 Relation<String, String> attr_values = types.get(key);
147 for (String attr : attr_values.keySet()) {
148 resultString.append("\t").append(key + ":\u200b" + attr).append("=\u200b")
149 .append(attr_values.getAll(attr));
150 }
151 }
152 out.println(lastLevel.ordinal()
153 + "\t" + lastLevel
154 + "\t" + count
155 + "\t" + totalCount
156 + "\t" + path + resultString);
157 } catch (RuntimeException e) {
158 throw e;
159 }
160 }
161 }
162
163 static class PathStore {
164 XPathParts lastParts = new XPathParts();
165 XPathParts nextParts = new XPathParts();
166 Level lastLevel;
167 Inheritance lastInheritance;
168 int count = 0;
169
170 TreeMap<String, Relation<String, String>> differences = new TreeMap<>();
171
172 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> add(
173 R3<Level, String, Inheritance> next) {
174 count++;
175 boolean wasNull = lastLevel == null;
176 Level level = null;
177 String path = null;
178 Inheritance inherited = null;
179
180 if (next != null) {
181 level = next.get0();
182 path = next.get1();
183 inherited = next.get2();
184
185 nextParts = setNewParts(path);
186 if (sameElements()) {
187 addDifferences();
188 return null;
189 }
190 }
191 // clear the values
192 clean(differences);
193 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> results = Row.of(
194 lastLevel, lastInheritance, count - 1, lastParts.toString().replace("/", "\u200B/"), differences);
195 lastParts = nextParts;
196 differences = new TreeMap<>();
197 nextParts = new XPathParts();
198 lastLevel = level;
199 lastInheritance = inherited;
200 count = 1;
201 if (wasNull) return null;
202 return results;
203 }
204
205 private void clean(TreeMap<String, Relation<String, String>> differences2) {
206 for (int i = 0; i < lastParts.size(); ++i) {
207 String element = lastParts.getElement(i);
208 Relation<String, String> attr_values = differences2.get(element);
209 if (attr_values == null) continue;
210 for (String attr : attr_values.keySet()) {
211 lastParts.putAttributeValue(i, attr, "*");
212 }
213 }
214 }
215
216 private XPathParts setNewParts(String path) {
217 XPathParts parts = XPathParts.getFrozenInstance(path).cloneAsThawed(); // not frozen, for removeElement
218 if (path.startsWith("//ldml/dates/timeZoneNames/metazone")
219 || path.startsWith("//ldml/dates/timeZoneNames/zone")) {
220 String element = nextParts.getElement(-1);
221 nextParts.setElement(-1, "zoneChoice");
222 nextParts.putAttributeValue(-1, "type", element);
223 element = nextParts.getElement(-2);
224 nextParts.setElement(-2, "zoneLength");
225 nextParts.putAttributeValue(-2, "type", element);
226 } else if (path.startsWith("//ldml/dates/calendars/calendar")) {
227 if (!"gregorian".equals(parts.getAttributeValue(3, "type"))) {
228 for (int i = parts.size() - 1; i > 3; --i) {
229 parts.removeElement(i);
230 }
231 parts.addElement("*");
232 }
233 }
234 return parts;
235 }
236
237 private void addDifferences() {
238 for (int i = 0; i < lastParts.size(); ++i) {
239 Map<String, String> lastAttrs = lastParts.getAttributes(i);
240 Map<String, String> nextAttrs = nextParts.getAttributes(i);
241 if (!lastAttrs.equals(nextAttrs)) {
242 String element = lastParts.getElement(i);
243 Relation<String, String> old = differences.get(element);
244 if (old == null) {
245 old = Relation.of(new TreeMap<String, Set<String>>(attributeComparator), TreeSet.class);
246 differences.put(element, old);
247 }
248 Set<String> union = Builder.with(new TreeSet<String>()).addAll(lastAttrs.keySet())
249 .addAll(nextAttrs.keySet()).get();
250 for (String key : union) {
251 String lastValue = lastAttrs.get(key);
252 String nextValue = nextAttrs.get(key);
253 if (!Objects.equals(lastValue, nextValue)) {
254 if (lastValue != null) old.put(key, lastValue);
255 if (nextValue != null) old.put(key, nextValue);
256 }
257 }
258 }
259 }
260 }
261
262 private boolean sameElements() {
263 if (lastParts.size() != nextParts.size()) return false;
264 for (int i = 0; i < lastParts.size(); ++i) {
265 if (!lastParts.getElement(i).equals(nextParts.getElement(i))) return false;
266 }
267 return true;
268 }
269
270 }
271
272 private static void summarizeCoverage(PrintWriter summary, PrintWriter samples2, PrintWriter counts) {
273 final Factory cldrFactory = Factory.make(MAIN_DIRECTORY, FILES);
274 final Factory collationFactory = Factory.make(COLLATION_DIRECTORY, FILES);
275 final Factory rbnfFactory = Factory.make(RBNF_DIRECTORY, FILES);
276
277 // CLDRFile sd = CLDRFile.make(CLDRFile.SUPPLEMENTAL_NAME, CldrUtility.SUPPLEMENTAL_DIRECTORY, true);
278 // CLDRFile smd = CLDRFile.make(CLDRFile.SUPPLEMENTAL_METADATA, CldrUtility.SUPPLEMENTAL_DIRECTORY, true);
279 //
280 // CoverageLevel.init(sd, smd);
281
282 NumberFormat percent = NumberFormat.getPercentInstance();
283 NumberFormat decimal = NumberFormat.getInstance();
284 decimal.setGroupingUsed(true);
285 decimal.setMaximumFractionDigits(2);
286 percent.setMaximumFractionDigits(2);
287 NumberFormat integer = NumberFormat.getIntegerInstance();
288 Set<String> localesFound = new TreeSet<>();
289
290 // get list of locales
291 LocaleLevelData mapLevelData = new LocaleLevelData();
292 TreeSet<String> mainAvailableSource = new TreeSet<>(cldrFactory.getAvailable());
293 TreeSet<String> mainAvailable = new TreeSet<>();
294 Relation<String, String> localeToVariants = Relation.of(new HashMap(), HashSet.class);
295 for (String locale : mainAvailableSource) {
296 if (localeFilter.skipLocale(locale, localeToVariants)) {
297 continue;
298 }
299 mainAvailable.add(locale);
300 }
301
302 System.out.println("gathering rbnf data");
303 Set<String> ordinals = new TreeSet<>();
304 Set<String> spellout = new TreeSet<>();
305 localesFound.clear();
306 for (String locale : rbnfFactory.getAvailable()) {
307 if (localeFilter.skipLocale(locale, null)) continue;
308 System.out.println(locale + "\t" + english.getName(locale));
309 getRBNFData(locale, rbnfFactory.make(locale, true), ordinals, spellout, localesFound);
310 }
311 markData("RBNF-Ordinals", ordinals, mapLevelData, mainAvailable, RBNF_LEVEL, RBNF_WEIGHT,
312 Row.of("//ldml/rbnf/ordinals", "?"));
313 markData("RBNF-Spellout", spellout, mapLevelData, mainAvailable, RBNF_LEVEL, RBNF_WEIGHT,
314 Row.of("//ldml/rbnf/spellout", "?"));
315 if (localesFound.size() != 0) {
316 System.out.println("Other rbnf found:\t" + localesFound);
317 }
318
319 System.out.println("gathering plural data");
320 localesFound = new TreeSet<>(supplementalData.getPluralLocales(PluralType.cardinal));
321 markData("Plurals", localesFound, mapLevelData, mainAvailable, PLURALS_LEVEL, PLURALS_WEIGHT,
322 Row.of("//supplementalData/plurals", "UCA"));
323
324 System.out.println("gathering collation data");
325 localesFound.clear();
326 for (String locale : collationFactory.getAvailable()) {
327 if (localeFilter.skipLocale(locale, null)) continue;
328 System.out.println(locale + "\t" + english.getName(locale));
329 getCollationData(locale, collationFactory.make(locale, true), localesFound);
330 }
331 markData("Collation", localesFound, mapLevelData, mainAvailable, COLLATION_LEVEL, COLLATION_WEIGHT,
332 Row.of("//ldml/collations", "UCA"));
333
334 System.out.println("gathering main data");
335 for (String locale : mainAvailable) {
336 System.out.println(locale + "\t" + english.getName(locale));
337 LevelData levelData = mapLevelData.get(locale);
338 getMainData(locale, levelData, cldrFactory.make(locale, true));
339 }
340
341 System.out.println("printing data");
342 String summaryLineHeader = "Code\tName\tWeighted Missing:\tFound:\tScore:";
343 summary.println(summaryLineHeader);
344 LanguageTagParser languageTagParser = new LanguageTagParser();
345
346 StringBuilder header = new StringBuilder();
347 //EnumSet<Level> skipLevels = EnumSet.of(Level.CORE, Level.POSIX, Level.COMPREHENSIVE, Level.OPTIONAL);
348 for (String locale : mapLevelData.keySet()) {
349 LevelData levelData = mapLevelData.get(locale);
350 String max = LikelySubtags.maximize(locale, supplementalData.getLikelySubtags());
351 String lang = languageTagParser.set(max).getLanguage();
352 String script = languageTagParser.set(max).getScript();
353
354 Counter<Level> missing = levelData.missing;
355 Counter<Level> found = levelData.found;
356 Relation<Level, R2<String, String>> samples = levelData.samples;
357 StringBuilder countLine = new StringBuilder(
358 script
359 + "\t" + english.getName(CLDRFile.SCRIPT_NAME, script)
360 + "\t" + lang
361 + "\t" + english.getName(CLDRFile.LANGUAGE_NAME, lang));
362 if (header != null) {
363 header.append("Code\tScript\tCode\tLocale");
364 }
365 // Now print the information
366 samples2.println();
367 samples2.println(locale + "\t" + english.getName(locale));
368 double weightedFound = 0;
369 double weightedMissing = 0;
370 long missingCountTotal = 0;
371 long foundCountTotal = 0;
372
373 for (Level level : Level.values()) {
374 if (level == Level.UNDETERMINED) {
375 continue;
376 }
377 long missingCount = missing.get(level);
378 missingCountTotal += missingCount;
379 long foundCount = found.get(level);
380 foundCountTotal += foundCount;
381 weightedFound += foundCount * level.getValue();
382 weightedMissing += missingCount * level.getValue();
383
384 countLine.append('\t').append(missingCountTotal).append('\t').append(foundCountTotal);
385 if (header != null) {
386 header.append("\t" + level + "-Missing\tFound");
387 }
388
389 samples2.println(level + "\tMissing:\t" + integer.format(missingCount) + "\tFound:\t"
390 + integer.format(foundCount)
391 + "\tScore:\t" + percent.format(foundCount / (double) (foundCount + missingCount))
392 + "\tLevel-Value:\t" + level.getValue());
393 Set<R2<String, String>> samplesAlready = samples.getAll(level);
394 if (samplesAlready != null) {
395 for (R2<String, String> row : samplesAlready) {
396 samples2.println("\t" + row);
397 }
398 if (samplesAlready.size() >= SHOW_EXAMPLES) {
399 samples2.println("\t...");
400 }
401 }
402 }
403 int base = Level.POSIX.getValue();
404 double foundCount = weightedFound / base;
405 double missingCount = weightedMissing / base;
406 String summaryLine = "Weighted Missing:\t" + decimal.format(missingCount) + "\tFound:\t"
407 + decimal.format(foundCount) + "\tScore:\t"
408 + percent.format(foundCount / (foundCount + missingCount));
409 String summaryLine2 = "\t" + decimal.format(missingCount) + "\t" + decimal.format(foundCount) + "\t"
410 + percent.format(foundCount / (foundCount + missingCount));
411 samples2.println(summaryLine);
412 summary.println(locale + "\t" + english.getName(locale) + "\t" + summaryLine2);
413 if (header != null) {
414 counts.println(header);
415 header = null;
416 }
417 counts.println(countLine);
418 }
419 }
420
421 private static void getRBNFData(String locale, CLDRFile cldrFile, Set<String> ordinals, Set<String> spellout,
422 Set<String> others) {
423 for (String path : cldrFile) {
424 if (path.endsWith("/alias")) {
425 continue;
426 }
427 if (!path.contains("rulesetGrouping")) {
428 continue;
429 }
430 if (skipUnconfirmed(path)) {
431 continue;
432 }
433 XPathParts parts = XPathParts.getFrozenInstance(path);
434 String ruleSetGrouping = parts.getAttributeValue(2, "type");
435 if (ruleSetGrouping.equals("SpelloutRules")) {
436 spellout.add(locale);
437 } else if (ruleSetGrouping.equals("OrdinalRules")) {
438 ordinals.add(locale);
439 } else {
440 others.add(ruleSetGrouping);
441 }
442 }
443 }
444
445 private static void markData(String title, Set<String> localesFound, LocaleLevelData mapLevelData,
446 TreeSet<String> mainAvailable, Level level, long weight, R2<String, String> samples) {
447 if (!mainAvailable.containsAll(localesFound)) {
448 final CBuilder<String, TreeSet<String>> cb = Builder.with(new TreeSet<String>());
449 System.out.println(title + " Locales that are not in main: " + cb
450 .addAll(localesFound)
451 .removeAll(mainAvailable)
452 .filter(nonAliasLocaleFilter).get());
453 }
454 for (String locale : mainAvailable) {
455 if (localesFound.contains(locale)) {
456 mapLevelData.get(locale).found.add(level, weight);
457 } else {
458 System.out.println(locale + "\t" + english.getName(locale) + "\t" + "missing " + title);
459 mapLevelData.get(locale).missing.add(level, weight);
460 mapLevelData.get(locale).samples.put(level, samples);
461 }
462 }
463 }
464
465 enum LocaleStatus {
466 BASE, ALIAS, VARIANT, DEFAULT_CONTENTS
467 }
468
469 private static class LocaleFilter implements Transform<String, LocaleStatus> {
470 private final LanguageTagParser ltp = new LanguageTagParser();
471 private final boolean checkAliases;
472
473 public LocaleFilter(boolean checkAliases) {
474 this.checkAliases = checkAliases;
475 }
476
477 private boolean skipLocale(String locale, Relation<String, String> localeToVariants) {
478 LocaleStatus result = transform(locale);
479 if (localeToVariants != null) {
480 localeToVariants.put(ltp.getLanguageScript(), ltp.getRegion());
481 }
482 return result != LocaleStatus.BASE;
483 }
484
485 @Override
486 public LocaleStatus transform(String locale) {
487 ltp.set(locale);
488 if (checkAliases) {
489 String language = ltp.getLanguage();
490 if (languageAliasInfo.get(language) != null) {
491 return LocaleStatus.ALIAS;
492 }
493 }
494 if (ltp.getRegion().length() != 0 || !ltp.getVariants().isEmpty()) {
495 // skip country locales, variants
496 return LocaleStatus.VARIANT;
497 }
498 if (defaultContents.contains(locale)) {
499 return LocaleStatus.DEFAULT_CONTENTS;
500 }
501 return LocaleStatus.BASE;
502 }
503 }
504
505 private static class BooleanLocaleFilter implements Transform<String, Boolean> {
506 private final LocaleFilter filter = new LocaleFilter(false);
507
508 @Override
509 public Boolean transform(String locale) {
510 return filter.transform(locale) == LocaleStatus.BASE ? Boolean.TRUE : Boolean.FALSE;
511 }
512 }
513
514 private static void getCollationData(String locale, CLDRFile cldrFile, Set<String> localesFound) {
515 for (String path : cldrFile) {
516 if (path.endsWith("/alias")) {
517 continue;
518 }
519 if (!path.contains("collations")) {
520 continue;
521 }
522 if (skipUnconfirmed(path)) {
523 continue;
524 }
525 localesFound.add(locale);
526
527 String fullPath = cldrFile.getFullXPath(path);
528 if (fullPath == null) {
529 fullPath = path;
530 }
531 XPathParts parts = XPathParts.getFrozenInstance(fullPath);
532 String validSubLocales = parts.getAttributeValue(1, "validSubLocales");
533 if (validSubLocales != null) {
534 String[] sublocales = validSubLocales.split("\\s+");
535 for (String sublocale : sublocales) {
536 if (localeFilter.skipLocale(locale, null)) continue;
537 localesFound.add(sublocale);
538 }
539 }
540 break;
541 }
542 }
543
544 public static boolean skipUnconfirmed(String path) {
545 return SKIP_UNCONFIRMED && (path.contains("unconfirmed") || path.contains("provisional"));
546 }
547
548 private static void getMainData(String locale, LevelData levelData, CLDRFile cldrFile) {
549 Status status = new Status();
550 Set<String> sorted = Builder.with(new TreeSet<String>()).addAll(cldrFile.iterator())
551 .addAll(cldrFile.getExtraPaths()).get();
552 CoverageInfo coverageInfo = CLDRConfig.getInstance().getCoverageInfo();
553 for (String path : sorted) {
554 if (path.endsWith("/alias")) {
555 continue;
556 }
557
558 String fullPath = cldrFile.getFullXPath(path);
559 String source = cldrFile.getSourceLocaleID(path, status);
560 Inheritance inherited = !source.equals(locale) || skipUnconfirmed(path)
561 ? Inheritance.inherited
562 : Inheritance.actual;
563
564 // Level level = sdi.getCoverageLevel(fullPath, locale);
565 Level level = coverageInfo.getCoverageLevel(fullPath, locale);
566 if (inherited == Inheritance.actual) {
567 levelData.found.add(level, 1);
568 } else {
569 levelData.missing.add(level, 1);
570 if (SHOW_EXAMPLES > 0) {
571 Set<R2<String, String>> samplesAlready = levelData.samples.getAll(level);
572 if (samplesAlready == null || samplesAlready.size() < SHOW_EXAMPLES) {
573 levelData.samples.put(level, Row.of(path, cldrFile.getStringValue(path)));
574 }
575 }
576 }
577 }
578 }
579
580 static class LevelData {
581 Counter<Level> missing = new Counter<>();
582 Relation<Level, R2<String, String>> samples = Relation.of(new EnumMap<Level, Set<R2<String, String>>>(
583 Level.class), LinkedHashSet.class);
584 Counter<Level> found = new Counter<>();
585 }
586
587 static class LocaleLevelData {
588 Map<String, LevelData> locale_levelData = new TreeMap<>();
589
590 public LevelData get(String locale) {
591 if (locale.equals("zh_Hans") || locale.equals("iw")) {
592 throw new IllegalArgumentException();
593 }
594 LevelData result = locale_levelData.get(locale);
595 if (result == null) {
596 locale_levelData.put(locale, result = new LevelData());
597 }
598 return result;
599 }
600
601 public Set<String> keySet() {
602 return locale_levelData.keySet();
603 }
604 }
605
606 }
607