• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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