1 package org.unicode.cldr.unittest; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.Collection; 8 import java.util.Collections; 9 import java.util.HashSet; 10 import java.util.Iterator; 11 import java.util.LinkedHashSet; 12 import java.util.List; 13 import java.util.Map.Entry; 14 import java.util.Set; 15 import java.util.TreeSet; 16 17 import org.unicode.cldr.util.CLDRConfig; 18 import org.unicode.cldr.util.CLDRPaths; 19 import org.unicode.cldr.util.DtdData; 20 import org.unicode.cldr.util.DtdData.Attribute; 21 import org.unicode.cldr.util.DtdData.Element; 22 import org.unicode.cldr.util.DtdData.ElementType; 23 import org.unicode.cldr.util.DtdType; 24 import org.unicode.cldr.util.MatchValue; 25 import org.unicode.cldr.util.MatchValue.EnumParser; 26 import org.unicode.cldr.util.Pair; 27 import org.unicode.cldr.util.SupplementalDataInfo; 28 import org.unicode.cldr.util.Validity; 29 import org.unicode.cldr.util.Validity.Status; 30 import org.unicode.cldr.util.XMLFileReader; 31 import org.unicode.cldr.util.XPathParts; 32 33 import com.google.common.base.Joiner; 34 import com.google.common.collect.Multimap; 35 import com.google.common.collect.TreeMultimap; 36 import com.ibm.icu.dev.test.TestFmwk; 37 38 public class TestDtdData extends TestFmwk { 39 private static final String COMMON_DIR = CLDRPaths.BASE_DIRECTORY + "common/"; 40 static CLDRConfig testInfo = CLDRConfig.getInstance(); 41 private static final SupplementalDataInfo SUPPLEMENTAL_DATA_INFO = testInfo 42 .getSupplementalDataInfo(); 43 main(String[] args)44 public static void main(String[] args) { 45 new TestDtdData().run(args); 46 } 47 TestRegularized()48 public void TestRegularized() { 49 String[][] tests = { 50 51 /* 52 * TODO: re-enable the first test or something like it. 53 * It began to fail as a result of copying dtdData in XPathParts.cloneAsThawed rather than always making it null. 54 * Reference: https://unicode.org/cldr/trac/ticket/12007 55 */ 56 // has a value & value attribute 57 // { "//supplementalData/plurals/pluralRanges[@locales=\"id ja\"]/pluralRange[@start=\"a\"][@end=\"b\"][@result=\"c\"]", 58 // "//supplementalData/plurals/pluralRanges[@locales=\"id\"]/pluralRange[@end=\"b\"][@start=\"a\"]/_result==c", 59 // "//supplementalData/plurals/pluralRanges[@locales=\"ja\"]/pluralRange[@end=\"b\"][@start=\"a\"]/_result==c" 60 // }, 61 62 { "//supplementalData/plurals[@type=\"cardinal\"]/pluralRules[@locales=\"am as bn\"]/pluralRule[@count=\"other\"]", 63 "//supplementalData/plurals[@type=\"cardinal\"]/pluralRules[@locales=\"am\"]/pluralRule[@count=\"other\"]==VALUE", 64 "//supplementalData/plurals[@type=\"cardinal\"]/pluralRules[@locales=\"as\"]/pluralRule[@count=\"other\"]==VALUE", 65 "//supplementalData/plurals[@type=\"cardinal\"]/pluralRules[@locales=\"bn\"]/pluralRule[@count=\"other\"]==VALUE" 66 }, 67 68 // no change 69 { "//supplementalData/primaryZones/primaryZone[@iso3166=\"CL\"]", 70 "//supplementalData/primaryZones/primaryZone[@iso3166=\"CL\"]==VALUE" 71 }, 72 73 // has only value attributes 74 { "//supplementalData/version[@number=\"$Revision: 12197 $\"][@cldrVersion=\"29\"][@unicodeVersion=\"8.0.0\"]", 75 "//supplementalData/version/_cldrVersion==29", 76 "//supplementalData/version/_unicodeVersion==8.0.0" 77 }, 78 79 // no change 80 { "//ldml/identity/language[@type=\"af\"]", 81 "//ldml/identity/language[@type=\"af\"]==VALUE" 82 }, 83 84 // // has a value & value attribute 85 // {"//ldml/annotations/annotation[@cp=\"[ߘ]\"][@tts=\"grinnikende gesig\"]", 86 // "//ldml/annotations/annotation[@cp=\"[ߘ]\"]", 87 // "//ldml/annotations/annotation_[@cp=\"[ߘ]\"]/_tts" 88 // }, 89 90 // has a value & value attribute 91 { "//ldml/rbnf/rulesetGrouping[@type=\"SpelloutRules\"]/ruleset[@type=\"2d-year\"][@access=\"private\"]/rbnfrule[@value=\"0\"]", 92 "//ldml/rbnf/rulesetGrouping[@type=\"SpelloutRules\"]/ruleset[@type=\"2d-year\"]/_access==private", 93 "//ldml/rbnf/rulesetGrouping[@type=\"SpelloutRules\"]/ruleset[@type=\"2d-year\"]/rbnfrule==VALUE", 94 "//ldml/rbnf/rulesetGrouping[@type=\"SpelloutRules\"]/ruleset[@type=\"2d-year\"]/rbnfrule_/_value==0" 95 }, 96 97 // has a value attribute 98 { "//ldmlBCP47/version[@number=\"$Revision: 12232 $\"][@cldrVersion=\"29\"]", 99 "//ldmlBCP47/version/_cldrVersion==29" 100 }, 101 102 // has a value & value attribute 103 { "//ldmlBCP47/keyword/key[@name=\"ca\"][@description=\"Calendar algorithm key\"][@deprecated=\"false\"][@alias=\"calendar\"][@valueType=\"incremental\"]/type[@name=\"chinese\"][@description=\"Traditional Chinese calendar\"][@deprecated=\"false\"]", 104 "//ldmlBCP47/keyword/key[@name=\"ca\"]/_alias==calendar", 105 "//ldmlBCP47/keyword/key[@name=\"ca\"]/_description==Calendar algorithm key", 106 "//ldmlBCP47/keyword/key[@name=\"ca\"]/_valueType==incremental", 107 "//ldmlBCP47/keyword/key[@name=\"ca\"]/type[@name=\"chinese\"]/_description==Traditional Chinese calendar", 108 "//ldmlBCP47/keyword/key[@name=\"ca\"]/_deprecated==false", 109 "//ldmlBCP47/keyword/key[@name=\"ca\"]/type[@name=\"chinese\"]/_deprecated==false" 110 }, 111 112 }; 113 Multimap<String, String> extras = TreeMultimap.create(); 114 115 for (String[] test : tests) { 116 final String path = test[0]; 117 final Set<String> expected = new TreeSet<>(Arrays.asList(test).subList(1, Arrays.asList(test).size())); 118 DtdData dtdData = DtdData.getInstance(DtdType.fromPath(path)); 119 120 Set<String> actual = new TreeSet<>(); 121 XPathParts parts = XPathParts.getFrozenInstance(path); 122 Set<String> pathForValues = dtdData.getRegularizedPaths(parts, extras); 123 for (Entry<String, Collection<String>> entry : extras.asMap().entrySet()) { 124 for (String value : entry.getValue()) { 125 actual.add(entry.getKey() + "==" + value); 126 } 127 } 128 if (pathForValues != null) { 129 for (String item : pathForValues) { 130 actual.add(item + "==VALUE"); 131 } 132 } 133 TreeSet<String> temp = new TreeSet<>(actual); 134 temp.removeAll(expected); 135 assertEquals("too many, extra: " + path, Collections.emptySet(), temp); 136 temp.clear(); 137 temp.addAll(expected); 138 temp.removeAll(actual); 139 assertEquals("too few, missing: " + path, Collections.emptySet(), temp); 140 } 141 } 142 TestDirectories()143 public void TestDirectories() throws IOException { 144 for (File dir : new File(COMMON_DIR).listFiles()) { 145 if (dir.isDirectory() == false) { 146 continue; 147 } 148 int maxFiles = 5; 149 logln(dir.toString()); 150 for (File file : dir.listFiles()) { 151 String name = file.getName(); 152 if (!name.endsWith(".xml")) { 153 continue; 154 } 155 List<Pair<String, String>> data = new ArrayList<>(); 156 for (Pair<String, String> pathValue : XMLFileReader.loadPathValues(file.toString(), data, true)) { 157 DtdType dtdTypeFromPath = DtdType.fromPath(pathValue.getFirst()); 158 if (!dtdTypeFromPath.directories.contains(dir.getName())) { 159 errln("Mismatch in " + file.toString() 160 + ": " + dtdTypeFromPath + ", " + dtdTypeFromPath.directories); 161 } 162 logln("\t" + file.getName() + "\t" + dtdTypeFromPath); 163 break; 164 } 165 if (--maxFiles < 0) break; 166 } 167 } 168 } 169 TestValueAttributesWithChildren()170 public void TestValueAttributesWithChildren() { 171 Multimap<String, String> m = TreeMultimap.create(); 172 for (DtdType type : DtdType.values()) { 173 if (type == DtdType.ldmlICU) { 174 continue; 175 } 176 DtdData dtdData = DtdData.getInstance(type); 177 Element special = dtdData.getElementFromName().get("special"); 178 checkEmpty(m, type, dtdData.ROOT, special, new HashSet<Element>(), 179 new ArrayList<>(Arrays.asList(dtdData.ROOT))); 180 } 181 Collection<String> items = m.get("error"); 182 if (items != null) { 183 for (String item : items) { 184 errln(item); 185 } 186 } 187 if (isVerbose()) { 188 items = m.get("warn"); 189 if (items != null) { 190 for (String item : items) { 191 warnln(item); 192 } 193 } 194 } 195 } 196 197 /** make sure that if the final element is empty, there is a value attribute required somewhere in the path 198 * @param m 199 * @param type 200 * @param seen 201 */ checkEmpty(Multimap<String, String> m, DtdType type, Element element, Element special, HashSet<Element> seen, List<Element> parents)202 private void checkEmpty(Multimap<String, String> m, DtdType type, Element element, Element special, HashSet<Element> seen, 203 List<Element> parents) { 204 if (seen.contains(element)) { 205 return; 206 } 207 seen.add(element); 208 if (element.isDeprecated()) { 209 return; 210 } 211 212 HashSet<Attribute> valueAttributes = new LinkedHashSet<>(); 213 HashSet<Attribute> distAttributes = new LinkedHashSet<>(); 214 for (Attribute attribute : element.getAttributes().keySet()) { 215 if (attribute.isDeprecated()) continue; 216 switch (attribute.getStatus()) { 217 case value: 218 //if (attribute.mode == Mode.REQUIRED || attribute.mode == Mode.FIXED) {//strong test 219 valueAttributes.add(attribute); 220 break; 221 case distinguished: 222 distAttributes.add(attribute); 223 break; 224 } 225 } 226 ElementType elementType = element.getType(); 227 switch (elementType) { 228 case EMPTY: 229 if (valueAttributes.isEmpty()) { 230 if (!distAttributes.isEmpty()) { 231 m.put("warn", type + "\t||" + showPath(parents) + "||path has neither value NOR value attributes NOR dist. attrs.||"); 232 } else { 233 m.put("error", "\t||" + showPath(parents) + "||path has neither value NOR value attributes||"); 234 } 235 } 236 break; 237 case ANY: 238 case PCDATA: 239 if (!valueAttributes.isEmpty()) { 240 m.put("warn", "\t||" + showPath(parents) + "||path has both value AND value attributes||" + valueAttributes + "||"); 241 } 242 break; 243 case CHILDREN: 244 // first remove deprecateds, and special 245 List<Element> children = new ArrayList<>(element.getChildren().keySet()); 246 for (Iterator<Element> it = children.iterator(); it.hasNext();) { 247 Element child = it.next(); 248 if (child.equals(special) || child.isDeprecated()) { 249 it.remove(); 250 } 251 } 252 253 // if no children left, treat like EMPTY 254 if (children.isEmpty()) { 255 if (valueAttributes.isEmpty()) { 256 errln(type + "\t|| " + showPath(parents) + "||path has neither value NOR value attributes||"); 257 } 258 break; 259 } 260 if (!valueAttributes.isEmpty()) { 261 switch (element.getName()) { 262 case "ruleset": 263 logKnownIssue("cldrbug:8909", "waiting for RBNF to use data"); 264 break; 265 case "key": 266 case "territory": 267 case "transform": 268 logKnownIssue("cldrbug:9982", "Lower priority fixes to bad xml"); 269 break; 270 default: 271 m.put("error", "\t||" + showPath(parents) + "||path has both children AND value attributes" 272 + "||" + valueAttributes 273 + "||" + children + "||"); 274 break; 275 } 276 } 277 for (Element child : children) { 278 parents.add(child); 279 checkEmpty(m, type, child, special, seen, parents); 280 parents.remove(parents.size() - 1); 281 } 282 break; 283 } 284 } 285 showPath(List<Element> parents)286 private String showPath(List<Element> parents) { 287 return "!//" + Joiner.on("/").join(parents); 288 } 289 TestNewDtdData()290 public void TestNewDtdData() { 291 for (DtdType type : DtdType.values()) { 292 if (type == DtdType.ldmlICU) { 293 continue; 294 } 295 DtdData dtdData = DtdData.getInstance(type); 296 for (Element element : dtdData.getElements()) { 297 boolean orderedNew = dtdData.isOrdered(element.name); 298 boolean orderedOld = isOrderedOld(element.name, type); 299 assertEquals("isOrdered " + type + ":" + element, orderedOld, orderedNew); 300 boolean deprecatedNew = dtdData.isDeprecated(element.name, "*", "*"); 301 boolean deprecatedOld = SUPPLEMENTAL_DATA_INFO.isDeprecated(type, element.name, "*", "*"); 302 assertEquals("isDeprecated " + type + ":" + element, deprecatedOld, deprecatedNew); 303 304 for (Attribute attribute : element.getAttributes().keySet()) { 305 boolean distinguishedNew = dtdData.isDistinguishing(element.name, attribute.name); 306 boolean distinguishedOld = isDistinguishingOld(type, element.name, attribute.name); 307 assertEquals("isDistinguished " + type 308 + ": elementName.equals(\"" + element.name + "\") && attribute.equals(\"" + attribute.name + "\")", distinguishedOld, distinguishedNew); 309 deprecatedNew = dtdData.isDeprecated(element.name, attribute.name, "*"); 310 deprecatedOld = SUPPLEMENTAL_DATA_INFO.isDeprecated(type, element.name, attribute.name, "*"); 311 assertEquals("isDeprecated " + type + ":" + attribute, deprecatedOld, deprecatedNew); 312 for (String value : attribute.values.keySet()) { 313 deprecatedNew = dtdData.isDeprecated(element.name, attribute.name, value); 314 deprecatedOld = SUPPLEMENTAL_DATA_INFO.isDeprecated(type, element.name, attribute.name, value); 315 assertEquals("isDeprecated " + type + ":" + attribute + ":" + value, deprecatedOld, deprecatedNew); 316 } 317 } 318 } 319 } 320 } 321 322 // public void TestNonLeafValues() { 323 // for (DtdType type : DtdType.values()) { 324 // if (type == DtdType.ldmlICU) { 325 // continue; 326 // } 327 // DtdData dtdData = DtdData.getInstance(type); 328 // for (Element element : dtdData.getElements()) { 329 // if (element.isDeprecated()) { 330 // continue; 331 // } 332 // switch (element.getType()) { 333 // case PCDATA: 334 // case EMPTY: continue; 335 // case ANY: 336 // } 337 // for (Attribute attribute : element.getAttributes().keySet()) { 338 // if (attribute.isDeprecated()) { 339 // continue; 340 // } 341 // switch (attribute.getStatus()) { 342 // case value: 343 // errln(type + "\t" + element + "\t" + attribute + "\t"); 344 // } 345 // } 346 // } 347 // } 348 // 349 // } 350 351 // TESTING CODE 352 static final Set<String> orderedElements = Collections.unmodifiableSet(new HashSet<>(Arrays 353 .asList( 354 // can prettyprint with TestAttributes 355 356 // DTD: ldml 357 // <collation> children 358 "base", "optimize", "rules", "settings", "suppress_contractions", 359 360 // <rules> children 361 "i", "ic", "p", "pc", "reset", "s", "sc", "t", "tc", "x", 362 363 // <x> children 364 "context", "extend", "i", "ic", "p", "pc", "s", "sc", "t", "tc", 365 "last_non_ignorable", "last_secondary_ignorable", "last_tertiary_ignorable", 366 367 // <variables> children 368 "variable", 369 370 // <rulesetGrouping> children 371 "ruleset", 372 373 // <ruleset> children 374 "rbnfrule", 375 376 // <exceptions> children (deprecated, use 'suppressions') 377 "exception", 378 379 // <suppressions> children 380 "suppression", 381 382 // DTD: supplementalData 383 // <territory> children 384 // "languagePopulation", 385 386 // <postalCodeData> children 387 // "postCodeRegex", 388 389 // <characters> children 390 // "character-fallback", 391 392 // <character-fallback> children 393 // "character", 394 395 // <character> children 396 "substitute", // may occur multiple times 397 398 // <transform> children 399 "comment", "tRule", 400 401 // <validity> children 402 // both of these don't need to be ordered, but must delay changes until after isDistinguished always uses 403 // the dtd type 404 "attributeValues", // attribute values shouldn't need ordering, as long as these are distinguishing: 405 // elements="zoneItem" attributes="type" 406 "variable", // doesn't need to be ordered 407 408 // <pluralRules> children 409 "pluralRule", 410 411 // <codesByTerritory> children 412 // "telephoneCountryCode", // doesn't need ordering, as long as code is distinguishing, telephoneCountryCode 413 // code="376" 414 415 // <numberingSystems> children 416 // "numberingSystem", // doesn't need ordering, since id is distinguishing 417 418 // <metazoneInfo> children 419 // "timezone", // doesn't need ordering, since type is distinguishing 420 421 "attributes", // shouldn't need this in //supplementalData/metadata/suppress/attributes, except that the 422 // element is badly designed 423 424 "languageMatch", 425 426 "exception", // needed for new segmentations 427 "coverageLevel", // needed for supplemental/coverageLevel.xml 428 "coverageVariable", // needed for supplemental/coverageLevel.xml 429 "substitute", // needed for characters.xml 430 "unitPreference" 431 ))); 432 isOrderedOld(String element, DtdType type)433 public static boolean isOrderedOld(String element, DtdType type) { 434 return orderedElements.contains(element); 435 } 436 isDistinguishingOld(DtdType dtdType, String elementName, String attribute)437 public boolean isDistinguishingOld(DtdType dtdType, String elementName, String attribute) { 438 switch (dtdType) { 439 case ldml: 440 return attribute.equals("_q") 441 || attribute.equals("key") 442 || attribute.equals("indexSource") 443 || attribute.equals("request") 444 || attribute.equals("count") 445 || attribute.equals("id") 446 || attribute.equals("registry") 447 || attribute.equals("alt") 448 || attribute.equals("mzone") 449 || attribute.equals("from") 450 || attribute.equals("to") 451 || attribute.equals("value") && !elementName.equals("rbnfrule") 452 || attribute.equals("yeartype") 453 || attribute.equals("numberSystem") 454 || attribute.equals("parent") 455 || elementName.equals("annotation") && attribute.equals("cp") 456 || (attribute.equals("type") 457 && !elementName.equals("default") 458 && !elementName.equals("measurementSystem") 459 && !elementName.equals("mapping") 460 && !elementName.equals("abbreviationFallback") 461 && !elementName.equals("preferenceOrdering")) 462 || (elementName.equals("parseLenients") && (attribute.equals("scope") || attribute.equals("level"))) 463 || (elementName.equals("parseLenient") && attribute.equals("sample")) 464 || (elementName.equals("ordinalMinimalPairs") && attribute.equals("ordinal")) 465 || (elementName.equals("styleName") && attribute.equals("subtype")) 466 || (elementName.equals("unitPattern") && attribute.equals("case")) 467 || (elementName.equals("compoundUnitPattern") && attribute.equals("case")) 468 || (elementName.equals("compoundUnitPattern1") && (attribute.equals("case") || attribute.equals("gender"))) 469 || (elementName.equals("genderMinimalPairs") && attribute.equals("gender")) 470 || (elementName.equals("caseMinimalPairs") && attribute.equals("case")) 471 ; 472 473 case ldmlBCP47: 474 return attribute.equals("_q") 475 //|| attribute.equals("alias") 476 || attribute.equals("name") 477 || attribute.equals("extension"); 478 case supplementalData: 479 return attribute.equals("_q") 480 || (elementName.equals("matchVariable") && attribute.equals("id")) 481 || attribute.equals("iso4217") 482 || attribute.equals("iso3166") 483 || attribute.equals("code") 484 || (attribute.equals("type") && !elementName.equals("calendarSystem") && !elementName.equals("mapZone") 485 && !elementName.equals("numberingSystem") && !elementName.equals("variable")) 486 || attribute.equals("id") && elementName.equals("variable") 487 || attribute.equals("alt") 488 || attribute.equals("dtds") 489 || attribute.equals("idStatus") 490 || elementName.equals("deprecatedItems") 491 && (attribute.equals("type") || attribute.equals("elements") || attribute.equals("attributes") || attribute.equals("values")) 492 || elementName.equals("character") 493 && attribute.equals("value") 494 || elementName.equals("dayPeriodRules") 495 && attribute.equals("locales") 496 || elementName.equals("dayPeriodRule") 497 && (attribute.equals("type")) 498 || elementName.equals("metazones") && attribute.equals("type") 499 || elementName.equals("subgroup") && attribute.equals("subtype") 500 || elementName.equals("mapZone") 501 && (attribute.equals("other") || attribute.equals("territory")) 502 || elementName.equals("postCodeRegex") 503 && attribute.equals("territoryId") 504 || elementName.equals("calendarPreference") 505 && attribute.equals("territories") 506 || elementName.equals("minDays") 507 && attribute.equals("count") 508 || elementName.equals("firstDay") 509 && attribute.equals("day") 510 || elementName.equals("weekendStart") 511 && attribute.equals("day") 512 || elementName.equals("weekendEnd") 513 && attribute.equals("day") 514 || elementName.equals("measurementSystem") 515 && attribute.equals("category") 516 || elementName.equals("unitPreferences") 517 && (attribute.equals("category") || attribute.equals("usage") || attribute.equals("scope")) 518 || elementName.equals("unitPreference") 519 && (attribute.equals("regions") || attribute.equals("geq")) 520 || elementName.equals("distinguishingItems") 521 && attribute.equals("attributes") 522 || elementName.equals("codesByTerritory") 523 && attribute.equals("territory") 524 || elementName.equals("currency") 525 && attribute.equals("iso4217") 526 || elementName.equals("territoryAlias") 527 && attribute.equals("type") 528 || elementName.equals("territoryCodes") 529 && attribute.equals("type") 530 || elementName.equals("group") 531 && (attribute.equals("status")) // || attribute.equals("grouping") 532 || elementName.equals("plurals") 533 && attribute.equals("type") 534 || elementName.equals("pluralRules") 535 && attribute.equals("locales") 536 || elementName.equals("pluralRule") 537 && attribute.equals("count") 538 || elementName.equals("pluralRanges") 539 && attribute.equals("locales") 540 || elementName.equals("pluralRange") 541 && (attribute.equals("start") || attribute.equals("end")) 542 || elementName.equals("hours") 543 && (attribute.equals("preferred") || attribute.equals("allowed")) 544 || elementName.equals("personList") && attribute.equals("type") 545 || elementName.equals("likelySubtag") 546 && attribute.equals("from") 547 || elementName.equals("rgPath") 548 && attribute.equals("path") 549 || elementName.equals("timezone") 550 && attribute.equals("type") 551 || elementName.equals("usesMetazone") 552 && (attribute.equals("to") || attribute.equals("from")) // attribute.equals("mzone") || 553 || elementName.equals("numberingSystem") 554 && attribute.equals("id") 555 || elementName.equals("group") 556 && attribute.equals("type") 557 || elementName.equals("currency") 558 && attribute.equals("from") 559 || elementName.equals("currency") 560 && attribute.equals("to") 561 || elementName.equals("currency") 562 && attribute.equals("iso4217") 563 || (elementName.equals("parentLocale") || elementName.equals("languageGroup")) 564 && attribute.equals("parent") 565 || elementName.equals("currencyCodes") 566 && attribute.equals("type") 567 || elementName.equals("approvalRequirement") 568 && (attribute.equals("locales") || attribute.equals("paths")) 569 || elementName.equals("weekOfPreference") 570 && attribute.equals("locales") 571 || elementName.equals("coverageVariable") 572 && attribute.equals("key") 573 || elementName.equals("coverageLevel") 574 && (attribute.equals("inLanguage") || attribute.equals("inScript") || attribute.equals("inTerritory") || attribute.equals("match")) 575 || elementName.equals("languageMatch") 576 && (attribute.equals("desired") || attribute.equals("supported")) 577 || (elementName.equals("transform") && (attribute.equals("source") || attribute.equals("target") || attribute.equals("direction") || attribute 578 .equals("variant"))) 579 || (elementName.equals("grammaticalFeatures") && (attribute.equals("locales") || attribute.equals("targets"))) 580 || (elementName.equals("grammaticalDefiniteness") && attribute.equals("scope")) 581 || (elementName.equals("grammaticalCase") && attribute.equals("scope")) 582 || (elementName.equals("grammaticalGender") && attribute.equals("scope")) 583 || (elementName.equals("convertUnit") && (attribute.equals("source") || attribute.equals("target"))) 584 || (elementName.equals("unitConstant") && attribute.equals("constant")) 585 || (elementName.equals("unitQuantity") && attribute.equals("baseUnit")) 586 587 || attribute.equals("scope") 588 || elementName.equals("deriveComponent") && (attribute.equals("feature") || attribute.equals("structure")) 589 || elementName.equals("grammaticalDerivations") && attribute.equals("locales") 590 || elementName.equals("deriveCompound") && (attribute.equals("feature")|| attribute.equals("structure")) 591 ; 592 593 case keyboard: 594 return attribute.equals("_q") 595 || elementName.equals("keyboard") && attribute.equals("locale") 596 || elementName.equals("keyMap") && attribute.equals("modifiers") 597 || elementName.equals("map") && attribute.equals("iso") 598 || elementName.equals("map") && attribute.equals("optional") 599 || elementName.equals("map") && attribute.equals("longpress-status") 600 || elementName.equals("transforms") && attribute.equals("type") 601 || elementName.equals("transform") && attribute.equals("from") 602 || elementName.equals("import") && attribute.equals("path") 603 || elementName.equals("reorder") && attribute.equals("before") 604 || elementName.equals("reorder") && attribute.equals("from") 605 || elementName.equals("reorder") && attribute.equals("after") 606 || elementName.equals("layer") && attribute.equals("modifier") 607 || elementName.equals("switch") && attribute.equals("iso") 608 || elementName.equals("transform") && attribute.equals("before") 609 || elementName.equals("transform") && attribute.equals("after") 610 || elementName.equals("backspace") && attribute.equals("before") 611 || elementName.equals("backspace") && attribute.equals("from") 612 || elementName.equals("backspace") && attribute.equals("after") 613 || elementName.equals("vkeys") && attribute.equals("type") 614 || elementName.equals("flick") && attribute.equals("directions") 615 || elementName.equals("row") && attribute.equals("keys") 616 || elementName.equals("vkey") && attribute.equals("iso") 617 || elementName.equals("display") && attribute.equals("to") 618 || elementName.equals("flicks") && attribute.equals("iso"); 619 620 case platform: 621 return attribute.equals("_q") 622 || elementName.equals("platform") && attribute.equals("id") 623 || elementName.equals("map") && attribute.equals("keycode"); 624 case ldmlICU: 625 return false; 626 default: 627 throw new IllegalArgumentException("Type is wrong: " + dtdType); 628 } 629 // if (result != matches(distinguishingAttributeMap, new String[]{elementName, attribute}, true)) { 630 // matches(distinguishingAttributeMap, new String[]{elementName, attribute}, true); 631 // throw new IllegalArgumentException("Failed: " + elementName + ", " + attribute); 632 // } 633 } 634 635 /** 636 * @deprecated 637 * @return 638 */ 639 @Deprecated getSerialElements()640 public static Set<String> getSerialElements() { 641 return orderedElements; 642 } 643 644 public static enum TestEnum {a, b, c, d} 645 TestEnumParser()646 public void TestEnumParser() throws ClassNotFoundException { 647 Object[][] tests = { 648 {Status.class, "regular", Status.regular}, 649 {Status.class, "regular deprecated", Validity.Status.regular, Status.deprecated}, 650 {Status.class, "deprecated regular", "regular deprecated", Validity.Status.regular, Status.deprecated}, 651 {TestEnum.class, "b a", "a b", TestEnum.a, TestEnum.b}, 652 {TestEnum.class, "!c d", "a b", TestEnum.a, TestEnum.b}, 653 }; 654 for (Object[] test : tests) { 655 Class aClass = (Class<Enum>)test[0]; 656 EnumParser parser = MatchValue.EnumParser.of(aClass); 657 final String inputText = (String)test[1]; 658 int startOfValues = test[2] instanceof String ? 3 : 2; 659 String expectedFormat = startOfValues == 3 ? (String) test[2] : inputText; 660 Set<Enum> expected = new TreeSet<>(); 661 for (Object item : Arrays.asList(test).subList(startOfValues, test.length)) { 662 expected.add((Enum)item); 663 } 664 665 Set<Enum> actual = parser.parse(inputText); 666 assertEquals("parse " + test[1], expected, actual); 667 668 String formatted = parser.format(expected); 669 assertEquals("format " + expected, expectedFormat, formatted); 670 671 } 672 } 673 TestMatchValue()674 public void TestMatchValue() { 675 Object[][] tests = { 676 {"validity/short-unit/deprecated", "inch-hg"} 677 }; 678 for (Object[] test : tests) { 679 MatchValue matcher = MatchValue.of((String)test[0]); 680 final String toMatch = (String)test[1]; 681 boolean expectedValue = test.length < 3 ? true : Boolean.valueOf((String)test[2]); 682 683 final boolean actual = matcher.is(toMatch); 684 assertEquals(Arrays.asList(test).toString(), expectedValue, actual); 685 } 686 } 687 } 688