|
9 | 9 | import java.util.Arrays;
|
10 | 10 | import java.util.Collection;
|
11 | 11 | import java.util.Collections;
|
| 12 | +import java.util.Comparator; |
12 | 13 | import java.util.HashSet;
|
13 | 14 | import java.util.Iterator;
|
14 | 15 | import java.util.LinkedHashSet;
|
|
24 | 25 | import org.unicode.cldr.util.DtdData.Element;
|
25 | 26 | import org.unicode.cldr.util.DtdData.Element.ValueConstraint;
|
26 | 27 | import org.unicode.cldr.util.DtdData.ElementType;
|
| 28 | +import org.unicode.cldr.util.DtdData.Mode; |
27 | 29 | import org.unicode.cldr.util.DtdType;
|
| 30 | +import org.unicode.cldr.util.Joiners; |
| 31 | +import org.unicode.cldr.util.MapComparator; |
28 | 32 | import org.unicode.cldr.util.MatchValue;
|
29 | 33 | import org.unicode.cldr.util.MatchValue.EnumParser;
|
30 | 34 | import org.unicode.cldr.util.Pair;
|
@@ -936,4 +940,131 @@ public void testEmptyPcdata() {
|
936 | 940 | assertEquals(path, expected, actual);
|
937 | 941 | }
|
938 | 942 | }
|
| 943 | + |
| 944 | + final Set<String> attributeOrderGrandfathered = |
| 945 | + Set.of( |
| 946 | + "//ldml…/version[@ number < cldrVersion", // Status: metadata ≠ value Mode: |
| 947 | + // REQUIRED ≠ FIXED |
| 948 | + "//ldml…/pattern[@ numbers < count", // Status: value ≠ distinguished Mode: |
| 949 | + // OPTIONAL |
| 950 | + "//ldml…/symbols[@ references < numberSystem", // Status: metadata ≠ |
| 951 | + // distinguished Mode: OPTIONAL |
| 952 | + "//supplementalData…/languagePopulation[@ writingPercent < populationPercent", // Status: value Mode: OPTIONAL ≠ REQUIRED |
| 953 | + "//supplementalData…/metazoneId[@ longId < deprecated", // Status: value |
| 954 | + // Mode: OPTIONAL ≠ NULL |
| 955 | + "//supplementalData…/version[@ number < cldrVersion", // Status: metadata ≠ |
| 956 | + // value Mode: REQUIRED ≠ |
| 957 | + // FIXED |
| 958 | + "//supplementalData…/coverageLevel[@ value < match", // Status: value ≠ |
| 959 | + // distinguished Mode: |
| 960 | + // REQUIRED |
| 961 | + "//supplementalData…/parentLocale[@ localeRules < locales", // Status: value |
| 962 | + // Mode: OPTIONAL ≠ |
| 963 | + // REQUIRED |
| 964 | + "//supplementalData…/group[@ grouping < status", // Status: value ≠ |
| 965 | + // distinguished Mode: |
| 966 | + // OPTIONAL |
| 967 | + "//supplementalData…/approvalRequirement[@ votes < locales", // Status: value ≠ |
| 968 | + // distinguished |
| 969 | + // Mode: REQUIRED |
| 970 | + "//supplementalData…/mapZone[@ type < other", // Status: value ≠ distinguished |
| 971 | + // Mode: REQUIRED |
| 972 | + "//supplementalData…/transform[@ variant < direction", // Status: distinguished |
| 973 | + // Mode: OPTIONAL ≠ |
| 974 | + // NULL |
| 975 | + "//supplementalData…/transform[@ backwardAlias < visibility", // Status: value |
| 976 | + // Mode: OPTIONAL |
| 977 | + // ≠ NULL |
| 978 | + "//supplementalData…/numberingSystem[@ type < id", // Status: value ≠ |
| 979 | + // distinguished Mode: |
| 980 | + // REQUIRED |
| 981 | + "//supplementalData…/variable[@ type < id", // Status: value ≠ distinguished |
| 982 | + // Mode: OPTIONAL ≠ REQUIRED |
| 983 | + "//ldmlBCP47…/type[@ since < iana", // Status: metadata ≠ value Mode: OPTIONAL |
| 984 | + "//ldmlBCP47…/version[@ number < cldrVersion", // Status: metadata ≠ value Mode: |
| 985 | + // REQUIRED ≠ FIXED |
| 986 | + "//ldmlBCP47…/key[@ extension < name", // Status: distinguished Mode: |
| 987 | + // OPTIONAL ≠ REQUIRED |
| 988 | + "//ldmlBCP47…/key[@ description < deprecated", // Status: value Mode: |
| 989 | + // OPTIONAL ≠ NULL |
| 990 | + "//keyboard3…/reorder[@ before < from", // Status: value Mode: OPTIONAL ≠ |
| 991 | + // REQUIRED |
| 992 | + "//keyboardTest3…/repertoire[@ type < name", // Status: value ≠ distinguished |
| 993 | + // Mode: OPTIONAL ≠ REQUIRED |
| 994 | + "//keyboardTest3…/info[@ author < name" // Status: metadata ≠ distinguished |
| 995 | + // Mode: OPTIONAL ≠ REQUIRED |
| 996 | + ); |
| 997 | + |
| 998 | + public void testAttributeOrder() { |
| 999 | + for (DtdType dtdType : DtdType.values()) { |
| 1000 | + if (dtdType == DtdType.ldmlICU) { |
| 1001 | + continue; |
| 1002 | + } |
| 1003 | + DtdData dtdData = DtdData.getInstance(dtdType); |
| 1004 | + for (Element element : dtdData.getElements()) { |
| 1005 | + if (element.isDeprecated()) { |
| 1006 | + continue; |
| 1007 | + } |
| 1008 | + Attribute lastAttribute = null; |
| 1009 | + for (Attribute attribute : element.getAttributes().keySet()) { |
| 1010 | + if (attribute.isDeprecated() || attribute.name.equals("alt")) { |
| 1011 | + continue; |
| 1012 | + } |
| 1013 | + if (lastAttribute != null) { |
| 1014 | + String header = |
| 1015 | + Joiners.ES.join( |
| 1016 | + "//", |
| 1017 | + dtdType, |
| 1018 | + "…/", |
| 1019 | + element.name, |
| 1020 | + "[@ ", |
| 1021 | + lastAttribute.name, |
| 1022 | + " < ", |
| 1023 | + attribute.name); |
| 1024 | + int diffToLast = |
| 1025 | + attributeStatusModeComparator.compare(attribute, lastAttribute); |
| 1026 | + if (diffToLast < 0) { |
| 1027 | + String message = |
| 1028 | + Joiners.ES.join( |
| 1029 | + "\"", |
| 1030 | + header, |
| 1031 | + "\",\t// Status: ", |
| 1032 | + showDiff( |
| 1033 | + lastAttribute.getStatus(), |
| 1034 | + attribute.getStatus()), |
| 1035 | + "\tMode: ", |
| 1036 | + showDiff(lastAttribute.mode, attribute.mode)); |
| 1037 | + if (!attributeOrderGrandfathered.contains(header)) { |
| 1038 | + errln(message); |
| 1039 | + } else if (isVerbose()) { |
| 1040 | + warnln(message); |
| 1041 | + } |
| 1042 | + } |
| 1043 | + } |
| 1044 | + lastAttribute = attribute; |
| 1045 | + } |
| 1046 | + } |
| 1047 | + MapComparator<String> comp = dtdData.getAttributeComparator(); |
| 1048 | + comp.getOrder(); |
| 1049 | + } |
| 1050 | + warnln("Use -v to see overrides"); |
| 1051 | + } |
| 1052 | + |
| 1053 | + private String showDiff(Object a, Object b) { |
| 1054 | + return a == b ? a.toString() : a + " ≠ " + b; |
| 1055 | + } |
| 1056 | + |
| 1057 | + final Comparator<Attribute> attributeStatusModeComparator = |
| 1058 | + new Comparator<>() { |
| 1059 | + @Override |
| 1060 | + public int compare(Attribute o1, Attribute o2) { |
| 1061 | + return Comparator.comparing( |
| 1062 | + Attribute::getStatus) // @distinguished before @value before |
| 1063 | + // @metadata; |
| 1064 | + .thenComparing(x -> x.mode != Mode.OPTIONAL ? 0 : 1) |
| 1065 | + // #optional must come after all others |
| 1066 | + // (which can be intermixed) |
| 1067 | + .compare(o1, o2); |
| 1068 | + } |
| 1069 | + }; |
939 | 1070 | }
|
0 commit comments