1 package org.unicode.cldr.tool; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.PrintWriter; 6 import java.util.ArrayList; 7 import java.util.Arrays; 8 import java.util.Collection; 9 import java.util.Collections; 10 import java.util.HashSet; 11 import java.util.List; 12 import java.util.Map; 13 import java.util.Map.Entry; 14 import java.util.Objects; 15 import java.util.Set; 16 import java.util.TreeMap; 17 import java.util.TreeSet; 18 import java.util.regex.Matcher; 19 import java.util.regex.Pattern; 20 21 import org.unicode.cldr.draft.FileUtilities; 22 import org.unicode.cldr.tool.FormattedFileWriter.Anchors; 23 import org.unicode.cldr.tool.Option.Options; 24 import org.unicode.cldr.tool.Option.Params; 25 import org.unicode.cldr.util.CLDRConfig; 26 import org.unicode.cldr.util.CLDRFile; 27 import org.unicode.cldr.util.CLDRFile.Status; 28 import org.unicode.cldr.util.CLDRPaths; 29 import org.unicode.cldr.util.CldrUtility; 30 import org.unicode.cldr.util.Counter; 31 import org.unicode.cldr.util.DtdData; 32 import org.unicode.cldr.util.DtdType; 33 import org.unicode.cldr.util.Factory; 34 import org.unicode.cldr.util.LanguageTagParser; 35 import org.unicode.cldr.util.Level; 36 import org.unicode.cldr.util.LocaleIDParser; 37 import org.unicode.cldr.util.Pair; 38 import org.unicode.cldr.util.PathHeader; 39 import org.unicode.cldr.util.PathHeader.PageId; 40 import org.unicode.cldr.util.PathStarrer; 41 import org.unicode.cldr.util.PatternCache; 42 import org.unicode.cldr.util.SimpleXMLSource; 43 import org.unicode.cldr.util.SupplementalDataInfo; 44 import org.unicode.cldr.util.TransliteratorUtilities; 45 import org.unicode.cldr.util.XMLFileReader; 46 import org.unicode.cldr.util.XPathParts; 47 48 import com.google.common.base.Splitter; 49 import com.google.common.collect.Multimap; 50 import com.google.common.collect.TreeMultimap; 51 import com.ibm.icu.dev.util.CollectionUtilities; 52 import com.ibm.icu.impl.Relation; 53 import com.ibm.icu.impl.Row.R2; 54 import com.ibm.icu.impl.Row.R3; 55 import com.ibm.icu.impl.Row.R4; 56 import com.ibm.icu.text.NumberFormat; 57 import com.ibm.icu.util.ICUUncheckedIOException; 58 import com.ibm.icu.util.Output; 59 60 public class ChartDelta extends Chart { 61 private static final String DIR_NAME = "delta"; 62 63 private static final boolean SKIP_REFORMAT_ANNOTATIONS = ToolConstants.PREVIOUS_CHART_VERSION.compareTo("30") >= 0; 64 65 private static final PageId DEBUG_PAGE_ID = PageId.DayPeriod; 66 67 private static final SupplementalDataInfo SUPPLEMENTAL_DATA_INFO = CLDRConfig.getInstance().getSupplementalDataInfo(); 68 69 private static final String LAST_ARCHIVE_DIRECTORY = CLDRPaths.LAST_DIRECTORY; 70 private static final String CURRENT_DIRECTORY = CLDRPaths.ARCHIVE_DIRECTORY + "cldr-" + 71 ToolConstants.LAST_CHART_VERSION + "/"; 72 private static final String LOG_DIR = CLDRPaths.GEN_DIRECTORY + "charts/"; 73 74 enum MyOptions { 75 fileFilter(new Params().setHelp("filter by dir/locale, eg: ^main/en$ or .*/en").setDefault(".*").setMatch(".*")), verbose( 76 new Params().setHelp("verbose debugging messages")), 77 ; 78 79 // BOILERPLATE TO COPY 80 final Option option; 81 MyOptions(Params params)82 private MyOptions(Params params) { 83 option = new Option(this, params); 84 } 85 86 private static Options myOptions = new Options(); 87 static { 88 for (MyOptions option : MyOptions.values()) { myOptions.add(option, option.option)89 myOptions.add(option, option.option); 90 } 91 } 92 parse(String[] args, boolean showArguments)93 private static Set<String> parse(String[] args, boolean showArguments) { 94 return myOptions.parse(MyOptions.values()[0], args, true); 95 } 96 } 97 98 private Matcher fileFilter; 99 private boolean verbose; 100 ChartDelta(Matcher fileFilter, boolean verbose)101 public ChartDelta(Matcher fileFilter, boolean verbose) { 102 this.fileFilter = fileFilter; 103 this.verbose = verbose; 104 } 105 main(String[] args)106 public static void main(String[] args) { 107 System.out.println("use -DCHART_VERSION=28 to generate version 28."); 108 MyOptions.parse(args, true); 109 Matcher fileFilter = !MyOptions.fileFilter.option.doesOccur() ? null : PatternCache.get(MyOptions.fileFilter.option.getValue()).matcher(""); 110 boolean verbose = MyOptions.verbose.option.doesOccur(); 111 ChartDelta temp = new ChartDelta(fileFilter, verbose); 112 temp.writeChart(null); 113 temp.showTotals(); 114 } 115 116 private static final String SEP = "\u0001"; 117 private static final boolean DEBUG = false; 118 private static final String DEBUG_FILE = null; // "windowsZones.xml"; 119 static Pattern fileMatcher = PatternCache.get(".*"); 120 121 static String DIR = CLDRPaths.CHART_DIRECTORY + "/" + DIR_NAME + "/"; 122 static PathHeader.Factory phf = PathHeader.getFactory(ENGLISH); 123 static final Set<String> DONT_CARE = new HashSet<>(Arrays.asList("draft", "standard", "reference")); 124 125 @Override getDirectory()126 public String getDirectory() { 127 return DIR; 128 } 129 130 @Override getTitle()131 public String getTitle() { 132 return "Delta Charts"; 133 } 134 135 @Override getFileName()136 public String getFileName() { 137 return "index"; 138 } 139 140 @Override getExplanation()141 public String getExplanation() { 142 return "<p>Charts showing the differences from the last version. " 143 + "Titles prefixed by ¤ are special: either the locale data summary or supplemental data. " 144 + "Not all changed data is charted yet. For details see each chart.</p>"; 145 } 146 147 @Override writeContents(FormattedFileWriter pw)148 public void writeContents(FormattedFileWriter pw) throws IOException { 149 FormattedFileWriter.Anchors anchors = new FormattedFileWriter.Anchors(); 150 FileUtilities.copyFile(ChartDelta.class, "index.css", getDirectory()); 151 counter.clear(); 152 fileCounters.clear(); 153 writeNonLdmlPlain(anchors); 154 writeLdml(anchors); 155 pw.setIndex("Main Chart Index", "../index.html"); 156 pw.write(anchors.toString()); 157 } 158 159 static class PathHeaderSegment extends R3<PathHeader, Integer, String> { PathHeaderSegment(PathHeader b, int elementIndex, String attribute)160 public PathHeaderSegment(PathHeader b, int elementIndex, String attribute) { 161 super(b, elementIndex, attribute); 162 } 163 } 164 165 static class PathDiff extends R4<PathHeaderSegment, String, String, String> { PathDiff(String locale, PathHeaderSegment pathHeaderSegment, String oldValue, String newValue)166 public PathDiff(String locale, PathHeaderSegment pathHeaderSegment, String oldValue, String newValue) { 167 super(pathHeaderSegment, locale, oldValue, newValue); 168 } 169 } 170 171 static final CLDRFile EMPTY_CLDR = new CLDRFile(new SimpleXMLSource("und").freeze()); 172 173 enum ChangeType { 174 added, deleted, changed, same; get(String oldValue, String currentValue)175 public static ChangeType get(String oldValue, String currentValue) { 176 return oldValue == null ? added 177 : currentValue == null ? deleted 178 : oldValue.equals(currentValue) ? same 179 : changed; 180 } 181 } 182 183 Counter<ChangeType> counter = new Counter<>(); 184 Map<String, Counter<ChangeType>> fileCounters = new TreeMap<>(); 185 Set<String> badHeaders = new TreeSet<>(); 186 addChange(String file, ChangeType changeType, int count)187 private void addChange(String file, ChangeType changeType, int count) { 188 counter.add(changeType, count); // unified add 189 Counter<ChangeType> fileCounter = fileCounters.get(file); 190 if (fileCounter == null) { 191 fileCounters.put(file, fileCounter = new Counter<>()); 192 } 193 fileCounter.add(changeType, count); 194 } 195 showTotals()196 private void showTotals() { 197 try (PrintWriter pw = FileUtilities.openUTF8Writer(getTsvDir(DIR, DIR_NAME), DIR_NAME + "_summary.tsv")) { 198 pw.println("# percentages are of *new* total"); 199 pw.print("# dir\tfile"); 200 for (ChangeType item : ChangeType.values()) { 201 pw.print("\t" + (item == ChangeType.same ? "total" : item.toString())); 202 } 203 pw.println(); 204 showTotal(pw, "TOTAL/", counter); 205 206 for (Entry<String, Counter<ChangeType>> entry : fileCounters.entrySet()) { 207 showTotal(pw, entry.getKey(), entry.getValue()); 208 } 209 for (String s : badHeaders) { 210 pw.println(s); 211 } 212 pw.println("# EOF"); 213 } catch (IOException e) { 214 throw new ICUUncheckedIOException(e); 215 } 216 } 217 showTotal(PrintWriter pw, String title2, Counter<ChangeType> counter2)218 private void showTotal(PrintWriter pw, String title2, Counter<ChangeType> counter2) { 219 long total = counter2.getTotal(); 220 NumberFormat pf = NumberFormat.getPercentInstance(); 221 pf.setMinimumFractionDigits(2); 222 NumberFormat nf = NumberFormat.getIntegerInstance(); 223 pw.print(title2.replace("/", "\t")); 224 for (ChangeType item : ChangeType.values()) { 225 if (item == ChangeType.same) { 226 pw.print("\t" + nf.format(total)); 227 } else { 228 final long current = counter2.getCount(item); 229 pw.print("\t" + nf.format(current)); 230 } 231 } 232 pw.println(); 233 } 234 writeSubcharts(Anchors anchors)235 public void writeSubcharts(Anchors anchors) throws IOException { 236 FileUtilities.copyFile(ChartDelta.class, "index.css", getDirectory()); 237 counter.clear(); 238 fileCounters.clear(); 239 writeNonLdmlPlain(anchors); 240 writeLdml(anchors); 241 } 242 writeLdml(Anchors anchors)243 private void writeLdml(Anchors anchors) throws IOException { 244 245 try (PrintWriter tsvFile = FileUtilities.openUTF8Writer(getTsvDir(DIR, DIR_NAME), DIR_NAME + ".tsv"); 246 PrintWriter tsvCountFile = FileUtilities.openUTF8Writer(getTsvDir(DIR, DIR_NAME), DIR_NAME + "_count.tsv"); 247 ) { 248 tsvFile.println("# Section\tPage\tHeader\tCode\tLocale\tOld\tNew\tLevel"); 249 250 // set up factories 251 List<Factory> factories = new ArrayList<>(); 252 List<Factory> oldFactories = new ArrayList<>(); 253 // factories.add(Factory.make(CLDRPaths.BASE_DIRECTORY + "common/" + "main", ".*")); 254 // oldFactories.add(Factory.make(LAST_ARCHIVE_DIRECTORY + "common/" + "main", ".*")); 255 256 Counter<PathHeader> counts = new Counter<>(); 257 258 for (String dir : DtdType.ldml.directories) { 259 if (dir.equals("annotationsDerived") || dir.equals("casing")) { 260 continue; 261 } 262 String current = (ToolConstants.CLDR_VERSIONS.contains(ToolConstants.LAST_CHART_VERSION) 263 ? CURRENT_DIRECTORY : CLDRPaths.BASE_DIRECTORY) + "common/" + dir; 264 String past = LAST_ARCHIVE_DIRECTORY + "common/" + dir; 265 try { 266 factories.add(Factory.make(current, ".*")); 267 } catch (Exception e1) { 268 System.out.println("Skipping: " + dir + "\t" + e1.getMessage()); 269 continue; // skip where the directories don't exist in old versions 270 } 271 try { 272 oldFactories.add(Factory.make(past, ".*")); 273 } catch (Exception e) { 274 System.out.println("Couldn't open factory: " + past); 275 past = null; 276 oldFactories.add(null); 277 } 278 System.out.println("Will examine: " + dir + "\t\t" + current + "\t\t" + past); 279 } 280 if (factories.isEmpty()) { 281 throw new IllegalArgumentException("No factories found"); 282 } 283 // get a list of all the locales to cycle over 284 285 Relation<String, String> baseToLocales = Relation.of(new TreeMap<String, Set<String>>(), HashSet.class); 286 Matcher m = fileMatcher.matcher(""); 287 Set<String> defaultContents = SDI.getDefaultContentLocales(); 288 LanguageTagParser ltp = new LanguageTagParser(); 289 LikelySubtags ls = new LikelySubtags(SDI); 290 for (String file : factories.get(0).getAvailable()) { 291 if (defaultContents.contains(file)) { 292 continue; 293 } 294 if (!m.reset(file).matches()) { 295 continue; 296 } 297 String base = file.equals("root") ? "root" : ltp.set(ls.minimize(file)).getLanguageScript(); 298 baseToLocales.put(base, file); 299 } 300 301 // do keyboards later 302 303 Status currentStatus = new Status(); 304 Status oldStatus = new Status(); 305 Set<PathDiff> diff = new TreeSet<>(); 306 Set<String> paths = new HashSet<>(); 307 308 Relation<PathHeader, String> diffAll = Relation.of(new TreeMap<PathHeader, Set<String>>(), TreeSet.class); 309 // XPathParts pathPlain = new XPathParts(); 310 for (Entry<String, Set<String>> baseNLocale : baseToLocales.keyValuesSet()) { 311 String base = baseNLocale.getKey(); 312 // int qCount = 0; 313 for (int i = 0; i < factories.size(); ++i) { 314 Factory factory = factories.get(i); 315 Factory oldFactory = oldFactories.get(i); 316 List<File> sourceDirs = Arrays.asList(factory.getSourceDirectories()); 317 if (sourceDirs.size() != 1) { 318 throw new IllegalArgumentException("Internal error: expect single source dir"); 319 } 320 File sourceDir = sourceDirs.get(0); 321 String sourceDirLeaf = sourceDir.getName(); 322 //System.out.println(sourceDirLeaf); 323 boolean resolving = !sourceDirLeaf.contains("subdivisions") 324 && !sourceDirLeaf.contains("transforms"); 325 for (String locale : baseNLocale.getValue()) { 326 //System.out.println("\t" + locale); 327 String nameAndLocale = sourceDirLeaf + "/" + locale; 328 if (fileFilter != null && !fileFilter.reset(nameAndLocale).find()) { 329 if (verbose) { 330 System.out.println("SKIPPING: " + nameAndLocale); 331 } 332 continue; 333 } 334 // boolean isBase = locale.equals(base); 335 if (verbose) { 336 System.out.println(nameAndLocale); 337 } 338 CLDRFile current = makeWithFallback(factory, locale, resolving); 339 CLDRFile old = makeWithFallback(oldFactory, locale, resolving); 340 if (!locale.equals("root") && current.getLocaleID().equals("root") && old.getLocaleID().equals("root")) { 341 continue; 342 } 343 if (old == EMPTY_CLDR && current == EMPTY_CLDR) { 344 continue; 345 } 346 paths.clear(); 347 for (String path : current.fullIterable()) { 348 paths.add(path); 349 } 350 for (String path : old.fullIterable()) { 351 paths.add(path); 352 } 353 354 Output<String> reformattedValue = new Output<String>(); 355 Output<Boolean> hasReformattedValue = new Output<Boolean>(); 356 357 for (String path : paths) { 358 if (path.startsWith("//ldml/identity") 359 || path.endsWith("/alias") 360 || path.startsWith("//ldml/segmentations") // do later 361 || path.startsWith("//ldml/rbnf") // do later 362 ) { 363 continue; 364 } 365 if (path.contains("/tRule")) { 366 int debug = 0; 367 } 368 PathHeader ph = getPathHeader(path); 369 if (ph == null) { 370 continue; 371 } 372 373 String oldValue = null; 374 String currentValue = null; 375 376 { 377 String sourceLocaleCurrent = current.getSourceLocaleID(path, currentStatus); 378 String sourceLocaleOld = getReformattedPath(oldStatus, old, path, reformattedValue, hasReformattedValue); 379 380 // filter out stuff that differs at a higher level 381 if (!sourceLocaleCurrent.equals(locale) 382 && !sourceLocaleOld.equals(locale)) { 383 continue; 384 } 385 if (!path.equals(currentStatus.pathWhereFound) 386 && !path.equals(oldStatus.pathWhereFound)) { 387 continue; 388 } 389 // fix some incorrect cases? 390 391 currentValue = current.getStringValue(path); 392 oldValue = hasReformattedValue.value ? reformattedValue.value : old.getStringValue(path); 393 } 394 // handle non-distinguishing attributes 395 addPathDiff(sourceDir, old, current, locale, ph, diff); 396 397 addValueDiff(sourceDir, oldValue, currentValue, locale, ph, diff, diffAll); 398 } 399 } 400 } 401 writeDiffs(anchors, base, diff, tsvFile, counts); 402 diff.clear(); 403 } 404 writeDiffs(anchors, diffAll); 405 406 writeCounter(tsvCountFile, "Count", counts); 407 tsvFile.println("# EOF"); 408 tsvCountFile.println("# EOF"); 409 } 410 411 } 412 getReformattedPath(Status oldStatus, CLDRFile old, String path, Output<String> value, Output<Boolean> hasReformattedValue)413 private String getReformattedPath(Status oldStatus, CLDRFile old, String path, Output<String> value, Output<Boolean> hasReformattedValue) { 414 if (SKIP_REFORMAT_ANNOTATIONS || !path.startsWith("//ldml/annotations/")) { 415 hasReformattedValue.value = Boolean.FALSE; 416 return old.getSourceLocaleID(path, oldStatus); 417 } 418 // OLD: <annotation cp='[]' tts='grinning face'>face; grin</annotation> 419 // NEW: <annotation cp="">face | grin</annotation> 420 // <annotation cp="" type="tts">grinning face</annotation> 421 // from the NEW paths, get the OLD values 422 XPathParts parts = XPathParts.getInstance(path); 423 boolean isTts = parts.getAttributeValue(-1, "type") != null; 424 if (isTts) { 425 parts.removeAttribute(-1, "type"); 426 } 427 String cp = parts.getAttributeValue(-1, "cp"); 428 parts.setAttribute(-1, "cp", "[" + cp + "]"); 429 430 String oldStylePath = parts.toString(); 431 String temp = old.getStringValue(oldStylePath); 432 if (temp == null) { 433 hasReformattedValue.value = Boolean.FALSE; 434 } else if (isTts) { 435 String temp2 = old.getFullXPath(oldStylePath); 436 value.value = XPathParts.getInstance(temp2).getAttributeValue(-1, "tts"); 437 hasReformattedValue.value = Boolean.TRUE; 438 } else { 439 value.value = temp.replaceAll("\\s*;\\s*", " | "); 440 hasReformattedValue.value = Boolean.TRUE; 441 } 442 return old.getSourceLocaleID(oldStylePath, oldStatus); 443 } 444 445 PathStarrer starrer = new PathStarrer().setSubstitutionPattern("%A"); 446 getPathHeader(String path)447 private PathHeader getPathHeader(String path) { 448 try { 449 PathHeader ph = phf.fromPath(path); 450 if (ph.getPageId() == PageId.Unknown) { 451 String star = starrer.set(path); 452 badHeaders.add(star); 453 return null; 454 } 455 return ph; 456 } catch (Exception e) { 457 String star = starrer.set(path); 458 badHeaders.add(star); 459 // System.err.println("Skipping path with bad PathHeader: " + path); 460 return null; 461 } 462 } 463 makeWithFallback(Factory oldFactory, String locale, boolean resolving)464 private CLDRFile makeWithFallback(Factory oldFactory, String locale, boolean resolving) { 465 if (oldFactory == null) { 466 return EMPTY_CLDR; 467 } 468 CLDRFile old; 469 String oldLocale = locale; 470 while (true) { // fall back for old, maybe to root 471 try { 472 old = oldFactory.make(oldLocale, resolving); 473 break; 474 } catch (Exception e) { 475 oldLocale = LocaleIDParser.getParent(oldLocale); 476 if (oldLocale == null) { 477 return EMPTY_CLDR; 478 } 479 } 480 } 481 return old; 482 } 483 addPathDiff(File sourceDir, CLDRFile old, CLDRFile current, String locale, PathHeader ph, Set<PathDiff> diff2)484 private void addPathDiff(File sourceDir, CLDRFile old, CLDRFile current, String locale, PathHeader ph, Set<PathDiff> diff2) { 485 String path = ph.getOriginalPath(); 486 String fullPathCurrent = current.getFullXPath(path); 487 String fullPathOld = old.getFullXPath(path); 488 if (Objects.equals(fullPathCurrent, fullPathOld)) { 489 return; 490 } 491 XPathParts pathPlain = new XPathParts().set(path); 492 XPathParts pathCurrent = fullPathCurrent == null ? pathPlain : new XPathParts().set(fullPathCurrent); 493 XPathParts pathOld = fullPathOld == null ? pathPlain : new XPathParts().set(fullPathOld); 494 TreeSet<String> fullAttributes = null; 495 int size = pathCurrent.size(); 496 String parentAndName = parentAndName(sourceDir, locale); 497 for (int elementIndex = 0; elementIndex < size; ++elementIndex) { // will have same size 498 Collection<String> distinguishing = pathPlain.getAttributeKeys(elementIndex); 499 Collection<String> attributesCurrent = pathCurrent.getAttributeKeys(elementIndex); 500 Collection<String> attributesOld = pathCurrent.getAttributeKeys(elementIndex); 501 if (attributesCurrent.isEmpty() && attributesOld.isEmpty()) { 502 continue; 503 } 504 if (fullAttributes == null) { 505 fullAttributes = new TreeSet<String>(); 506 } else { 507 fullAttributes.clear(); 508 } 509 fullAttributes.addAll(attributesCurrent); 510 fullAttributes.addAll(attributesOld); 511 fullAttributes.removeAll(distinguishing); 512 fullAttributes.removeAll(DONT_CARE); 513 514 // at this point we only have non-distinguishing 515 for (String attribute : fullAttributes) { 516 String attributeValueOld = pathOld.getAttributeValue(elementIndex, attribute); 517 String attributeValueCurrent = pathCurrent.getAttributeValue(elementIndex, attribute); 518 if (Objects.equals(attributeValueOld, attributeValueCurrent)) { 519 addChange(parentAndName, ChangeType.same, 1); 520 continue; 521 } 522 addChange(parentAndName, ChangeType.get(attributeValueOld, attributeValueCurrent), 1); 523 524 PathDiff row = new PathDiff( 525 locale, 526 new PathHeaderSegment(ph, size - elementIndex - 1, attribute), 527 attributeValueOld, 528 attributeValueCurrent); 529 if (DEBUG) { 530 System.out.println(row); 531 } 532 diff2.add(row); 533 } 534 } 535 } 536 parentAndName(File sourceDir, String locale)537 private String parentAndName(File sourceDir, String locale) { 538 return sourceDir.getName() + "/" + locale + ".xml"; 539 } 540 addValueDiff(File sourceDir, String valueOld, String valueCurrent, String locale, PathHeader ph, Set<PathDiff> diff, Relation<PathHeader, String> diffAll)541 private void addValueDiff(File sourceDir, String valueOld, String valueCurrent, String locale, PathHeader ph, Set<PathDiff> diff, 542 Relation<PathHeader, String> diffAll) { 543 // handle stuff that can be split specially 544 Splitter splitter = getSplitter(ph.getOriginalPath(), valueOld, valueCurrent); 545 int count = 1; 546 String parentAndName = parentAndName(sourceDir, locale); 547 if (Objects.equals(valueCurrent, valueOld)) { 548 if (splitter != null && valueCurrent != null) { 549 count = splitHandlingNull(splitter, valueCurrent).size(); 550 } 551 addChange(parentAndName, ChangeType.same, count); 552 } else { 553 if (splitter != null) { 554 List<String> setOld = splitHandlingNull(splitter, valueOld); 555 List<String> setNew = splitHandlingNull(splitter, valueCurrent); 556 int[] sameAndNotInSecond = new int[2]; 557 valueOld = getFilteredValue(setOld, setNew, sameAndNotInSecond); 558 addChange(parentAndName, ChangeType.same, sameAndNotInSecond[0]); 559 addChange(parentAndName, ChangeType.deleted, sameAndNotInSecond[1]); 560 sameAndNotInSecond[0] = sameAndNotInSecond[1] = 0; 561 valueCurrent = getFilteredValue(setNew, setOld, sameAndNotInSecond); 562 addChange(parentAndName, ChangeType.added, sameAndNotInSecond[1]); 563 } else { 564 addChange(parentAndName, ChangeType.get(valueOld, valueCurrent), count); 565 } 566 PathDiff row = new PathDiff(locale, new PathHeaderSegment(ph, -1, ""), valueOld, valueCurrent); 567 diff.add(row); 568 diffAll.put(ph, locale); 569 } 570 } 571 splitHandlingNull(Splitter splitter, String value)572 private List<String> splitHandlingNull(Splitter splitter, String value) { 573 return value == null ? null : splitter.splitToList(value); 574 } 575 getSplitter(String path, String valueOld, String valueCurrent)576 private Splitter getSplitter(String path, String valueOld, String valueCurrent) { 577 if (path.contains("/annotation") && !path.contains("tts")) { 578 return DtdData.BAR_SPLITTER; 579 } else if (valueOld != null && valueOld.contains("\n") || valueCurrent != null && valueCurrent.contains("\n")) { 580 return DtdData.CR_SPLITTER; 581 } else { 582 return null; 583 } 584 } 585 586 /** 587 * Return string with all lines from linesToRemove removed 588 * @param toGetStringFor 589 * @param linesToRemove 590 * @return 591 */ getFilteredValue(Collection<String> toGetStringFor, Collection<String> linesToRemove, int[] sameAndDiff)592 private String getFilteredValue(Collection<String> toGetStringFor, Collection<String> linesToRemove, 593 int[] sameAndDiff) { 594 if (toGetStringFor == null) { 595 return null; 596 } 597 StringBuilder buf = new StringBuilder(); 598 Set<String> toRemove = linesToRemove == null ? Collections.emptySet() : new HashSet<>(linesToRemove); 599 boolean removed = false; 600 for (String old : toGetStringFor) { 601 if (toRemove.contains(old)) { 602 removed = true; 603 sameAndDiff[0]++; 604 } else { 605 sameAndDiff[1]++; 606 if (removed) { 607 buf.append("…\n"); 608 removed = false; 609 } 610 buf.append(old).append('\n'); 611 } 612 } 613 if (removed) { 614 buf.append("…"); 615 } else if (buf.length() > 0) { 616 buf.setLength(buf.length() - 1); // remove final \n 617 } 618 return buf.toString(); 619 } 620 writeDiffs(Anchors anchors, String file, String title, Multimap<PathHeader, String> bcp, PrintWriter tsvFile)621 private void writeDiffs(Anchors anchors, String file, String title, Multimap<PathHeader, String> bcp, PrintWriter tsvFile) { 622 TablePrinter tablePrinter = new TablePrinter() 623 .addColumn("Section", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 624 .addColumn("Page", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true)//.setRepeatDivider(true) 625 .addColumn("Header", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 626 .addColumn("Code", "class='source'", null, "class='source'", false) 627 .addColumn("Old", "class='target'", null, "class='target'", false) // width='20%' 628 .addColumn("New", "class='target'", null, "class='target'", false); // width='20%' 629 PathHeader ph1 = phf.fromPath("//supplementalData/metadata/alias/subdivisionAlias[@type=\"TW-TXQ\"]/_reason"); 630 PathHeader ph2 = phf.fromPath("//supplementalData/metadata/alias/subdivisionAlias[@type=\"LA-XN\"]/_replacement"); 631 ph1.compareTo(ph2); 632 for (Entry<PathHeader, Collection<String>> entry : bcp.asMap().entrySet()) { 633 PathHeader ph = entry.getKey(); 634 if (ph.getPageId() == DEBUG_PAGE_ID) { 635 System.out.println(ph + "\t" + ph.getOriginalPath()); 636 } 637 for (String value : entry.getValue()) { 638 String[] oldNew = value.split(SEP); 639 tablePrinter.addRow() 640 .addCell(ph.getSectionId()) 641 .addCell(ph.getPageId()) 642 .addCell(ph.getHeader()) 643 .addCell(ph.getCode()) 644 .addCell(oldNew[0]) 645 .addCell(oldNew[1]) 646 .finishRow(); 647 } 648 } 649 writeTable(anchors, file, tablePrinter, title, tsvFile); 650 } 651 writeDiffs(Anchors anchors, Relation<PathHeader, String> diffAll)652 private void writeDiffs(Anchors anchors, Relation<PathHeader, String> diffAll) { 653 TablePrinter tablePrinter = new TablePrinter() 654 .addColumn("Section", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 655 .addColumn("Page", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 656 .addColumn("Header", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 657 .addColumn("Code", "class='source'", null, "class='source'", true) 658 .addColumn("Locales where different", "class='target'", null, "class='target'", true); 659 for (Entry<PathHeader, Set<String>> row : diffAll.keyValuesSet()) { 660 PathHeader ph = row.getKey(); 661 Set<String> locales = row.getValue(); 662 tablePrinter.addRow() 663 .addCell(ph.getSectionId()) 664 .addCell(ph.getPageId()) 665 .addCell(ph.getHeader()) 666 .addCell(ph.getCode()) 667 .addCell(CollectionUtilities.join(locales, " ")) 668 .finishRow(); 669 } 670 } 671 writeDiffs(Anchors anchors, String file, Set<PathDiff> diff, PrintWriter tsvFile, Counter<PathHeader> counts)672 private void writeDiffs(Anchors anchors, String file, Set<PathDiff> diff, PrintWriter tsvFile, Counter<PathHeader> counts) { 673 if (diff.isEmpty()) { 674 return; 675 } 676 TablePrinter tablePrinter = new TablePrinter() 677 .addColumn("Section", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 678 .addColumn("Page", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 679 .addColumn("Header", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true) 680 .addColumn("Code", "class='source'", null, "class='source'", true) 681 .addColumn("Locale", "class='source'", null, "class='source'", true) 682 .addColumn("Old", "class='target'", null, "class='target'", true) // width='20%' 683 .addColumn("New", "class='target'", null, "class='target'", true) // width='20%' 684 .addColumn("Level", "class='target'", null, "class='target'", true); 685 686 for (PathDiff row : diff) { 687 PathHeaderSegment phs = row.get0(); 688 counts.add(phs.get0(), 1); 689 String locale = row.get1(); 690 String oldValue = row.get2(); 691 String currentValue = row.get3(); 692 693 PathHeader ph = phs.get0(); 694 Integer pathIndex = phs.get1(); 695 String attribute = phs.get2(); 696 String specialCode = ph.getCode(); 697 698 if (!attribute.isEmpty()) { 699 specialCode += "_" + attribute; 700 if (pathIndex != 0) { 701 specialCode += "|" + pathIndex; 702 } 703 } 704 Level coverageLevel = SUPPLEMENTAL_DATA_INFO.getCoverageLevel(ph.getOriginalPath(), locale); 705 String fixedOldValue = oldValue == null ? "▷missing◁" : TransliteratorUtilities.toHTML.transform(oldValue); 706 String fixedNewValue = currentValue == null ? "▷removed◁" : TransliteratorUtilities.toHTML.transform(currentValue); 707 708 tablePrinter.addRow() 709 .addCell(ph.getSectionId()) 710 .addCell(ph.getPageId()) 711 .addCell(ph.getHeader()) 712 .addCell(specialCode) 713 .addCell(locale) 714 .addCell(fixedOldValue) 715 .addCell(fixedNewValue) 716 .addCell(coverageLevel) 717 .finishRow(); 718 719 } 720 writeTable(anchors, file, tablePrinter, ENGLISH.getName(file) + " Delta", tsvFile); 721 722 diff.clear(); 723 } 724 725 private class ChartDeltaSub extends Chart { 726 String title; 727 String file; 728 private TablePrinter tablePrinter; 729 private PrintWriter tsvFile; 730 ChartDeltaSub(String title, String file, TablePrinter tablePrinter, PrintWriter tsvFile)731 public ChartDeltaSub(String title, String file, TablePrinter tablePrinter, PrintWriter tsvFile) { 732 super(); 733 this.title = title; 734 this.file = file; 735 this.tablePrinter = tablePrinter; 736 this.tsvFile = tsvFile; 737 } 738 739 @Override getDirectory()740 public String getDirectory() { 741 return DIR; 742 } 743 744 @Override getShowDate()745 public boolean getShowDate() { 746 return false; 747 } 748 749 @Override getTitle()750 public String getTitle() { 751 return title; 752 } 753 754 @Override getFileName()755 public String getFileName() { 756 return file; 757 } 758 759 @Override getExplanation()760 public String getExplanation() { 761 return "<p>Lists data fields that differ from the last version." 762 + " Inherited differences in locales are suppressed, except where the source locales are different. " 763 + " The collations and metadata still have a raw format." 764 + " The rbnf, segmentations, and annotations are not yet included.<p>"; 765 } 766 767 @Override writeContents(FormattedFileWriter pw)768 public void writeContents(FormattedFileWriter pw) throws IOException { 769 pw.write(tablePrinter.toTable()); 770 tablePrinter.toTsv(tsvFile); 771 } 772 } 773 writeTable(Anchors anchors, String file, TablePrinter tablePrinter, String title, PrintWriter tsvFile)774 private void writeTable(Anchors anchors, String file, TablePrinter tablePrinter, String title, PrintWriter tsvFile) { 775 ChartDeltaSub chartDeltaSub = new ChartDeltaSub(title, file, tablePrinter, tsvFile); 776 chartDeltaSub.writeChart(anchors); 777 } 778 writeNonLdmlPlain(Anchors anchors)779 public void writeNonLdmlPlain(Anchors anchors) throws IOException { 780 try (PrintWriter tsvFile = FileUtilities.openUTF8Writer(getTsvDir(DIR, DIR_NAME), DIR_NAME + "_supp.tsv"); 781 PrintWriter tsvCountFile = FileUtilities.openUTF8Writer(getTsvDir(DIR, DIR_NAME), DIR_NAME + "_supp_count.tsv"); 782 ) { 783 tsvFile.println("# Section\tPage\tHeader\tCode\tOld\tNew"); 784 785 Multimap<PathHeader, String> bcp = TreeMultimap.create(); 786 Multimap<PathHeader, String> supplemental = TreeMultimap.create(); 787 Multimap<PathHeader, String> transforms = TreeMultimap.create(); 788 789 Counter<PathHeader> countSame = new Counter<>(); 790 Counter<PathHeader> countAdded = new Counter<>(); 791 Counter<PathHeader> countDeleted = new Counter<>(); 792 793 for (String dir : new File(CLDRPaths.BASE_DIRECTORY + "common/").list()) { 794 if (DtdType.ldml.directories.contains(dir) 795 || dir.equals(".DS_Store") 796 || dir.equals("dtd") // TODO as flat files 797 || dir.equals("properties") // TODO as flat files 798 || dir.equals("uca") // TODO as flat files 799 ) { 800 continue; 801 } 802 File dir1 = new File(LAST_ARCHIVE_DIRECTORY + "common/" + dir); 803 File dir2 = new File(CLDRPaths.BASE_DIRECTORY + "common/" + dir); 804 805 for (String file : dir2.list()) { 806 if (!file.endsWith(".xml")) { 807 continue; 808 } 809 String parentAndFile = dir + "/" + file; 810 String base = file.substring(0, file.length() - 4); 811 if (fileFilter != null && !fileFilter.reset(dir + "/" + base).find()) { 812 if (verbose) { 813 System.out.println("SKIPPING: " + dir + "/" + base); 814 } 815 continue; 816 } 817 818 if (verbose) { 819 System.out.println(file); 820 } 821 Relation<PathHeader, String> contents1 = fillData(dir1.toString() + "/", file); 822 Relation<PathHeader, String> contents2 = fillData(dir2.toString() + "/", file); 823 824 Set<PathHeader> keys = new TreeSet<PathHeader>(CldrUtility.ifNull(contents1.keySet(), Collections.<PathHeader> emptySet())); 825 keys.addAll(CldrUtility.ifNull(contents2.keySet(), Collections.<PathHeader> emptySet())); 826 DtdType dtdType = null; 827 for (PathHeader key : keys) { 828 String originalPath = key.getOriginalPath(); 829 if (originalPath.contains("/paradigmLocales")) { 830 int debug = 0; 831 } 832 boolean isTransform = originalPath.contains("/tRule"); 833 if (dtdType == null) { 834 dtdType = DtdType.fromPath(originalPath); 835 } 836 Multimap<PathHeader, String> target = dtdType == DtdType.ldmlBCP47 ? bcp 837 : isTransform ? transforms 838 : supplemental; 839 Set<String> set1 = contents1.get(key); 840 Set<String> set2 = contents2.get(key); 841 842 if (Objects.equals(set1, set2)) { 843 if (file.equals(DEBUG_FILE)) { // for debugging 844 System.out.println("**Same: " + key + "\t" + set1); 845 } 846 addChange(parentAndFile, ChangeType.same, set1.size()); 847 countSame.add(key, 1); 848 continue; 849 } 850 if (set1 == null) { 851 addChange(parentAndFile, ChangeType.added, set2.size()); 852 for (String s : set2) { 853 addRow(target, key, "▷missing◁", s); 854 countAdded.add(key, 1); 855 } 856 } else if (set2 == null) { 857 addChange(parentAndFile, ChangeType.deleted, set1.size()); 858 for (String s : set1) { 859 addRow(target, key, s, "▷removed◁"); 860 countDeleted.add(key, 1); 861 } 862 } else { 863 Set<String> s1M2 = set1; 864 Set<String> s2M1 = set2; 865 // Set<String> s1M2 = new LinkedHashSet<>(set1); 866 // s1M2.removeAll(set2); 867 // Set<String> s2M1 = new LinkedHashSet<>(set2); 868 // s2M1.removeAll(set1); 869 if (s1M2.isEmpty()) { 870 addRow(target, key, "▷missing◁", CollectionUtilities.join(s2M1, ", ")); 871 addChange(parentAndFile, ChangeType.added, s2M1.size()); 872 countAdded.add(key, 1); 873 } else if (s2M1.isEmpty()) { 874 addRow(target, key, CollectionUtilities.join(s1M2, ", "), "▷removed◁"); 875 addChange(parentAndFile, ChangeType.deleted, s1M2.size()); 876 countDeleted.add(key, 1); 877 } else { 878 String valueOld; 879 String valueCurrent; 880 881 int[] sameAndNotInSecond = new int[2]; 882 valueOld = getFilteredValue(s1M2, s1M2, sameAndNotInSecond); 883 addChange(parentAndFile, ChangeType.same, sameAndNotInSecond[0]); 884 countSame.add(key, 1); 885 addChange(parentAndFile, ChangeType.deleted, sameAndNotInSecond[1]); 886 sameAndNotInSecond[1] = 0; 887 countDeleted.add(key, 1); 888 valueCurrent = getFilteredValue(s2M1, s1M2, sameAndNotInSecond); 889 addChange(parentAndFile, ChangeType.added, sameAndNotInSecond[1]); 890 addRow(target, key, valueOld, valueCurrent); 891 countAdded.add(key, 1); 892 } 893 } 894 } 895 } 896 } 897 // private void writeDiffs(Anchors anchors, String file, String title, Relation<PathHeader, String> bcp) { 898 899 writeDiffs(anchors, "bcp47", "¤¤BCP47 Delta", bcp, tsvFile); 900 writeDiffs(anchors, "supplemental-data", "¤¤Supplemental Delta", supplemental, tsvFile); 901 writeDiffs(anchors, "transforms", "¤¤Transforms Delta", transforms, tsvFile); 902 903 writeCounter(tsvCountFile, "CountSame", countSame); 904 tsvCountFile.println(); 905 writeCounter(tsvCountFile, "CountAdded", countAdded); 906 tsvCountFile.println(); 907 writeCounter(tsvCountFile, "CountDeleted", countDeleted); 908 909 tsvFile.println("# EOF"); 910 tsvCountFile.println("# EOF"); 911 } 912 } 913 writeCounter(PrintWriter tsvFile, String title, Counter<PathHeader> countDeleted)914 private void writeCounter(PrintWriter tsvFile, String title, Counter<PathHeader> countDeleted) { 915 tsvFile.append("# " 916 + title 917 + "\tPathHeader\n\n"); 918 for (R2<Long, PathHeader> entry : countDeleted.getEntrySetSortedByCount(false, null)) { 919 tsvFile.println(entry.get0() + "\t" + entry.get1()); 920 } 921 } 922 addRow(Multimap<PathHeader, String> target, PathHeader key, String oldItem, String newItem)923 private void addRow(Multimap<PathHeader, String> target, PathHeader key, String oldItem, String newItem) { 924 if (oldItem.isEmpty() || newItem.isEmpty()) { 925 throw new IllegalArgumentException(); 926 } 927 target.put(key, oldItem + SEP + newItem); 928 } 929 930 // private static final Splitter ONHYPHEN = Splitter.on('-'); 931 // private final LanguageTagParser lparser = new LanguageTagParser(); 932 // private final Map<LstrType, Map<Validity.Status, Set<String>>> validity = Validity.getInstance().getData(); 933 // private final Set<String> regularLanguage = validity.get(LstrType.language).get(Validity.Status.regular); 934 // 935 // private String name(String key) { 936 // // eo-eo_FONIPA 937 // // Latin-ASCII 938 // int i = 0; 939 // try { 940 // StringBuilder sb = new StringBuilder(); 941 // for (String part : ONHYPHEN.split(key)) { 942 // lparser.set(part); 943 // String base = lparser.getLanguage(); 944 // int script = UScript.getCodeFromName(base); 945 // if (script != UScript.INVALID_CODE) { 946 // part = UScript.getName(script); 947 // } else if (regularLanguage.contains(base)) { 948 // part = ENGLISH.getName(part); 949 // } 950 // if (i != 0) { 951 // sb.append('-'); 952 // } 953 // sb.append(part); 954 // ++i; 955 // } 956 // return sb.toString(); 957 // } catch (Exception e) { 958 // // TODO fix this to handle all cases 959 // return key; 960 // } 961 // } 962 // 963 // private String removeStart(String key, String... string) { 964 // for (String start : string) { 965 // if (key.startsWith(start)) { 966 // return "…" + key.substring(start.length()); 967 // } 968 // } 969 // return key; 970 // } 971 // fillData(String directory, String file)972 public Relation<PathHeader, String> fillData(String directory, String file) { 973 Relation<PathHeader, String> results = Relation.of(new TreeMap<PathHeader, Set<String>>(), TreeSet.class); 974 975 List<Pair<String, String>> contents1; 976 try { 977 contents1 = XMLFileReader.loadPathValues(directory + file, new ArrayList<Pair<String, String>>(), true); 978 } catch (Exception e) { 979 return results; 980 } 981 DtdType dtdType = null; 982 DtdData dtdData = null; 983 Multimap<String, String> extras = TreeMultimap.create(); 984 985 for (Pair<String, String> s : contents1) { 986 String path = s.getFirst(); 987 if (path.contains("/collat")) { 988 int debug = 0; 989 } 990 991 String value = s.getSecond(); 992 if (dtdType == null) { 993 dtdType = DtdType.fromPath(path); 994 dtdData = DtdData.getInstance(dtdType); 995 } 996 XPathParts pathPlain = XPathParts.getFrozenInstance(path); 997 if (dtdData.isMetadata(pathPlain)) { 998 continue; 999 } 1000 Set<String> pathForValues = dtdData.getRegularizedPaths(pathPlain, extras); 1001 if (pathForValues != null) { 1002 for (String pathForValue : pathForValues) { 1003 PathHeader pathHeader = phf.fromPath(pathForValue); 1004 Splitter splitter = getValueSplitter(pathPlain); 1005 for (String line : splitter.split(value)) { 1006 // special case # in transforms 1007 if (isComment(pathPlain, line)) { 1008 continue; 1009 } 1010 results.put(pathHeader, line); 1011 } 1012 } 1013 } 1014 for (Entry<String, Collection<String>> entry : extras.asMap().entrySet()) { 1015 final String extraPath = entry.getKey(); 1016 final PathHeader pathHeaderExtra = phf.fromPath(extraPath); 1017 final Collection<String> extraValue = entry.getValue(); 1018 if (isExtraSplit(extraPath)) { 1019 for (String items : extraValue) { 1020 results.putAll(pathHeaderExtra, DtdData.SPACE_SPLITTER.splitToList(items)); 1021 } 1022 } else { 1023 results.putAll(pathHeaderExtra, extraValue); 1024 } 1025 } 1026 if (pathForValues == null && !value.isEmpty()) { 1027 System.err.println("Shouldn't happen"); 1028 } 1029 } 1030 return results; 1031 } 1032 isExtraSplit(String extraPath)1033 private boolean isExtraSplit(String extraPath) { 1034 if (extraPath.endsWith("/_type") && extraPath.startsWith("//supplementalData/metaZones/mapTimezones")) { 1035 return true; 1036 } 1037 return false; 1038 } 1039 getValueSplitter(XPathParts pathPlain)1040 public Splitter getValueSplitter(XPathParts pathPlain) { 1041 return DtdData.getValueSplitter(pathPlain); 1042 } 1043 isComment(XPathParts pathPlain, String line)1044 public static boolean isComment(XPathParts pathPlain, String line) { 1045 if (pathPlain.contains("transform")) { 1046 if (line.startsWith("#")) { 1047 return true; 1048 } 1049 } 1050 return false; 1051 } 1052 1053 // private static final Set<String> SKIP_ATTRIBUTE = new HashSet<String>(Arrays.asList("references")); 1054 // 1055 // private static void checkedPut(Map<String, String> contentsA, String path2, String value) { 1056 // String old = contentsA.get(path2); 1057 // if (old != null) { 1058 // if (old.equals(value)) { 1059 // return; 1060 // } 1061 // value = old + "\n" + value; 1062 // } 1063 // contentsA.put(path2, value); 1064 // } 1065 // 1066 // static final ChainedMap.M4<DtdType, String, String, Boolean> FIXED_DISTINGUISHING = ChainedMap.of( 1067 // new HashMap<DtdType, Object>(), 1068 // new HashMap<String, Object>(), 1069 // new HashMap<String, Object>(), 1070 // Boolean.class); 1071 // 1072 // static final ChainedMap.M3<DtdType, String, Boolean> FIXED_ORDERING = ChainedMap.of( 1073 // new HashMap<DtdType, Object>(), 1074 // new HashMap<String, Object>(), 1075 // Boolean.class); 1076 // 1077 // static { 1078 // //<key name="ca" description="Calendar algorithm key" alias="calendar"> 1079 // //<type name="buddhist" description="Thai Buddhist calendar"/> 1080 // FIXED_DISTINGUISHING.put(DtdType.ldmlBCP47, "key", "alias", false); 1081 // 1082 // FIXED_ORDERING.put(DtdType.supplementalData, "substitute", true); 1083 // 1084 // //<approvalRequirement votes="20" locales="Cldr:modern" paths="//ldml/numbers/symbols[^/]++/(decimal|group|(plus|minus)Sign)"/> 1085 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "approvalRequirement", "locales", true); 1086 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "approvalRequirement", "paths", true); 1087 // // approvalRequirement should be ordered, but for our comparison, simpler to override 1088 // 1089 // //<coverageVariable key="%acctPattern" value="[@type='accounting']/pattern[@type='standard']"/> 1090 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "coverageVariable", "key", true); 1091 // FIXED_ORDERING.put(DtdType.supplementalData, "coverageVariable", false); 1092 // 1093 // //<coverageLevel inLanguage="%phonebookCollationLanguages" value="minimal" match="localeDisplayNames/types/type[@type='phonebook'][@key='collation']"/> 1094 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "coverageLevel", "inLanguage", true); 1095 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "coverageLevel", "inScript", true); 1096 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "coverageLevel", "inTerritory", true); 1097 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "coverageLevel", "match", true); 1098 // FIXED_ORDERING.put(DtdType.supplementalData, "coverageLevel", false); // this should be true, but for our comparison, simpler to override 1099 // 1100 //// <!ATTLIST dayPeriodRule type NMTOKEN #REQUIRED > 1101 //// <!ATTLIST dayPeriodRule at NMTOKEN #IMPLIED > 1102 //// <!ATTLIST dayPeriodRule after NMTOKEN #IMPLIED > 1103 //// <!ATTLIST dayPeriodRule before NMTOKEN #IMPLIED > 1104 //// <!ATTLIST dayPeriodRule from NMTOKEN #IMPLIED > 1105 //// <!ATTLIST dayPeriodRule to NMTOKEN #IMPLIED > 1106 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "type", false); 1107 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "at", true); 1108 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "after", true); 1109 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "before", true); 1110 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "from", true); 1111 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "dayPeriodRule", "to", true); 1112 // 1113 //// <personList type="neutral" locales="af bn bg da de en et eu fa fil fi gu hu id ja kn ko ml ms no sv sw ta te th tr vi zu" /> 1114 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "personList", "type", false); 1115 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "personList", "locales", true); 1116 // 1117 // // <languageMatch desired="am_*_*" supported="en_GB" percent="90" oneway="true" /> <!-- fix ICU for en_GB --> 1118 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "languageMatch", "desired", true); 1119 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "languageMatch", "supported", true); 1120 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "languageMatch", "oneway", true); 1121 // FIXED_ORDERING.put(DtdType.supplementalData, "languageMatch", false); // this should be true, but for our comparison, simpler to override 1122 // 1123 // // <usesMetazone to="1979-10-25 23:00" from="1977-10-20 23:00" mzone="Europe_Central"/> 1124 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "usesMetazone", "from", true); 1125 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "usesMetazone", "to", true); 1126 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "usesMetazone", "mzone", false); 1127 // 1128 //// <!ATTLIST numberingSystem type ( numeric | algorithmic ) #REQUIRED > 1129 //// <!ATTLIST numberingSystem id NMTOKEN #REQUIRED > 1130 //// <!ATTLIST numberingSystem radix NMTOKEN #IMPLIED > 1131 //// <!ATTLIST numberingSystem digits CDATA #IMPLIED > 1132 //// <!ATTLIST numberingSystem rules CDATA #IMPLIED > 1133 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "numberingSystem", "type", false); 1134 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "numberingSystem", "radix", false); 1135 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "numberingSystem", "digits", false); 1136 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "numberingSystem", "rules", false); 1137 // 1138 // FIXED_ORDERING.put(DtdType.supplementalData, "pluralRule", false); // this should be true, but for our comparison, simpler to override 1139 // 1140 // //pluralRanges locales 1141 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "pluralRanges", "locales", true); 1142 // 1143 // // <pluralRange start="one" end="one" result="one"/> 1144 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "pluralRange", "start", true); 1145 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "pluralRange", "end", true); 1146 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "pluralRange", "result", false); 1147 // 1148 //// <territory type="AC" gdp="35200000" literacyPercent="99" population="940"> <!--Ascension Island--> 1149 //// <languagePopulation type="en" populationPercent="99" references="R1020"/> <!--English--> 1150 // 1151 // FIXED_DISTINGUISHING.put(DtdType.ldml, "rbnfrule", "value", false); 1152 // FIXED_ORDERING.put(DtdType.ldml, "ruleset", false); // this should be true, but for our comparison, simpler to override 1153 // FIXED_ORDERING.put(DtdType.ldml, "rbnfrule", false); // this should be true, but for our comparison, simpler to override 1154 // 1155 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "transform", "source", true); 1156 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "transform", "target", true); 1157 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "transform", "direction", true); 1158 // FIXED_DISTINGUISHING.put(DtdType.supplementalData, "transform", "variant", true); 1159 // FIXED_ORDERING.put(DtdType.supplementalData, "tRule", false); 1160 // 1161 // System.out.println("Current Overrides for Distinguishing"); 1162 // for (R4<DtdType, String, String, Boolean> entry : FIXED_DISTINGUISHING.rows()){ 1163 // DtdData dtdData = DtdData.getInstance(entry.get0()); 1164 // boolean actual = dtdData.isDistinguishing(entry.get1(), entry.get2()); 1165 // boolean fixed = entry.get3(); 1166 // if (actual != fixed) { 1167 // System.out.println("\tOverride to\t" + entry); 1168 // } else { 1169 // System.out.println("\tUnnecessary\t" + entry); 1170 // } 1171 // } 1172 // System.out.println("Current Overrides for Ordering"); 1173 // for (R3<DtdType, String, Boolean> entry : FIXED_ORDERING.rows()){ 1174 // DtdData dtdData = DtdData.getInstance(entry.get0()); 1175 // boolean actual = dtdData.isOrdered(entry.get1()); 1176 // boolean fixed = entry.get2(); 1177 // if (actual != fixed) { 1178 // System.out.println("\tOverride to\t" + entry); 1179 // } else { 1180 // System.out.println("\tUnnecessary\t" + entry); 1181 // } 1182 // } 1183 // 1184 // // FIXED_ORDERING.freeze(); Add to ChainedMap? 1185 // } 1186 1187 // private static boolean isFixedDistinguishing(DtdType dtdType, String element, String key) { 1188 // Boolean override = FIXED_DISTINGUISHING.get(dtdType, element, key); 1189 // if (override != null) { 1190 // return override; 1191 // } 1192 // return CLDRFile.isDistinguishing(dtdType, element, key); 1193 // } 1194 // 1195 // private static boolean shouldBeOrdered(DtdType dtdType, String element) { 1196 // Boolean override = FIXED_ORDERING.get(dtdType, element); 1197 // if (override != null) { 1198 // return override; 1199 // } 1200 // return CLDRFile.isOrdered(element, dtdType); 1201 // } 1202 1203 } 1204