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 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<R3<Level, String, Inheritance>>(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, cldrFile, item, store); 118 } 119 show(out, cldrFile, null, store); 120 } 121 122 private static class RowComparator implements Comparator<R3<Level, String, Inheritance>> { 123 124 public int compare(R3<Level, String, Inheritance> o1, R3<Level, String, Inheritance> o2) { 125 int result = o1.get0().compareTo(o2.get0()); 126 if (result != 0) return result; 127 result = CLDRFile.getComparator(DtdType.ldml).compare(o1.get1(), o2.get1()); 128 if (result != 0) return result; 129 result = o1.get2().compareTo(o2.get2()); 130 return result; 131 } 132 } 133 134 private static void show(PrintWriter out, CLDRFile cldrFile, R3<Level, String, Inheritance> next, PathStore store) { 135 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> results = store.add(next); 136 if (results != null) { 137 Level lastLevel = results.get0(); 138 int count = results.get2(); 139 String path = results.get3(); 140 totalCount += count; 141 try { 142 StringBuilder resultString = new StringBuilder(); 143 TreeMap<String, Relation<String, String>> types = results.get4(); 144 for (String key : types.keySet()) { 145 Relation<String, String> attr_values = types.get(key); 146 for (String attr : attr_values.keySet()) { 147 resultString.append("\t").append(key + ":\u200b" + attr).append("=\u200b") 148 .append(attr_values.getAll(attr)); 149 } 150 } 151 out.println(lastLevel.ordinal() 152 + "\t" + lastLevel 153 + "\t" + count 154 + "\t" + totalCount 155 + "\t" + path + resultString); 156 } catch (RuntimeException e) { 157 throw e; 158 } 159 } 160 } 161 162 static class PathStore { 163 XPathParts lastParts = new XPathParts(); 164 XPathParts nextParts = new XPathParts(); 165 Level lastLevel; 166 Inheritance lastInheritance; 167 int count = 0; 168 169 TreeMap<String, Relation<String, String>> differences = new TreeMap<String, Relation<String, String>>(); 170 171 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> add( 172 R3<Level, String, Inheritance> next) { 173 count++; 174 boolean wasNull = lastLevel == null; 175 Level level = null; 176 String path = null; 177 Inheritance inherited = null; 178 179 if (next != null) { 180 level = next.get0(); 181 path = next.get1(); 182 inherited = next.get2(); 183 184 setParts(nextParts, path); 185 if (sameElements()) { 186 addDifferences(); 187 return null; 188 } 189 } 190 // clear the values 191 clean(lastParts, differences); 192 R5<Level, Inheritance, Integer, String, TreeMap<String, Relation<String, String>>> results = Row.of( 193 lastLevel, lastInheritance, count - 1, lastParts.toString().replace("/", "\u200B/"), differences); 194 lastParts = nextParts; 195 differences = new TreeMap<String, Relation<String, String>>(); 196 nextParts = new XPathParts(); 197 lastLevel = level; 198 lastInheritance = inherited; 199 count = 1; 200 if (wasNull) return null; 201 return results; 202 } 203 204 private void clean(XPathParts lastParts2, TreeMap<String, Relation<String, String>> differences2) { 205 for (int i = 0; i < lastParts.size(); ++i) { 206 String element = lastParts.getElement(i); 207 Relation<String, String> attr_values = differences2.get(element); 208 if (attr_values == null) continue; 209 for (String attr : attr_values.keySet()) { 210 lastParts.putAttributeValue(i, attr, "*"); 211 } 212 } 213 } 214 215 private void setParts(XPathParts parts, String path) { 216 parts.set(path); 217 if (path.startsWith("//ldml/dates/timeZoneNames/metazone") 218 || path.startsWith("//ldml/dates/timeZoneNames/zone")) { 219 String element = nextParts.getElement(-1); 220 nextParts.setElement(-1, "zoneChoice"); 221 nextParts.putAttributeValue(-1, "type", element); 222 element = nextParts.getElement(-2); 223 nextParts.setElement(-2, "zoneLength"); 224 nextParts.putAttributeValue(-2, "type", element); 225 } else if (path.startsWith("//ldml/dates/calendars/calendar")) { 226 if (!"gregorian".equals(parts.getAttributeValue(3, "type"))) { 227 for (int i = parts.size() - 1; i > 3; --i) { 228 parts.removeElement(i); 229 } 230 parts.addElement("*"); 231 } 232 } 233 } 234 235 private void addDifferences() { 236 for (int i = 0; i < lastParts.size(); ++i) { 237 Map<String, String> lastAttrs = lastParts.getAttributes(i); 238 Map<String, String> nextAttrs = nextParts.getAttributes(i); 239 if (!lastAttrs.equals(nextAttrs)) { 240 String element = lastParts.getElement(i); 241 Relation<String, String> old = differences.get(element); 242 if (old == null) { 243 old = Relation.of(new TreeMap<String, Set<String>>(attributeComparator), TreeSet.class); 244 differences.put(element, old); 245 } 246 Set<String> union = Builder.with(new TreeSet<String>()).addAll(lastAttrs.keySet()) 247 .addAll(nextAttrs.keySet()).get(); 248 for (String key : union) { 249 String lastValue = lastAttrs.get(key); 250 String nextValue = nextAttrs.get(key); 251 if (!Objects.equals(lastValue, nextValue)) { 252 if (lastValue != null) old.put(key, lastValue); 253 if (nextValue != null) old.put(key, nextValue); 254 } 255 } 256 } 257 } 258 } 259 260 private boolean sameElements() { 261 if (lastParts.size() != nextParts.size()) return false; 262 for (int i = 0; i < lastParts.size(); ++i) { 263 if (!lastParts.getElement(i).equals(nextParts.getElement(i))) return false; 264 } 265 return true; 266 } 267 268 } 269 270 private static void summarizeCoverage(PrintWriter summary, PrintWriter samples2, PrintWriter counts) { 271 final Factory cldrFactory = Factory.make(MAIN_DIRECTORY, FILES); 272 final Factory collationFactory = Factory.make(COLLATION_DIRECTORY, FILES); 273 final Factory rbnfFactory = Factory.make(RBNF_DIRECTORY, FILES); 274 275 // CLDRFile sd = CLDRFile.make(CLDRFile.SUPPLEMENTAL_NAME, CldrUtility.SUPPLEMENTAL_DIRECTORY, true); 276 // CLDRFile smd = CLDRFile.make(CLDRFile.SUPPLEMENTAL_METADATA, CldrUtility.SUPPLEMENTAL_DIRECTORY, true); 277 // 278 // CoverageLevel.init(sd, smd); 279 280 NumberFormat percent = NumberFormat.getPercentInstance(); 281 NumberFormat decimal = NumberFormat.getInstance(); 282 decimal.setGroupingUsed(true); 283 decimal.setMaximumFractionDigits(2); 284 percent.setMaximumFractionDigits(2); 285 NumberFormat integer = NumberFormat.getIntegerInstance(); 286 Set<String> localesFound = new TreeSet<String>(); 287 288 // get list of locales 289 LocaleLevelData mapLevelData = new LocaleLevelData(); 290 TreeSet<String> mainAvailableSource = new TreeSet<String>(cldrFactory.getAvailable()); 291 TreeSet<String> mainAvailable = new TreeSet<String>(); 292 Relation<String, String> localeToVariants = Relation.of(new HashMap(), HashSet.class); 293 for (String locale : mainAvailableSource) { 294 if (localeFilter.skipLocale(locale, localeToVariants)) { 295 continue; 296 } 297 mainAvailable.add(locale); 298 } 299 300 System.out.println("gathering rbnf data"); 301 Set<String> ordinals = new TreeSet<String>(); 302 Set<String> spellout = new TreeSet<String>(); 303 localesFound.clear(); 304 for (String locale : rbnfFactory.getAvailable()) { 305 if (localeFilter.skipLocale(locale, null)) continue; 306 System.out.println(locale + "\t" + english.getName(locale)); 307 getRBNFData(locale, rbnfFactory.make(locale, true), ordinals, spellout, localesFound); 308 } 309 markData("RBNF-Ordinals", ordinals, mapLevelData, mainAvailable, RBNF_LEVEL, RBNF_WEIGHT, 310 Row.of("//ldml/rbnf/ordinals", "?")); 311 markData("RBNF-Spellout", spellout, mapLevelData, mainAvailable, RBNF_LEVEL, RBNF_WEIGHT, 312 Row.of("//ldml/rbnf/spellout", "?")); 313 if (localesFound.size() != 0) { 314 System.out.println("Other rbnf found:\t" + localesFound); 315 } 316 317 System.out.println("gathering plural data"); 318 localesFound = new TreeSet<String>(supplementalData.getPluralLocales(PluralType.cardinal)); 319 markData("Plurals", localesFound, mapLevelData, mainAvailable, PLURALS_LEVEL, PLURALS_WEIGHT, 320 Row.of("//supplementalData/plurals", "UCA")); 321 322 System.out.println("gathering collation data"); 323 localesFound.clear(); 324 for (String locale : collationFactory.getAvailable()) { 325 if (localeFilter.skipLocale(locale, null)) continue; 326 System.out.println(locale + "\t" + english.getName(locale)); 327 getCollationData(locale, collationFactory.make(locale, true), localesFound); 328 } 329 markData("Collation", localesFound, mapLevelData, mainAvailable, COLLATION_LEVEL, COLLATION_WEIGHT, 330 Row.of("//ldml/collations", "UCA")); 331 332 System.out.println("gathering main data"); 333 for (String locale : mainAvailable) { 334 System.out.println(locale + "\t" + english.getName(locale)); 335 LevelData levelData = mapLevelData.get(locale); 336 getMainData(locale, levelData, cldrFactory.make(locale, true)); 337 } 338 339 System.out.println("printing data"); 340 String summaryLineHeader = "Code\tName\tWeighted Missing:\tFound:\tScore:"; 341 summary.println(summaryLineHeader); 342 LanguageTagParser languageTagParser = new LanguageTagParser(); 343 344 StringBuilder header = new StringBuilder(); 345 //EnumSet<Level> skipLevels = EnumSet.of(Level.CORE, Level.POSIX, Level.COMPREHENSIVE, Level.OPTIONAL); 346 for (String locale : mapLevelData.keySet()) { 347 LevelData levelData = mapLevelData.get(locale); 348 String max = LikelySubtags.maximize(locale, supplementalData.getLikelySubtags()); 349 String lang = languageTagParser.set(max).getLanguage(); 350 String script = languageTagParser.set(max).getScript(); 351 352 Counter<Level> missing = levelData.missing; 353 Counter<Level> found = levelData.found; 354 Relation<Level, R2<String, String>> samples = levelData.samples; 355 StringBuilder countLine = new StringBuilder( 356 script 357 + "\t" + english.getName(CLDRFile.SCRIPT_NAME, script) 358 + "\t" + lang 359 + "\t" + english.getName(CLDRFile.LANGUAGE_NAME, lang)); 360 if (header != null) { 361 header.append("Code\tScript\tCode\tLocale"); 362 } 363 // Now print the information 364 samples2.println(); 365 samples2.println(locale + "\t" + english.getName(locale)); 366 double weightedFound = 0; 367 double weightedMissing = 0; 368 long missingCountTotal = 0; 369 long foundCountTotal = 0; 370 371 for (Level level : Level.values()) { 372 if (level == Level.UNDETERMINED) { 373 continue; 374 } 375 long missingCount = missing.get(level); 376 missingCountTotal += missingCount; 377 long foundCount = found.get(level); 378 foundCountTotal += foundCount; 379 weightedFound += foundCount * level.getValue(); 380 weightedMissing += missingCount * level.getValue(); 381 382 countLine.append('\t').append(missingCountTotal).append('\t').append(foundCountTotal); 383 if (header != null) { 384 header.append("\t" + level + "-Missing\tFound"); 385 } 386 387 samples2.println(level + "\tMissing:\t" + integer.format(missingCount) + "\tFound:\t" 388 + integer.format(foundCount) 389 + "\tScore:\t" + percent.format(foundCount / (double) (foundCount + missingCount)) 390 + "\tLevel-Value:\t" + level.getValue()); 391 Set<R2<String, String>> samplesAlready = samples.getAll(level); 392 if (samplesAlready != null) { 393 for (R2<String, String> row : samplesAlready) { 394 samples2.println("\t" + row); 395 } 396 if (samplesAlready.size() >= SHOW_EXAMPLES) { 397 samples2.println("\t..."); 398 } 399 } 400 } 401 int base = Level.POSIX.getValue(); 402 double foundCount = weightedFound / base; 403 double missingCount = weightedMissing / base; 404 String summaryLine = "Weighted Missing:\t" + decimal.format(missingCount) + "\tFound:\t" 405 + decimal.format(foundCount) + "\tScore:\t" 406 + percent.format(foundCount / (foundCount + missingCount)); 407 String summaryLine2 = "\t" + decimal.format(missingCount) + "\t" + decimal.format(foundCount) + "\t" 408 + percent.format(foundCount / (foundCount + missingCount)); 409 samples2.println(summaryLine); 410 summary.println(locale + "\t" + english.getName(locale) + "\t" + summaryLine2); 411 if (header != null) { 412 counts.println(header); 413 header = null; 414 } 415 counts.println(countLine); 416 } 417 } 418 419 private static void getRBNFData(String locale, CLDRFile cldrFile, Set<String> ordinals, Set<String> spellout, 420 Set<String> others) { 421 XPathParts parts = new XPathParts(); 422 for (String path : cldrFile) { 423 if (path.endsWith("/alias")) { 424 continue; 425 } 426 if (!path.contains("rulesetGrouping")) { 427 continue; 428 } 429 if (skipUnconfirmed(path)) { 430 continue; 431 } 432 parts.set(path); 433 String ruleSetGrouping = parts.getAttributeValue(2, "type"); 434 if (ruleSetGrouping.equals("SpelloutRules")) { 435 spellout.add(locale); 436 } else if (ruleSetGrouping.equals("OrdinalRules")) { 437 ordinals.add(locale); 438 } else { 439 others.add(ruleSetGrouping); 440 } 441 } 442 } 443 444 private static void markData(String title, Set<String> localesFound, LocaleLevelData mapLevelData, 445 TreeSet<String> mainAvailable, Level level, long weight, R2<String, String> samples) { 446 if (!mainAvailable.containsAll(localesFound)) { 447 final CBuilder<String, TreeSet<String>> cb = Builder.with(new TreeSet<String>()); 448 System.out.println(title + " Locales that are not in main: " + cb 449 .addAll(localesFound) 450 .removeAll(mainAvailable) 451 .filter(nonAliasLocaleFilter).get()); 452 } 453 for (String locale : mainAvailable) { 454 if (localesFound.contains(locale)) { 455 mapLevelData.get(locale).found.add(level, weight); 456 } else { 457 System.out.println(locale + "\t" + english.getName(locale) + "\t" + "missing " + title); 458 mapLevelData.get(locale).missing.add(level, weight); 459 mapLevelData.get(locale).samples.put(level, samples); 460 } 461 } 462 } 463 464 enum LocaleStatus { 465 BASE, ALIAS, VARIANT, DEFAULT_CONTENTS 466 } 467 468 private static class LocaleFilter implements Transform<String, LocaleStatus> { 469 private final LanguageTagParser ltp = new LanguageTagParser(); 470 private final boolean checkAliases; 471 472 public LocaleFilter(boolean checkAliases) { 473 this.checkAliases = checkAliases; 474 } 475 476 private boolean skipLocale(String locale, Relation<String, String> localeToVariants) { 477 LocaleStatus result = transform(locale); 478 if (localeToVariants != null) { 479 localeToVariants.put(ltp.getLanguageScript(), ltp.getRegion()); 480 } 481 return result != LocaleStatus.BASE; 482 } 483 484 public LocaleStatus transform(String locale) { 485 ltp.set(locale); 486 if (checkAliases) { 487 String language = ltp.getLanguage(); 488 if (languageAliasInfo.get(language) != null) { 489 return LocaleStatus.ALIAS; 490 } 491 } 492 if (ltp.getRegion().length() != 0 || !ltp.getVariants().isEmpty()) { 493 // skip country locales, variants 494 return LocaleStatus.VARIANT; 495 } 496 if (defaultContents.contains(locale)) { 497 return LocaleStatus.DEFAULT_CONTENTS; 498 } 499 return LocaleStatus.BASE; 500 } 501 } 502 503 private static class BooleanLocaleFilter implements Transform<String, Boolean> { 504 private final LocaleFilter filter = new LocaleFilter(false); 505 506 public Boolean transform(String locale) { 507 return filter.transform(locale) == LocaleStatus.BASE ? Boolean.TRUE : Boolean.FALSE; 508 } 509 } 510 511 private static void getCollationData(String locale, CLDRFile cldrFile, Set<String> localesFound) { 512 XPathParts parts = new XPathParts(); 513 for (String path : cldrFile) { 514 if (path.endsWith("/alias")) { 515 continue; 516 } 517 if (!path.contains("collations")) { 518 continue; 519 } 520 if (skipUnconfirmed(path)) { 521 continue; 522 } 523 localesFound.add(locale); 524 525 String fullPath = cldrFile.getFullXPath(path); 526 if (fullPath == null) fullPath = path; 527 try { 528 parts.set(fullPath); 529 } catch (RuntimeException e) { 530 throw e; 531 } 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<Level>(); 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<Level>(); 585 } 586 587 static class LocaleLevelData { 588 Map<String, LevelData> locale_levelData = new TreeMap<String, LevelData>(); 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