• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.unittest;
2 
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collection;
7 import java.util.EnumMap;
8 import java.util.EnumSet;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.LinkedHashMap;
12 import java.util.LinkedHashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.Set;
17 import java.util.TreeMap;
18 import java.util.TreeSet;
19 import java.util.regex.Matcher;
20 
21 import org.unicode.cldr.test.CoverageLevel2;
22 import org.unicode.cldr.test.ExampleGenerator;
23 import org.unicode.cldr.util.CLDRConfig;
24 import org.unicode.cldr.util.CLDRFile;
25 import org.unicode.cldr.util.CLDRFile.Status;
26 import org.unicode.cldr.util.CLDRPaths;
27 import org.unicode.cldr.util.CLDRURLS;
28 import org.unicode.cldr.util.CldrUtility;
29 import org.unicode.cldr.util.Containment;
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.Emoji;
34 import org.unicode.cldr.util.Factory;
35 import org.unicode.cldr.util.GrammarInfo;
36 import org.unicode.cldr.util.GrammarInfo.CaseValues;
37 import org.unicode.cldr.util.GrammarInfo.GenderValues;
38 import org.unicode.cldr.util.LanguageTagParser;
39 import org.unicode.cldr.util.Level;
40 import org.unicode.cldr.util.Organization;
41 import org.unicode.cldr.util.Pair;
42 import org.unicode.cldr.util.PathDescription;
43 import org.unicode.cldr.util.PathHeader;
44 import org.unicode.cldr.util.PathHeader.PageId;
45 import org.unicode.cldr.util.PathHeader.SectionId;
46 import org.unicode.cldr.util.PathHeader.SurveyToolStatus;
47 import org.unicode.cldr.util.PathHeader.Width;
48 import org.unicode.cldr.util.PathStarrer;
49 import org.unicode.cldr.util.PatternCache;
50 import org.unicode.cldr.util.PatternPlaceholders;
51 import org.unicode.cldr.util.PatternPlaceholders.PlaceholderInfo;
52 import org.unicode.cldr.util.PatternPlaceholders.PlaceholderStatus;
53 import org.unicode.cldr.util.StandardCodes;
54 import org.unicode.cldr.util.SupplementalDataInfo;
55 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo;
56 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count;
57 import org.unicode.cldr.util.SupplementalDataInfo.PluralType;
58 import org.unicode.cldr.util.With;
59 import org.unicode.cldr.util.XMLFileReader;
60 import org.unicode.cldr.util.XPathParts;
61 
62 import com.google.common.base.Joiner;
63 import com.google.common.collect.HashMultimap;
64 import com.google.common.collect.ImmutableSet;
65 import com.google.common.collect.LinkedListMultimap;
66 import com.google.common.collect.Multimap;
67 import com.google.common.collect.TreeMultimap;
68 import com.ibm.icu.impl.Relation;
69 import com.ibm.icu.impl.Row;
70 import com.ibm.icu.impl.Row.R2;
71 
72 public class TestPathHeader extends TestFmwkPlus {
73     private static final DtdType DEBUG_DTD_TYPE = null; // DtdType.supplementalData;
74     private static final String COMMON_DIR = CLDRPaths.BASE_DIRECTORY + "common/";
75     private static final boolean DEBUG = false;
76 
main(String[] args)77     public static void main(String[] args) {
78         new TestPathHeader().run(args);
79     }
80 
81     static final CLDRConfig info = CLDRConfig.getInstance();
82     static final Factory factory = info.getCommonAndSeedAndMainAndAnnotationsFactory();
83     static final CLDRFile english = factory.make("en", true);
84     static final SupplementalDataInfo supplemental = info
85         .getSupplementalDataInfo();
86     static PathHeader.Factory pathHeaderFactory = PathHeader
87         .getFactory(english);
88     private EnumSet<PageId> badZonePages = EnumSet.of(PageId.UnknownT);
89 
tempTestAnnotation()90     public void tempTestAnnotation() {
91         // NEW:     <annotation cp="��">face | grin</annotation>
92         //          <annotation cp="��" type="tts">grinning face</annotation>
93 
94         final String path1 = "//ldml/annotations/annotation[@cp=\"��\"]";
95         PathHeader ph1 = pathHeaderFactory.fromPath(path1);
96         logln(ph1.toString() + "\t" + path1);
97         final String path2 = "//ldml/annotations/annotation[@cp=\"��\"][@type=\"tts\"]";
98         PathHeader ph2 = pathHeaderFactory.fromPath(path2);
99         logln(ph2.toString() + "\t" + path2);
100         final String path3 = "//ldml/annotations/annotation[@cp=\"��\"]";
101         PathHeader ph3 = pathHeaderFactory.fromPath(path2);
102         logln(ph3.toString() + "\t" + path3);
103 
104         assertNotEquals("pathheader", ph1, ph2);
105         assertNotEquals("pathheader", ph1.toString(), ph2.toString());
106         assertRelation("pathheader", true, ph1, TestFmwkPlus.LEQ, ph3);
107         assertRelation("pathheader", true, ph3, TestFmwkPlus.LEQ, ph2);
108     }
109 
110     static final String[] MIN_LOCALES = {"root", "en", "de", "ru", "ko"}; // choose locales with range of case/gender structures
111 
tempTestCompletenessLdmlDtd()112     public void tempTestCompletenessLdmlDtd() {
113         // List<String> failures = null;
114         pathHeaderFactory.clearCache();
115         PathChecker pathChecker = new PathChecker();
116         for (String directory : DtdType.ldml.directories) {
117             Factory factory2 = CLDRConfig.getInstance().getMainAndAnnotationsFactory();
118             Set<String> source = factory2.getAvailable();
119             for (String file : getFilesToTest(source, MIN_LOCALES)) {
120                 if (DEBUG) warnln(" TestCompletenessLdmlDtd: " + directory + ", " + file);
121                 DtdData dtdData = null;
122                 CLDRFile cldrFile = factory2.make(file, true);
123                 for (String path : cldrFile.fullIterable()) {
124                     pathChecker.checkPathHeader(cldrFile.getDtdData(), path);
125                 }
126             }
127         }
128         Set<String> missing = pathHeaderFactory.getUnmatchedRegexes();
129         if (missing.size() != 0) {
130             for (String e : missing) {
131                 errln("Path Regex never matched:\t" + e);
132             }
133         }
134         if (!pathChecker.badHeaders.isEmpty()) {
135             System.out.println("For help with DTD updates: " + CLDRURLS.CLDR_UPDATINGDTD_URL);
136         }
137     }
138 
getFilesToTest(Collection<String> source, String... doFirst)139     private Collection<String> getFilesToTest(Collection<String> source, String... doFirst) {
140         LinkedHashSet<String> files = new LinkedHashSet<>(Arrays.asList(doFirst));
141         files.retainAll(source); // put first
142         files.addAll(new HashSet<>(source)); // now add others semi-randomly
143         int max = Math.min(30, files.size());
144         if (getInclusion() == 10 || files.size() <= max) {
145             return files;
146         }
147         ArrayList<String> shortFiles = new ArrayList<>(files);
148         if (getInclusion() > 5) {
149             max += (files.size() - 30) * (getInclusion() - 5) / 10; // use proportional amount
150         }
151         return shortFiles.subList(0, max);
152     }
153 
TestCompleteness()154     public void TestCompleteness() {
155         PathHeader.Factory pathHeaderFactory2 = PathHeader.getFactory(english);
156         // List<String> failures = null;
157         pathHeaderFactory2.clearCache();
158         Multimap<PathHeader.PageId, PathHeader.SectionId> pageUniqueness = TreeMultimap.create();
159         Multimap<String, Pair<PathHeader.SectionId, PathHeader.PageId>> headerUniqueness = TreeMultimap.create();
160         Set<String> toTest;
161         switch (getInclusion()) {
162         default:
163             toTest = StandardCodes.make().getLocaleCoverageLocales(Organization.cldr);
164             break;
165         case 10:
166             toTest = factory.getAvailable();
167             break;
168         }
169         toTest = ImmutableSet.<String> builder().add("en").addAll(toTest).build();
170         Set<String> seenPaths = new HashSet<>();
171         Set<String> localSeenPaths = new TreeSet<>();
172         for (String locale : toTest) {
173             localSeenPaths.clear();
174             for (String p : factory.make(locale, true).fullIterable()) {
175                 if (p.startsWith("//ldml/identity/")) {
176                     continue;
177                 }
178                 if (seenPaths.contains(p)) {
179                     continue;
180                 }
181                 seenPaths.add(p);
182                 localSeenPaths.add(p);
183                 // if (p.contains("symbol[@alt") && failures == null) {
184                 // PathHeader result = pathHeaderFactory2.fromPath(p, failures = new
185                 // ArrayList<String>());
186                 // logln("Matching " + p + ": " + result + "\t" +
187                 // result.getSurveyToolStatus());
188                 // for (String failure : failures) {
189                 // logln("\t" + failure);
190                 // }
191                 // }
192                 PathHeader ph;
193                 try {
194                     ph = pathHeaderFactory2.fromPath(p);
195                 } catch (Exception e1) {
196                     try {
197                         ph = pathHeaderFactory2.fromPath(p);
198                     } catch (Exception e2) {
199                         throw new IllegalArgumentException(locale + ":\t" + p, e2);
200                     }
201                 }
202                 if (ph == null) {
203                     errln("Failed to create path from: " + p);
204                     continue;
205                 }
206                 final SectionId sectionId = ph.getSectionId();
207                 if (sectionId != SectionId.Special) {
208                     pageUniqueness.put(ph.getPageId(), sectionId);
209                     headerUniqueness.put(ph.getHeader(), new Pair<>(sectionId, ph.getPageId()));
210                 }
211             }
212             if (!localSeenPaths.isEmpty()) {
213                 logln(locale + ": checked " + localSeenPaths.size() + " new paths");
214             }
215         }
216         Set<String> missing = pathHeaderFactory2.getUnmatchedRegexes();
217         if (missing.size() != 0) {
218             for (String e : missing) {
219                 if (e.contains("//ldml/")) {
220                     if (e.contains("//ldml/rbnf/") || e.contains("//ldml/segmentations/") || e.contains("//ldml/collations/")) {
221                         continue;
222                     }
223                     logln("Path Regex never matched:\t" + e);
224                 }
225             }
226         }
227 
228         for (Entry<PageId, Collection<SectionId>> e : pageUniqueness.asMap().entrySet()) {
229             Collection<SectionId> values = e.getValue();
230             if (values.size() != 1) {
231                 warnln("Duplicate page in section: " + CldrUtility.toString(e));
232             }
233         }
234 
235         for (Entry<String, Collection<Pair<SectionId, PageId>>> e : headerUniqueness.asMap().entrySet()) {
236             Collection<Pair<SectionId, PageId>> values = e.getValue();
237             if (values.size() != 1) {
238                 warnln("Duplicate header in (section,page): " + CldrUtility.toString(e));
239             }
240         }
241     }
242 
Test6170()243     public void Test6170() {
244         String p1 = "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"speed-kilometer-per-hour\"]/unitPattern[@count=\"other\"]";
245         String p2 = "//ldml/units/unitLength[@type=\"narrow\"]/unit[@type=\"area-square-meter\"]/unitPattern[@count=\"other\"]";
246         PathHeader ph1 = pathHeaderFactory.fromPath(p1);
247         PathHeader ph2 = pathHeaderFactory.fromPath(p2);
248         int comp12 = ph1.compareTo(ph2);
249         int comp21 = ph2.compareTo(ph1);
250         assertEquals("comp ph", comp12, -comp21);
251     }
252 
TestVariant()253     public void TestVariant() {
254         PathHeader p1 = pathHeaderFactory
255             .fromPath("//ldml/localeDisplayNames/languages/language[@type=\"ug\"][@alt=\"variant\"]");
256         PathHeader p2 = pathHeaderFactory
257             .fromPath("//ldml/localeDisplayNames/languages/language[@type=\"ug\"]");
258         assertNotEquals("variants", p1, p2);
259         assertNotEquals("variants", p1.toString(), p2.toString());
260         // Code Lists Languages Arabic Script ug-variant
261     }
262 
Test4587()263     public void Test4587() {
264         String test = "//ldml/dates/timeZoneNames/metazone[@type=\"Pacific/Wallis\"]/short/standard";
265         PathHeader ph = pathHeaderFactory.fromPath(test);
266         if (ph == null) {
267             errln("Failure with " + test);
268         } else {
269             logln(ph + "\t" + test);
270         }
271     }
272 
TestMiscPatterns()273     public void TestMiscPatterns() {
274         String test = "//ldml/numbers/miscPatterns[@numberSystem=\"arab\"]/pattern[@type=\"atLeast\"]";
275         PathHeader ph = pathHeaderFactory.fromPath(test);
276         assertNotNull("MiscPatterns path not found", ph);
277         if (false)
278             System.out.println(english.getStringValue(test));
279     }
280 
TestPluralOrder()281     public void TestPluralOrder() {
282         Set<PathHeader> sorted = new TreeSet<>();
283         for (String locale : new String[] { "ru", "ar", "ja" }) {
284             sorted.clear();
285             CLDRFile cldrFile = info.getCLDRFile(locale, true);
286             CoverageLevel2 coverageLevel = CoverageLevel2.getInstance(locale);
287             for (String path : cldrFile.fullIterable()) {
288                 if (!path.contains("@count")) {
289                     continue;
290                 }
291                 Level level = coverageLevel.getLevel(path);
292                 if (Level.MODERN.compareTo(level) < 0) {
293                     continue;
294                 }
295                 PathHeader p = pathHeaderFactory.fromPath(path);
296                 sorted.add(p);
297             }
298             for (PathHeader p : sorted) {
299                 logln(locale + "\t" + p + "\t" + p.getOriginalPath());
300             }
301         }
302     }
303 
304     static final String APPEND_TIMEZONE = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/appendItems/appendItem[@request=\"Timezone\"]";
305     static final String APPEND_TIMEZONE_END = "/dateTimeFormats/appendItems/appendItem[@request=\"Timezone\"]";
306     static final String BEFORE_PH = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\"ms\"]";
307     static final String AFTER_PH = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/intervalFormats/intervalFormatItem[@id=\"d\"]/greatestDifference[@id=\"d\"]";
308 
TestAppendTimezone()309     public void TestAppendTimezone() {
310         CLDRFile cldrFile = info.getEnglish();
311         CoverageLevel2 coverageLevel = CoverageLevel2.getInstance("en");
312         assertEquals("appendItem:Timezone", Level.MODERATE,
313             coverageLevel.getLevel(APPEND_TIMEZONE));
314 
315         PathHeader ph = pathHeaderFactory.fromPath(APPEND_TIMEZONE);
316         assertEquals("appendItem:Timezone pathheader", "Timezone", ph.getCode());
317         // check that they are in the right place (they weren't before!)
318         PathHeader phBefore = pathHeaderFactory.fromPath(BEFORE_PH);
319         PathHeader phAfter = pathHeaderFactory.fromPath(AFTER_PH);
320         assertTrue(phBefore, LEQ, ph);
321         assertTrue(ph, LEQ, phAfter);
322 
323         PathDescription pathDescription = new PathDescription(supplemental,
324             english, null, null, PathDescription.ErrorHandling.CONTINUE);
325         String description = pathDescription.getDescription(APPEND_TIMEZONE,
326             "tempvalue", null, null);
327         assertTrue("appendItem:Timezone pathDescription",
328             description.contains("“Timezone”"));
329 
330         PatternPlaceholders patternPlaceholders = PatternPlaceholders
331             .getInstance();
332         PlaceholderStatus status = patternPlaceholders
333             .getStatus(APPEND_TIMEZONE);
334         assertEquals("appendItem:Timezone placeholders",
335             PlaceholderStatus.REQUIRED, status);
336 
337         Map<String, PlaceholderInfo> placeholderInfo = patternPlaceholders
338             .get(APPEND_TIMEZONE);
339         PlaceholderInfo placeholderInfo2 = placeholderInfo.get("{1}");
340         if (assertNotNull("appendItem:Timezone placeholders", placeholderInfo2)) {
341             assertEquals("appendItem:Timezone placeholders",
342                 "APPEND_FIELD_FORMAT", placeholderInfo2.name);
343             assertEquals("appendItem:Timezone placeholders", "Pacific Time",
344                 placeholderInfo2.example);
345         }
346         ExampleGenerator eg = new ExampleGenerator(cldrFile, cldrFile, CLDRPaths.SUPPLEMENTAL_DIRECTORY);
347         String example = eg.getExampleHtml(APPEND_TIMEZONE, cldrFile.getStringValue(APPEND_TIMEZONE));
348         String result = ExampleGenerator.simplify(example, false);
349         assertEquals("", "〖❬6:25:59 PM❭ ❬GMT❭〗", result);
350     }
351 
TestOptional()352     public void TestOptional() {
353         if (true) return;
354         Map<PathHeader, String> sorted = new TreeMap<>();
355         for (String locale : new String[] { "af" }) {
356             sorted.clear();
357             CLDRFile cldrFile = info.getCLDRFile(locale, true);
358             CoverageLevel2 coverageLevel = CoverageLevel2.getInstance(locale);
359             for (String path : cldrFile.fullIterable()) {
360                 Level level = coverageLevel.getLevel(path);
361                 if (supplemental.isDeprecated(DtdType.ldml, path)) {
362                     continue;
363                 }
364 
365                 if (Level.COMPREHENSIVE.compareTo(level) != 0) {
366                     continue;
367                 }
368 
369                 PathHeader ph = pathHeaderFactory.fromPath(path);
370                 if (ph == null || ph.shouldHide()) {
371                     continue;
372                 }
373                 final SurveyToolStatus status = ph.getSurveyToolStatus();
374                 sorted.put(
375                     ph,
376                     locale + "\t" + status + "\t" + ph + "\t"
377                         + ph.getOriginalPath());
378             }
379             Set<String> codes = new LinkedHashSet<>();
380             PathHeader old = null;
381             String line = null;
382             for (Entry<PathHeader, String> s : sorted.entrySet()) {
383                 PathHeader p = s.getKey();
384                 String v = s.getValue();
385                 if (old == null) {
386                     line = v;
387                     codes.add(p.getCode());
388                 } else if (p.getSectionId() == old.getSectionId()
389                     && p.getPageId() == old.getPageId()
390                     && p.getHeader().equals(old.getHeader())) {
391                     codes.add(p.getCode());
392                 } else {
393                     logln(line + "\t" + codes.toString());
394                     codes.clear();
395                     line = v;
396                     codes.add(p.getCode());
397                 }
398                 old = p;
399             }
400             logln(line + "\t" + codes.toString());
401         }
402     }
403 
TestPluralCanonicals()404     public void TestPluralCanonicals() {
405         Relation<String, String> data = Relation.of(
406             new LinkedHashMap<String, Set<String>>(), TreeSet.class);
407         for (String locale : factory.getAvailable()) {
408             if (locale.contains("_")) {
409                 continue;
410             }
411             PluralInfo info = supplemental.getPlurals(PluralType.cardinal,
412                 locale);
413             Set<String> keywords = info.getCanonicalKeywords();
414             data.put(keywords.toString(), locale);
415         }
416         for (Entry<String, Set<String>> entry : data.keyValuesSet()) {
417             logln(entry.getKey() + "\t" + entry.getValue());
418         }
419     }
420 
TestPluralPaths()421     public void TestPluralPaths() {
422         // do the following line once, when the file is opened
423         Set<String> filePaths = pathHeaderFactory.pathsForFile(english);
424 
425         // check that English doesn't contain few or many
426         verifyContains(PageId.Duration, filePaths, "few", false);
427         verifyContains(PageId.C_NAmerica, filePaths, "many", false);
428         verifyContains(PageId.C_SAmerica, filePaths, "many", false);
429         verifyContains(PageId.C_NWEurope, filePaths, "many", false);
430         verifyContains(PageId.C_SEEurope, filePaths, "many", false);
431         verifyContains(PageId.C_NAfrica, filePaths, "many", false);
432         verifyContains(PageId.C_WAfrica, filePaths, "many", false);
433         verifyContains(PageId.C_SAfrica, filePaths, "many", false);
434         verifyContains(PageId.C_EAfrica, filePaths, "many", false);
435         verifyContains(PageId.C_CAsia, filePaths, "many", false);
436         verifyContains(PageId.C_WAsia, filePaths, "many", false);
437         verifyContains(PageId.C_SEAsia, filePaths, "many", false);
438         verifyContains(PageId.C_Oceania, filePaths, "many", false);
439         verifyContains(PageId.C_Unknown, filePaths, "many", false);
440 
441         // check that Arabic does contain few and many
442         filePaths = pathHeaderFactory.pathsForFile(info.getCLDRFile("ar", true));
443 
444         verifyContains(PageId.Duration, filePaths, "few", true);
445         verifyContains(PageId.C_NAmerica, filePaths, "many", true);
446         verifyContains(PageId.C_SAmerica, filePaths, "many", true);
447         verifyContains(PageId.C_NWEurope, filePaths, "many", true);
448         verifyContains(PageId.C_SEEurope, filePaths, "many", true);
449         verifyContains(PageId.C_NAfrica, filePaths, "many", true);
450         verifyContains(PageId.C_WAfrica, filePaths, "many", true);
451         verifyContains(PageId.C_SAfrica, filePaths, "many", true);
452         verifyContains(PageId.C_EAfrica, filePaths, "many", true);
453         verifyContains(PageId.C_CAsia, filePaths, "many", true);
454         verifyContains(PageId.C_WAsia, filePaths, "many", true);
455         verifyContains(PageId.C_SEAsia, filePaths, "many", true);
456         verifyContains(PageId.C_Oceania, filePaths, "many", true);
457         verifyContains(PageId.C_Unknown, filePaths, "many", true);
458     }
459 
TestCoverage()460     public void TestCoverage() {
461         Map<Row.R2<SectionId, PageId>, Counter<Level>> data = new TreeMap<>();
462         CLDRFile cldrFile = english;
463         for (String path : cldrFile.fullIterable()) {
464             if (supplemental.isDeprecated(DtdType.ldml, path)) {
465                 errln("Deprecated path in English: " + path);
466                 continue;
467             }
468             Level level = supplemental.getCoverageLevel(path,
469                 cldrFile.getLocaleID());
470             PathHeader p = pathHeaderFactory.fromPath(path);
471             SurveyToolStatus status = p.getSurveyToolStatus();
472 
473             boolean hideCoverage = level == Level.COMPREHENSIVE;
474             boolean hidePathHeader = p.shouldHide();
475             if (hidePathHeader != hideCoverage) {
476                 String message = "PathHeader: " + status + ", Coverage: "
477                     + level + ": " + path;
478                 if (hidePathHeader && !hideCoverage) {
479                     errln(message);
480                 } else if (!hidePathHeader && hideCoverage) {
481                     logln(message);
482                 }
483             }
484             final R2<SectionId, PageId> key = Row.of(p.getSectionId(),
485                 p.getPageId());
486             Counter<Level> counter = data.get(key);
487             if (counter == null) {
488                 data.put(key, counter = new Counter<>());
489             }
490             counter.add(level, 1);
491         }
492         StringBuffer b = new StringBuffer("\t");
493         for (Level level : Level.values()) {
494             b.append("\t" + level);
495         }
496         logln(b.toString());
497         for (Entry<R2<SectionId, PageId>, Counter<Level>> entry : data
498             .entrySet()) {
499             b.setLength(0);
500             b.append(entry.getKey().get0() + "\t" + entry.getKey().get1());
501             Counter<Level> counter = entry.getValue();
502             long total = 0;
503             for (Level level : Level.values()) {
504                 total += counter.getCount(level);
505                 b.append("\t" + total);
506             }
507             logln(b.toString());
508         }
509     }
510 
Test00AFile()511     public void Test00AFile() {
512         final String localeId = "en";
513         Counter<Level> counter = new Counter<>();
514         Map<String, PathHeader> uniqueness = new HashMap<>();
515         Set<String> alreadySeen = new HashSet<>();
516         check(localeId, true, uniqueness, alreadySeen);
517         // check paths
518         for (Entry<SectionId, Set<PageId>> sectionAndPages : PathHeader.Factory
519             .getSectionIdsToPageIds().keyValuesSet()) {
520             final SectionId section = sectionAndPages.getKey();
521             if (section == SectionId.Supplemental || section == SectionId.BCP47) {
522                 continue;
523             }
524             logln(section.toString());
525             for (PageId page : sectionAndPages.getValue()) {
526                 final Set<String> cachedPaths = PathHeader.Factory
527                     .getCachedPaths(section, page);
528                 if (cachedPaths == null) {
529                     if (!badZonePages.contains(page) && page != PageId.Unknown) {
530                         errln("Null pages for: " + section + "\t" + page);
531                     }
532                 } else if (section == SectionId.Special
533                     && page == PageId.Unknown) {
534                     // skip
535                 } else if (section == SectionId.Timezones
536                     && page == PageId.UnknownT) {
537                     // skip
538                 } else if (section == SectionId.Misc
539                     && page == PageId.Transforms) {
540                     // skip
541                 } else {
542 
543                     int count2 = cachedPaths.size();
544                     if (count2 == 0) {
545                         warnln("Missing pages for: " + section + "\t" + page);
546                     } else {
547                         counter.clear();
548                         for (String s : cachedPaths) {
549                             Level coverage = supplemental.getCoverageLevel(s,
550                                 localeId);
551                             counter.add(coverage, 1);
552                         }
553                         String countString = "";
554                         int total = 0;
555                         for (Level item : Level.values()) {
556                             long count = counter.get(item);
557                             if (count != 0) {
558                                 if (!countString.isEmpty()) {
559                                     countString += ",\t+";
560                                 }
561                                 total += count;
562                                 countString += item + "=" + total;
563                             }
564                         }
565                         logln("\t" + page + "\t" + countString);
566                         if (page.toString().startsWith("Unknown")) {
567                             logln("\t\t" + cachedPaths);
568                         }
569                     }
570                 }
571             }
572         }
573     }
574 
TestMetazones()575     public void TestMetazones() {
576 
577         CLDRFile nativeFile = info.getEnglish();
578         Set<PathHeader> pathHeaders = getPathHeaders(nativeFile);
579         // String oldPage = "";
580         String oldHeader = "";
581         for (PathHeader entry : pathHeaders) {
582             final String page = entry.getPage();
583             // if (!oldPage.equals(page)) {
584             // logln(page);
585             // oldPage = page;
586             // }
587             String header = entry.getHeader();
588             if (!oldHeader.equals(header)) {
589                 logln(page + "\t" + header);
590                 oldHeader = header;
591             }
592         }
593     }
594 
getPathHeaders(CLDRFile nativeFile)595     public Set<PathHeader> getPathHeaders(CLDRFile nativeFile) {
596         Set<PathHeader> pathHeaders = new TreeSet<>();
597         for (String path : nativeFile.fullIterable()) {
598             PathHeader p = pathHeaderFactory.fromPath(path);
599             pathHeaders.add(p);
600         }
601         return pathHeaders;
602     }
603 
verifyContains(PageId pageId, Set<String> filePaths, String substring, boolean contains)604     public void verifyContains(PageId pageId, Set<String> filePaths,
605         String substring, boolean contains) {
606         String path;
607         path = findOneContaining(allPaths(pageId, filePaths), substring);
608         if (contains) {
609             if (path == null) {
610                 errln("No path contains <" + substring + ">");
611             }
612         } else {
613             if (path != null) {
614                 errln("Path contains <" + substring + ">\t" + path);
615             }
616         }
617     }
618 
findOneContaining(Collection<String> allPaths, String substring)619     private String findOneContaining(Collection<String> allPaths,
620         String substring) {
621         for (String path : allPaths) {
622             if (path.contains(substring)) {
623                 return path;
624             }
625         }
626         return null;
627     }
628 
allPaths(PageId pageId, Set<String> filePaths)629     public Set<String> allPaths(PageId pageId, Set<String> filePaths) {
630         Set<String> result = PathHeader.Factory.getCachedPaths(
631             pageId.getSectionId(), pageId);
632         result.retainAll(filePaths);
633         return result;
634     }
635 
TestUniqueness()636     public void TestUniqueness() {
637         Factory factory2 = CLDRConfig.getInstance().getMainAndAnnotationsFactory();
638         Set<String> source = factory2.getAvailable();
639         for (String file : getFilesToTest(source, MIN_LOCALES)) {
640             CLDRFile nativeFile = factory2.make(file,true);
641             Map<PathHeader, String> headerToPath = new HashMap<>();
642             Map<String, String> headerVisibleToPath = new HashMap<>();
643             for (String path : nativeFile.fullIterable()) {
644                 PathHeader p = pathHeaderFactory.fromPath(path);
645                 if (p.getSectionId() == SectionId.Special) {
646                     continue;
647                 }
648                 String old = headerToPath.get(p);
649                 if (old == null) {
650                     headerToPath.put(p, path);
651                 } else if (!old.equals(path)) {
652                     if (true) { // for debugging
653                         pathHeaderFactory.clearCache();
654                         List<String> failuresOld = new ArrayList<>();
655                         pathHeaderFactory.fromPath(old, failuresOld);
656                         List<String> failuresPath = new ArrayList<>();
657                         pathHeaderFactory.fromPath(path, failuresPath);
658                     }
659                     errln(file + " collision with path " + p + "\t" + old + "\t" + path);
660                 }
661                 final String visible = p.toString();
662                 old = headerVisibleToPath.get(visible);
663                 if (old == null) {
664                     headerVisibleToPath.put(visible, path);
665                 } else if (!old.equals(path)) {
666                     errln("Collision with path " + visible + "\t" + old + "\t"
667                         + path);
668                 }
669             }
670         }
671     }
672 
TestStatus()673     public void TestStatus() {
674         CLDRFile nativeFile = info.getEnglish();
675         PathStarrer starrer = new PathStarrer();
676         EnumMap<SurveyToolStatus, Relation<String, String>> info2 = new EnumMap<>(
677             SurveyToolStatus.class);
678         Set<String> nuked = new HashSet<>();
679         Set<String> deprecatedStar = new HashSet<>();
680 
681         for (String path : nativeFile.fullIterable()) {
682 
683             PathHeader p = pathHeaderFactory.fromPath(path);
684             final SurveyToolStatus surveyToolStatus = p.getSurveyToolStatus();
685 
686             if (p.getSectionId() == SectionId.Special
687                 && surveyToolStatus == SurveyToolStatus.READ_WRITE) {
688                 errln("SurveyToolStatus should not be " + surveyToolStatus
689                     + ": " + p);
690             }
691 
692             String starred = starrer.set(path);
693             List<String> attr = starrer.getAttributes();
694             if (surveyToolStatus != SurveyToolStatus.READ_WRITE) {
695                 nuked.add(starred);
696             }
697 
698             // check against deprecated
699             boolean isDeprecated = supplemental.isDeprecated(DtdType.ldml, path);
700             if (isDeprecated != (surveyToolStatus == SurveyToolStatus.DEPRECATED)) {
701                 if (!deprecatedStar.contains(starred)) {
702                     errln("Different from DtdData deprecated:\t"
703                         + isDeprecated + "\t" + surveyToolStatus + "\t"
704                         + path);
705                     deprecatedStar.add(starred);
706                 }
707             }
708 
709             Relation<String, String> data = info2.get(surveyToolStatus);
710             if (data == null) {
711                 info2.put(
712                     surveyToolStatus,
713                     data = Relation.of(new TreeMap<String, Set<String>>(),
714                         TreeSet.class));
715             }
716             data.put(starred, Joiner.on("|").join(attr));
717         }
718         for (Entry<SurveyToolStatus, Relation<String, String>> entry : info2
719             .entrySet()) {
720             final SurveyToolStatus status = entry.getKey();
721             for (Entry<String, Set<String>> item : entry.getValue()
722                 .keyValuesSet()) {
723                 final String starred = item.getKey();
724                 if (status == SurveyToolStatus.READ_WRITE
725                     && !nuked.contains(starred)) {
726                     continue;
727                 }
728                 logln(status + "\t" + starred + "\t" + item.getValue());
729             }
730         }
731     }
732 
TestPathsNotInEnglish()733     public void TestPathsNotInEnglish() {
734         Set<String> englishPaths = new HashSet<>();
735         for (String path : english.fullIterable()) {
736             englishPaths.add(path);
737         }
738         Set<String> alreadySeen = new HashSet<>(englishPaths);
739 
740         for (String locale : factory.getAvailable()) {
741             CLDRFile nativeFile = info.getCLDRFile(locale, false);
742             CoverageLevel2 coverageLevel2 = null;
743             for (String path : nativeFile.fullIterable()) {
744                 if (alreadySeen.contains(path) || path.contains("@count")) {
745                     continue;
746                 }
747                 if (coverageLevel2 == null) {
748                     coverageLevel2 = CoverageLevel2.getInstance(locale);
749                 }
750                 Level level = coverageLevel2.getLevel(path);
751                 if (Level.COMPREHENSIVE.compareTo(level) < 0) {
752                     continue;
753                 }
754                 logln("Path not in English\t" + locale + "\t" + path);
755                 alreadySeen.add(path);
756             }
757         }
758     }
759 
TestPathDescriptionCompleteness()760     public void TestPathDescriptionCompleteness() {
761         PathDescription pathDescription = new PathDescription(supplemental,
762             english, null, null, PathDescription.ErrorHandling.CONTINUE);
763         Matcher normal = PatternCache.get(
764             "https://cldr.unicode.org/translation/[-a-zA-Z0-9_]").matcher("");
765         // https://cldr.unicode.org/translation/plurals#TOC-Minimal-Pairs
766         Set<String> alreadySeen = new HashSet<>();
767         PathStarrer starrer = new PathStarrer();
768 
769         checkPathDescriptionCompleteness(pathDescription, normal,
770             "//ldml/numbers/defaultNumberingSystem", alreadySeen, starrer);
771         for (PathHeader pathHeader : getPathHeaders(english)) {
772             if (pathHeader.shouldHide()) {
773                 continue;
774             }
775             String path = pathHeader.getOriginalPath();
776             checkPathDescriptionCompleteness(pathDescription, normal, path,
777                 alreadySeen, starrer);
778         }
779     }
780 
checkPathDescriptionCompleteness( PathDescription pathDescription, Matcher normal, String path, Set<String> alreadySeen, PathStarrer starrer)781     public void checkPathDescriptionCompleteness(
782         PathDescription pathDescription, Matcher normal, String path,
783         Set<String> alreadySeen, PathStarrer starrer) {
784         String value = english.getStringValue(path);
785         String description = pathDescription.getDescription(path, value, null,
786             null);
787         String starred = starrer.set(path);
788         if (alreadySeen.contains(starred)) {
789             return;
790         } else if (description == null) {
791             errln("Path has no description:\t" + value + "\t" + path);
792         } else if (!description.contains("https://")) {
793             errln("Description has no URL:\t" + description + "\t" + value
794                 + "\t" + path);
795         } else if (!normal.reset(description).find()) {
796             errln("Description has generic URL, fix to be specific:\t"
797                 + description + "\t" + value + "\t" + path);
798         } else if (description == PathDescription.MISSING_DESCRIPTION) {
799             errln("Fallback Description:\t" + value + "\t" + path);
800         } else {
801             return;
802         }
803         // Add if we had a problem, keeping us from being overwhelmed with
804         // errors.
805         alreadySeen.add(starred);
806     }
807 
TestTerritoryOrder()808     public void TestTerritoryOrder() {
809         final Set<String> goodAvailableCodes = StandardCodes.make().getGoodAvailableCodes("territory");
810         Set<String> results = showContained("001", 0, new HashSet<>(
811             goodAvailableCodes));
812         results.remove("ZZ");
813         for (String territory : results) {
814             String sub = Containment.getSubcontinent(territory);
815             String cont = Containment.getContinent(territory);
816             errln("Missing\t" + getNameAndOrder(territory) + "\t"
817                 + getNameAndOrder(sub) + "\t" + getNameAndOrder(cont));
818         }
819     }
820 
showContained(String territory, int level, Set<String> soFar)821     private Set<String> showContained(String territory, int level,
822         Set<String> soFar) {
823         if (!soFar.contains(territory)) {
824             return soFar;
825         }
826         soFar.remove(territory);
827         Set<String> contained = supplemental.getContained(territory);
828         if (contained == null) {
829             return soFar;
830         }
831         for (String containedItem : contained) {
832             logln(level + "\t" + getNameAndOrder(territory) + "\t"
833                 + getNameAndOrder(containedItem));
834         }
835         for (String containedItem : contained) {
836             showContained(containedItem, level + 1, soFar);
837         }
838         return soFar;
839     }
840 
getNameAndOrder(String territory)841     private String getNameAndOrder(String territory) {
842         return territory + "\t"
843             + english.getName(CLDRFile.TERRITORY_NAME, territory) + "\t"
844             + Containment.getOrder(territory);
845     }
846 
TestZCompleteness()847     public void TestZCompleteness() {
848         Map<String, PathHeader> uniqueness = new HashMap<>();
849         Set<String> alreadySeen = new HashSet<>();
850         LanguageTagParser ltp = new LanguageTagParser();
851         int count = 0;
852         for (String locale : factory.getAvailable()) {
853             if (!ltp.set(locale).getRegion().isEmpty()) {
854                 continue;
855             }
856             check(locale, false, uniqueness, alreadySeen);
857             ++count;
858         }
859         logln("Count:\t" + count);
860     }
861 
check(String localeID, boolean resolved, Map<String, PathHeader> uniqueness, Set<String> alreadySeen)862     public void check(String localeID, boolean resolved,
863         Map<String, PathHeader> uniqueness, Set<String> alreadySeen) {
864         CLDRFile nativeFile = info.getCLDRFile(localeID, resolved);
865         int count = 0;
866         for (String path : nativeFile) {
867             if (alreadySeen.contains(path)) {
868                 continue;
869             }
870             alreadySeen.add(path);
871             final PathHeader pathHeader = pathHeaderFactory.fromPath(path);
872             ++count;
873             if (pathHeader == null) {
874                 errln("Null pathheader for " + path);
875             } else {
876                 String visible = pathHeader.toString();
877                 PathHeader old = uniqueness.get(visible);
878                 if (pathHeader.getSectionId() == SectionId.Timezones) {
879                     final PageId pageId = pathHeader.getPageId();
880                     if (badZonePages.contains(pageId)
881                         && !pathHeader.getCode().equals("Unknown")) {
882                         String msg = "Bad page ID:\t" + pageId + "\t" + pathHeader + "\t" + path;
883                         if (!logKnownIssue("cldrbug:7802", "ICU/CLDR time zone data sync problem - " + msg)) {
884                             errln("Bad page ID:\t" + pageId + "\t" + pathHeader
885                                 + "\t" + path);
886                         }
887                     }
888                 }
889                 if (old == null) {
890                     if (pathHeader.getSection().equals("Special")) {
891                         if (pathHeader.getSection().equals("Unknown")) {
892                             errln("PathHeader has fallback: " + visible + "\t"
893                                 + pathHeader.getOriginalPath());
894                             // } else {
895                             // logln("Special:\t" + visible + "\t" +
896                             // pathHeader.getOriginalPath());
897                         }
898                     }
899                     uniqueness.put(visible, pathHeader);
900                 } else if (!old.equals(pathHeader)) {
901                     if (pathHeader.getSectionId() == SectionId.Special) {
902                         logln("Special PathHeader not unique: " + visible
903                             + "\t" + pathHeader.getOriginalPath() + "\t"
904                             + old.getOriginalPath());
905                     } else {
906                         errln("PathHeader not unique: " + visible + "\t"
907                             + pathHeader.getOriginalPath() + "\t"
908                             + old.getOriginalPath());
909                     }
910                 }
911             }
912         }
913         logln(localeID + "\t" + count);
914     }
915 
TestContainment()916     public void TestContainment() {
917         Map<String, Map<String, String>> metazoneToRegionToZone = supplemental
918             .getMetazoneToRegionToZone();
919         Map<String, String> metazoneToContinent = supplemental
920             .getMetazoneToContinentMap();
921         for (String metazone : metazoneToRegionToZone.keySet()) {
922             Map<String, String> regionToZone = metazoneToRegionToZone
923                 .get(metazone);
924             String worldZone = regionToZone.get("001");
925             String territory = Containment.getRegionFromZone(worldZone);
926             if (territory == null) {
927                 territory = "ZZ";
928             }
929             String cont = Containment.getContinent(territory);
930             int order = Containment.getOrder(territory);
931             String sub = Containment.getSubcontinent(territory);
932             String revision = PathHeader.getMetazonePageTerritory(metazone);
933             String continent = metazoneToContinent.get(metazone);
934             if (continent == null) {
935                 continent = "UnknownT";
936             }
937             // Russia, Antarctica => territory
938             // in Australasia, Asia, S. America => subcontinent
939             // in N. America => N. America (grouping of 3 subcontinents)
940             // in everything else => continent
941 
942             if (territory.equals("RU")) {
943                 assertEquals("Russia special case", "RU", revision);
944             } else if (territory.equals("US")) {
945                 assertEquals("N. America special case", "003", revision);
946             } else if (territory.equals("BR")) {
947                 assertEquals("S. America special case", "005", revision);
948             }
949             if (isVerbose()) {
950                 String name = english.getName(CLDRFile.TERRITORY_NAME, cont);
951                 String name2 = english.getName(CLDRFile.TERRITORY_NAME, sub);
952                 String name3 = english.getName(CLDRFile.TERRITORY_NAME,
953                     territory);
954                 String name4 = english.getName(CLDRFile.TERRITORY_NAME,
955                     revision);
956 
957                 logln(metazone + "\t" + continent + "\t" + name + "\t" + name2
958                     + "\t" + name3 + "\t" + order + "\t" + name4);
959             }
960         }
961     }
962 
TestZ()963     public void TestZ() {
964         PathStarrer pathStarrer = new PathStarrer();
965         pathStarrer.setSubstitutionPattern("%A");
966 
967         Set<PathHeader> sorted = new TreeSet<>();
968         Map<String, String> missing = new TreeMap<>();
969         Map<String, String> skipped = new TreeMap<>();
970         Map<String, String> collide = new TreeMap<>();
971 
972         logln("Traversing Paths");
973         for (String path : english) {
974             PathHeader pathHeader = pathHeaderFactory.fromPath(path);
975             String value = english.getStringValue(path);
976             if (pathHeader == null) {
977                 final String starred = pathStarrer.set(path);
978                 missing.put(starred, value + "\t" + path);
979                 continue;
980             }
981             if (pathHeader.getSection().equalsIgnoreCase("skip")) {
982                 final String starred = pathStarrer.set(path);
983                 skipped.put(starred, value + "\t" + path);
984                 continue;
985             }
986             sorted.add(pathHeader);
987         }
988         logln("\nConverted:\t" + sorted.size());
989         String lastHeader = "";
990         String lastPage = "";
991         String lastSection = "";
992         List<String> threeLevel = new ArrayList<>();
993         Status status = new Status();
994         CoverageLevel2 coverageLevel2 = CoverageLevel2.getInstance("en");
995 
996         for (PathHeader pathHeader : sorted) {
997             String original = pathHeader.getOriginalPath();
998             if (!original.equals(status.pathWhereFound)) {
999                 continue;
1000             }
1001             if (!lastSection.equals(pathHeader.getSection())) {
1002                 logln("");
1003                 threeLevel.add(pathHeader.getSection());
1004                 threeLevel.add("\t" + pathHeader.getPage());
1005                 threeLevel.add("\t\t" + pathHeader.getHeader());
1006                 lastSection = pathHeader.getSection();
1007                 lastPage = pathHeader.getPage();
1008                 lastHeader = pathHeader.getHeader();
1009             } else if (!lastPage.equals(pathHeader.getPage())) {
1010                 logln("");
1011                 threeLevel.add("\t" + pathHeader.getPage());
1012                 threeLevel.add("\t\t" + pathHeader.getHeader());
1013                 lastPage = pathHeader.getPage();
1014                 lastHeader = pathHeader.getHeader();
1015             } else if (!lastHeader.equals(pathHeader.getHeader())) {
1016                 logln("");
1017                 threeLevel.add("\t\t" + pathHeader.getHeader());
1018                 lastHeader = pathHeader.getHeader();
1019             }
1020             logln(pathHeader + "\t" + coverageLevel2.getLevel(original) + "\t"
1021                 + english.getStringValue(pathHeader.getOriginalPath())
1022                 + "\t" + pathHeader.getOriginalPath());
1023         }
1024         if (collide.size() != 0) {
1025             errln("\nCollide:\t" + collide.size());
1026             for (Entry<String, String> item : collide.entrySet()) {
1027                 errln("\t" + item);
1028             }
1029         }
1030         if (missing.size() != 0) {
1031             errln("\nMissing:\t" + missing.size());
1032             for (Entry<String, String> item : missing.entrySet()) {
1033                 errln("\t" + item.getKey() + "\tvalue:\t" + item.getValue());
1034             }
1035         }
1036         if (skipped.size() != 0) {
1037             errln("\nSkipped:\t" + skipped.size());
1038             for (Entry<String, String> item : skipped.entrySet()) {
1039                 errln("\t" + item);
1040             }
1041         }
1042         Counter<PathHeader.Factory.CounterData> counterData = pathHeaderFactory
1043             .getInternalCounter();
1044         logln("\nInternal Counter:\t" + counterData.size());
1045         for (PathHeader.Factory.CounterData item : counterData.keySet()) {
1046             logln("\t" + counterData.getCount(item) + "\t" + item.get2() // externals
1047             + "\t" + item.get3() + "\t" + item.get0() // internals
1048             + "\t" + item.get1());
1049         }
1050         logln("\nMenus/Headers:\t" + threeLevel.size());
1051         for (String item : threeLevel) {
1052             logln(item);
1053         }
1054         LinkedHashMap<String, Set<String>> sectionsToPages = org.unicode.cldr.util.PathHeader.Factory
1055             .getSectionsToPages();
1056         logln("\nMenus:\t" + sectionsToPages.size());
1057         for (Entry<String, Set<String>> item : sectionsToPages.entrySet()) {
1058             final String section = item.getKey();
1059             for (String page : item.getValue()) {
1060                 logln("\t" + section + "\t" + page);
1061                 int count = 0;
1062                 for (String path : pathHeaderFactory.filterCldr(section, page,
1063                     english)) {
1064                     count += 1; // just count them.
1065                 }
1066                 logln("\t" + count);
1067             }
1068         }
1069     }
1070 
1071     public static final Set<String> GERMAN_UNIT_ORDER = ImmutableSet.of(
1072         "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]",
1073         "//ldml/units/unitLength[@type=\"short\"]/compoundUnit[@type=\"power2\"]",
1074         "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]",
1075         "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]",
1076         "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-liter\"]",
1077         "//ldml/units/unitLength[@type=\"narrrow\"]/unit[@type=\"volume-liter\"]",
1078         "//ldml/numbers/minimalPairs/caseMinimalPairs",
1079         "//ldml/numbers/minimalPairs/genderMinimalPairs"
1080         );
TestOrder()1081     public void TestOrder() {
1082         String[] paths = {
1083             "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/dayPeriodContext[@type=\"format\"]/dayPeriodWidth[@type=\"narrow\"]/dayPeriod[@type=\"noon\"]",
1084             "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/dayPeriodContext[@type=\"format\"]/dayPeriodWidth[@type=\"narrow\"]/dayPeriod[@type=\"afternoon1\"]",
1085         };
1086         PathHeader pathHeaderLast = null;
1087         for (String path : paths) {
1088             PathHeader pathHeader = pathHeaderFactory.fromPath(path);
1089             if (pathHeaderLast != null) {
1090                 assertRelation("ordering", true, pathHeaderLast, LEQ, pathHeader);
1091             }
1092             pathHeaderLast = pathHeader;
1093         }
1094         CLDRFile german = factory.make("de", true);
1095         Multimap<PathHeader, String> pathHeaderToPaths = TreeMultimap.create();
1096         for (String path : german.fullIterable()) {
1097             for (String prefix : GERMAN_UNIT_ORDER) {
1098                 if (path.startsWith(prefix)) {
1099                     PathHeader pathHeader = pathHeaderFactory.fromPath(path);
1100                     pathHeaderToPaths.put(pathHeader, path);
1101                 }
1102             }
1103         }
1104         String[] germanExpected = {
1105             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/gender",  // Units    Volume  liter   long-gender
1106 
1107             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/displayName", // Units    Volume  liter   long-displayName
1108             "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-liter\"]/displayName",    // Units    Volume  liter   short-displayName
1109 
1110             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/perUnitPattern",  // Units    Volume  liter   long-per
1111             "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-liter\"]/perUnitPattern", // Units    Volume  liter   short-per
1112 
1113             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"]", // Units    Volume  liter   long-one-nominative
1114             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"][@case=\"accusative\"]",   // Units    Volume  liter   long-one-accusative
1115             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"][@case=\"genitive\"]", // Units    Volume  liter   long-one-genitive
1116             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"][@case=\"dative\"]",   // Units    Volume  liter   long-one-dative
1117             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"other\"]",   // Units    Volume  liter   long-other-nominative
1118             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"other\"][@case=\"accusative\"]", // Units    Volume  liter   long-other-accusative
1119             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"other\"][@case=\"genitive\"]",   // Units    Volume  liter   long-other-genitive
1120             "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"other\"][@case=\"dative\"]", // Units    Volume  liter   long-other-dative
1121             "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"]",    // Units    Volume  liter   short-one-nominative
1122             "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"other\"]",  // Units    Volume  liter   short-other-nominative
1123 
1124             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"masculine\"]",   // Units    Compound Units  power2  long-one-nominative-masculine
1125             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"feminine\"]",    // Units    Compound Units  power2  long-one-nominative-feminine
1126             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",  // Units    Compound Units  power2  long-one-nominative-dgender
1127             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"masculine\"][@case=\"accusative\"]", // Units    Compound Units  power2  long-one-accusative-masculine
1128             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"feminine\"][@case=\"accusative\"]",  // Units    Compound Units  power2  long-one-accusative-feminine
1129             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@case=\"accusative\"]",    // Units    Compound Units  power2  long-one-accusative-dgender
1130             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"masculine\"][@case=\"genitive\"]",   // Units    Compound Units  power2  long-one-genitive-masculine
1131             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"feminine\"][@case=\"genitive\"]",    // Units    Compound Units  power2  long-one-genitive-feminine
1132             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@case=\"genitive\"]",  // Units    Compound Units  power2  long-one-genitive-dgender
1133             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"masculine\"][@case=\"dative\"]", // Units    Compound Units  power2  long-one-dative-masculine
1134             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"feminine\"][@case=\"dative\"]",  // Units    Compound Units  power2  long-one-dative-feminine
1135             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@case=\"dative\"]",    // Units    Compound Units  power2  long-one-dative-dgender
1136             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"masculine\"]", // Units    Compound Units  power2  long-other-nominative-masculine
1137             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"feminine\"]",  // Units    Compound Units  power2  long-other-nominative-feminine
1138             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",    // Units    Compound Units  power2  long-other-nominative-dgender
1139             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"masculine\"][@case=\"accusative\"]",   // Units    Compound Units  power2  long-other-accusative-masculine
1140             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"feminine\"][@case=\"accusative\"]",    // Units    Compound Units  power2  long-other-accusative-feminine
1141             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@case=\"accusative\"]",  // Units    Compound Units  power2  long-other-accusative-dgender
1142             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"masculine\"][@case=\"genitive\"]", // Units    Compound Units  power2  long-other-genitive-masculine
1143             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"feminine\"][@case=\"genitive\"]",  // Units    Compound Units  power2  long-other-genitive-feminine
1144             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@case=\"genitive\"]",    // Units    Compound Units  power2  long-other-genitive-dgender
1145             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"masculine\"][@case=\"dative\"]",   // Units    Compound Units  power2  long-other-dative-masculine
1146             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@gender=\"feminine\"][@case=\"dative\"]",    // Units    Compound Units  power2  long-other-dative-feminine
1147             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"][@case=\"dative\"]",  // Units    Compound Units  power2  long-other-dative-dgender
1148             "//ldml/units/unitLength[@type=\"short\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]", // Units    Compound Units  power2  short-one-nominative-dgender
1149             "//ldml/units/unitLength[@type=\"short\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",   // Units    Compound Units  power2  short-other-nominative-dgender
1150             "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"]",    // Units    Compound Units  power2  narrow-one-nominative-dgender
1151             "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"other\"]",  // Units    Compound Units  power2  narrow-other-nominative-dgender
1152 
1153             "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"nominative\"]",   // Miscellaneous    Minimal Pairs   Case    nominative
1154             "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"accusative\"]",   // Miscellaneous    Minimal Pairs   Case    accusative
1155             "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"genitive\"]", // Miscellaneous    Minimal Pairs   Case    genitive
1156             "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"dative\"]",   // Miscellaneous    Minimal Pairs   Case    dative
1157             "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"masculine\"]",    // Miscellaneous    Minimal Pairs   Gender  masculine
1158             "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"feminine\"]", // Miscellaneous    Minimal Pairs   Gender  feminine
1159             "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"neuter\"]",   // Miscellaneous    Minimal Pairs   Gender  neuter
1160 
1161             // we don't care about order here.
1162             "//ldml/units/unitLength[@type=\"long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",  // Special  Suppress    compound-UnitPattern1-power2    long
1163             "//ldml/units/unitLength[@type=\"narrow\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1",    // Special  Suppress    compound-UnitPattern1-power2    narrow
1164             "//ldml/units/unitLength[@type=\"short\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1", // Special  Suppress    compound-UnitPattern1-power2    short
1165             };
1166 
1167         int germanExpectedIndex = 0;
1168         int errorCount = 0;
1169         int item = 0;
1170         for (Entry<PathHeader, Collection<String>> entry : pathHeaderToPaths.asMap().entrySet()) {
1171             PathHeader ph = entry.getKey();
1172             Collection<String> epaths = entry.getValue();
1173             if (!assertEquals(entry.toString(), 1, epaths.size())) {
1174                 ++errorCount;
1175             }
1176             if (!assertEquals(++item + ") PathHeader order", germanExpected[germanExpectedIndex++], epaths.iterator().next())) {
1177                 ++errorCount;
1178             }
1179         }
1180         if (errorCount != 0) {
1181             for (Entry<PathHeader, Collection<String>> entry : pathHeaderToPaths.asMap().entrySet()) {
1182                 PathHeader ph = entry.getKey();
1183                 Collection<String> epaths = entry.getValue();
1184                 System.out.println("\"" + epaths.iterator().next().replace("\"", "\\\"") + "\",\t// " + ph);
1185             }
1186         }
1187     }
1188 
Test8414()1189     public void Test8414() {
1190         PathDescription pathDescription = new PathDescription(supplemental,
1191             english, null, null, PathDescription.ErrorHandling.CONTINUE);
1192 
1193         String prefix = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/dayPeriodContext[@type=\"";
1194         String suffix = "\"]/dayPeriodWidth[@type=\"wide\"]/dayPeriod[@type=\"morning1\"]";
1195 
1196         final String path0 = prefix + "format" + suffix;
1197         final String path1 = prefix + "stand-alone" + suffix;
1198         String v0 = english.getStringValue(path0);
1199         String v1 = english.getStringValue(path1);
1200         String p0 = pathDescription.getDescription(path0, v0, null, null);
1201         String p1 = pathDescription.getDescription(path1, v1, null, null);
1202         assertTrue("Check pd for format", p0.contains("in the morning"));
1203         assertTrue("Check pd for stand-alone", !p1.contains("in the morning"));
1204     }
1205 
TestCompletenessNonLdmlDtd()1206     public void TestCompletenessNonLdmlDtd() {
1207         PathChecker pathChecker = new PathChecker();
1208         Set<String> directories = new LinkedHashSet<>();
1209         Multimap<String, String> pathValuePairs = LinkedListMultimap.create();
1210         // get all the directories containing non-Ldml dtd files
1211         for (DtdType dtdType : DtdType.values()) {
1212             if (dtdType == DtdType.ldml || dtdType == DtdType.ldmlICU) {
1213                 continue;
1214             }
1215             DtdData dtdData = DtdData.getInstance(dtdType);
1216             for (String dir : dtdType.directories) {
1217                 if (DEBUG_DTD_TYPE != null && !DEBUG_DTD_TYPE.directories.contains(dir)) {
1218                     continue;
1219                 }
1220                 File dir2 = new File(COMMON_DIR + dir);
1221                 logln(dir2.getName());
1222                 for (String file : dir2.list()) {
1223                     // don't need to restrict with getFilesToTest(Arrays.asList(dir2.list()), "root", "en")) {
1224                     if (!file.endsWith(".xml")) {
1225                         continue;
1226                     }
1227                     if (DEBUG) warnln(" TestCompletenessNonLdmlDtd: " + dir + ", " + file);
1228                     logln(" \t" + file);
1229                     for (Pair<String, String> pathValue : XMLFileReader.loadPathValues(
1230                         dir2 + "/" + file, new ArrayList<Pair<String, String>>(), true)) {
1231                         final String path = pathValue.getFirst();
1232                         final String value = pathValue.getSecond();
1233 //                        logln("\t\t" + path);
1234                         if (path.startsWith("//supplementalData/unitPreferenceData/unitPreferences")
1235                             && path.contains("skeleton")) {
1236                             int debug = 0;
1237                         }
1238                         pathChecker.checkPathHeader(dtdData, path);
1239                     }
1240                 }
1241             }
1242         }
1243         if (!pathChecker.badHeaders.isEmpty()) {
1244             System.out.println("For help with DTD updates: " + CLDRURLS.CLDR_UPDATINGDTD_URL);
1245         }
1246     }
1247 
1248     private class PathChecker {
1249         PathHeader.Factory phf = pathHeaderFactory;
1250         PathStarrer starrer = new PathStarrer().setSubstitutionPattern("%A");
1251 
1252         Set<String> badHeaders = new TreeSet<>();
1253         Map<PathHeader, PathHeader> goodHeaders = new HashMap<>();
1254         Set<PathHeader> seenBad = new HashSet<>();
1255         {
phf.clearCache()1256             phf.clearCache();
1257         }
1258 
checkPathHeader(DtdData dtdData, String rawPath)1259         public void checkPathHeader(DtdData dtdData, String rawPath) {
1260             XPathParts pathPlain = XPathParts.getFrozenInstance(rawPath);
1261             if (dtdData.isMetadata(pathPlain)) {
1262                 return;
1263             }
1264             if (dtdData.isDeprecated(pathPlain)) {
1265                 return;
1266             }
1267             Multimap<String, String> extras = HashMultimap.create();
1268             Set<String> fixedPaths = dtdData.getRegularizedPaths(pathPlain, extras);
1269             if (fixedPaths != null) {
1270                 for (String fixedPath : fixedPaths) {
1271                     checkSubpath(fixedPath);
1272                 }
1273             }
1274             for (String path : extras.keySet()) {
1275                 checkSubpath(path);
1276             }
1277         }
1278 
checkSubpath(String path)1279         public void checkSubpath(String path) {
1280             String message = ": Can't compute path header";
1281             if (path.contentEquals("//supplementalData/grammaticalData/grammaticalFeatures[@targets=\"nominal\"][@locales=\"it\"]/grammaticalGender/_values") ) {
1282                 int debug = 0;
1283             }
1284             PathHeader ph = null;
1285             try {
1286                 ph = phf.fromPath(path);
1287                 if (seenBad.contains(ph)) {
1288                     return;
1289                 }
1290                 if (ph.getPageId() == PageId.Deprecated) {
1291                     return; // don't care
1292                 }
1293                 if (ph.getPageId() != PageId.Unknown) {
1294                     PathHeader old = goodHeaders.put(ph, ph);
1295                     if (old != null && !path.equals(old.getOriginalPath())) {
1296                         errln("Duplicate path header for: " + ph
1297                             + "\n\t\t " + path
1298                             + "\n\t\t≠" + old.getOriginalPath());
1299                         seenBad.add(ph);
1300                     }
1301                     return;
1302                 }
1303                 // for debugging
1304                 phf.clearCache();
1305                 List<String> failures = new ArrayList<>();
1306                 ph = phf.fromPath(path, failures);
1307                 message = ": Unknown path header" + failures;
1308             } catch (Exception e) {
1309                 message = ": Exception in path header: " + e.getMessage();
1310             }
1311             String star = starrer.set(path);
1312             if (badHeaders.add(star)) {
1313                 errln(star + message + ", " + ph);
1314                 System.out.println("\tNo match in PathHeader.txt for " + path
1315                     + "\n\tYou get only one message for all paths matching " + star
1316                     + "\n\tFor example, check to see if the field in PathHeader.txt is in PathHeader.PageId."
1317                     + "\n\tIf not, either correct PathHeader.txt or add it to PageId"
1318                     + "\n\tIf you have a value attribute, you will need extra _ characters. The value attribute will show at the end with prefixed _, eg [...]/_skeleton."
1319                     + "If there can be a value for the path then that element will add _. "
1320                     );
1321             }
1322         }
1323     }
1324 
TestSupplementalItems()1325     public void TestSupplementalItems() {
1326         //      <weekOfPreference ordering="weekOfYear weekOfMonth" locales="am az bs cs cy da el et hi ky lt mk sk ta th"/>
1327         // logln(pathHeaderFactory.getRegexInfo());
1328         CLDRFile supplementalFile = CLDRConfig.getInstance().getSupplementalFactory().make("supplementalData", false);
1329         List<String> failures = new ArrayList<>();
1330         Multimap<String, String> pathValuePairs = LinkedListMultimap.create();
1331         for (String test : With.in(supplementalFile.iterator("//supplementalData/weekData"))) {
1332             failures.clear();
1333             XPathParts parts = XPathParts.getFrozenInstance(supplementalFile.getFullXPath(test));
1334             supplementalFile.getDtdData().getRegularizedPaths(parts, pathValuePairs);
1335             for (Entry<String, Collection<String>> entry : pathValuePairs.asMap().entrySet()) {
1336                 final String normalizedPath = entry.getKey();
1337                 final Collection<String> normalizedValue = entry.getValue();
1338                 PathHeader ph = pathHeaderFactory.fromPath(normalizedPath, failures);
1339                 if (ph == null || ph.getSectionId() == SectionId.Special) {
1340                     errln("Failure with " + test + " => " + normalizedPath + " = " + normalizedValue);
1341                 } else {
1342                     logln(ph + "\t" + test + " = " + normalizedValue);
1343                 }
1344             }
1345         }
1346     }
1347 
test10232()1348     public void test10232() {
1349         String[][] tests = {
1350             { "MMM", "Formats - Flexible - Date Formats" },
1351             { "dMM", "Formats - Flexible - Date Formats" },
1352             { "h", "Formats - Flexible - 12 Hour Time Formats" },
1353             { "hm", "Formats - Flexible - 12 Hour Time Formats" },
1354             { "Ehm", "Formats - Flexible - 12 Hour Time Formats" },
1355             { "H", "Formats - Flexible - 24 Hour Time Formats" },
1356             { "Hm", "Formats - Flexible - 24 Hour Time Formats" },
1357             { "EHm", "Formats - Flexible - 24 Hour Time Formats" },
1358         };
1359         for (String[] test : tests) {
1360             String path = "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\""
1361                 + test[0] + "\"]";
1362             PathHeader pathHeader = pathHeaderFactory.fromPath(path);
1363             assertEquals("flexible formats", test[1] + "|" + test[0], pathHeader.getHeader() + "|" + pathHeader.getCode());
1364         }
1365     }
1366 
1367     // Moved from TestAnnotations and generalized
testPathHeaderSize()1368     public void testPathHeaderSize() {
1369         String locale = "ar"; // choose one with lots of plurals
1370         int maxSize = 750;
1371         boolean showTable = false; // only printed if test fails or verbose
1372 
1373         Factory factory = CLDRConfig.getInstance().getCommonAndSeedAndMainAndAnnotationsFactory();
1374         CLDRFile english = factory.make(locale, true);
1375 
1376         PathHeader.Factory phf = PathHeader.getFactory(CLDRConfig.getInstance().getEnglish());
1377         Counter<PageId> counterPageId = new Counter<>();
1378         Counter<PageId> counterPageIdAll = new Counter<>();
1379         for (String path : english) {
1380             Level level = CLDRConfig.getInstance().getSupplementalDataInfo().getCoverageLevel(path, locale);
1381             PathHeader ph = phf.fromPath(path);
1382             if (level.compareTo(Level.MODERN) <= 0) {
1383                 counterPageId.add(ph.getPageId(), 1);
1384             }
1385             counterPageIdAll.add(ph.getPageId(), 1);
1386         }
1387         Set<R2<Long, PageId>> entrySetSortedByCount = counterPageId.getEntrySetSortedByCount(false, null);
1388         for (R2<Long, PageId> sizeAndPageId : entrySetSortedByCount) {
1389             long size = sizeAndPageId.get0();
1390             PageId pageId = sizeAndPageId.get1();
1391             if (!assertTrue(pageId.getSectionId() + "/" + pageId + " size (" + size
1392                 + ") < " + maxSize + "?", size < maxSize)) {
1393                 showTable = true;
1394             }
1395             // System.out.println(pageId + "\t" + size);
1396         }
1397         if (showTable || isVerbose()) {
1398             for (R2<Long, PageId> sizeAndPageId : entrySetSortedByCount) {
1399                 PageId pageId = sizeAndPageId.get1();
1400                 System.out.println(pageId.getSectionId() + "\t" + pageId + "\t" + sizeAndPageId.get0() + "\t" + counterPageIdAll.get(pageId));
1401             }
1402         }
1403     }
TestCLDR_11454()1404     public void TestCLDR_11454() {
1405         PathHeader.Factory phf = PathHeader.getFactory();
1406         PathHeader century = phf.fromPath("//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"duration-century\"]/displayName");
1407         PathHeader decade =  phf.fromPath("//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"duration-decade\"]/displayName");
1408         assertEquals("Section", century.getSectionId(), decade.getSectionId());
1409         assertEquals("Page", century.getPageId(), decade.getPageId());
1410     }
1411 
TestEmojiOrder()1412     public void TestEmojiOrder() {
1413         PathHeader.Factory phf = PathHeader.getFactory();
1414         String[] desiredOrder = {
1415             "��‍⚕", "��‍⚕", "��‍⚕",
1416             "��‍⚖", "��‍⚖", "��‍⚖"};
1417         List<PathHeader> pathHeaders = new ArrayList<>();
1418         for (String emoji : desiredOrder) {
1419             String base = "//ldml/annotations/annotation[@cp=\"" + emoji + "\"]";
1420             pathHeaders.add(phf.fromPath(base + "[@type=\"tts\"]"));
1421             pathHeaders.add(phf.fromPath(base));
1422             logln(emoji
1423                 + ": getEmojiMinorOrder="+ Emoji.getEmojiMinorOrder(Emoji.getMinorCategory(emoji))
1424                 + ", getEmojiToOrder="+ Emoji.getEmojiToOrder(emoji)
1425                 );
1426         }
1427         PathHeader lastItem = null;
1428         for (PathHeader item : pathHeaders) {
1429             if (lastItem != null) {
1430                 assertEquals("Section", lastItem.getSectionId(), item.getSectionId());
1431                 assertEquals("Page", lastItem.getPageId(), item.getPageId());
1432                 assertEquals("Header", lastItem.getHeader(), item.getHeader());
1433                 if (!assertTrue(lastItem + " < " + item, lastItem.compareTo(item) < 0)) {
1434                     lastItem.compareTo(item); // for debugging
1435                 }
1436             }
1437             lastItem = item;
1438         }
1439     }
1440 
TestQuotes()1441     public void TestQuotes() {
1442         // quotes should never appear in result
1443         PathHeader.Factory phf = PathHeader.getFactory();
1444         String[] tests = {
1445             "//supplementalData/plurals[@type=\"ordinal\"]/pluralRules[@locales=\"ig\"]/pluralRule[@count=\"other\"]",
1446             "//supplementalData/transforms/transform[@source=\"und-Khmr\"][@target=\"und-Latn\"]"
1447         };
1448         for (String test : tests) {
1449             PathHeader trial = phf.fromPath(test);
1450             assertEquals("No quotes in pathheader", false, trial.toString().contains("\""));
1451         }
1452     }
1453     /**
1454      * Make sure that the PathHeader sort order is consistent with the grammatical feature orders
1455      * "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"volume-liter\"]/displayName"
1456      * //ldml/units/unitLength[@type=\long\"]/unit[@type=\"volume-liter\"]/unitPattern[@count=\"one\"][@case=\"genitive\"]",
1457      * //ldml/units/unitLength[@type=\long\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"one\"][@gender=\"feminine\"][@case=\"accusative\"]",
1458      */
TestUnitOrder()1459     public void TestUnitOrder() {
1460         PathHeader.Factory phf = PathHeader.getFactory();
1461         List<PathHeader> expectedOrder = new ArrayList<>();
1462         List<Width> widths = Arrays.asList(Width.LONG, Width.SHORT, Width.NARROW);
1463         List<CaseValues> cases = Arrays.asList(GrammarInfo.CaseValues.values()).subList(0, 3);
1464         List<GenderValues> genders = Arrays.asList(GrammarInfo.GenderValues.values()).subList(0, 3);
1465 
1466         for (Width width : widths) {
1467             String path = "//ldml/units/unitLength[@type=\"" + width
1468                 + "\"]/unit[@type=\"length-meter\"]/displayName";
1469             expectedOrder.add(phf.fromPath(path));
1470         }
1471 
1472         for (Width width : widths) {
1473             for (Count count : Count.values()) {
1474                 for (GrammarInfo.CaseValues gCase : cases) {
1475                     if (width != Width.LONG && gCase != CaseValues.nominative) {
1476                         break;
1477                     }
1478                     String path = "//ldml/units/unitLength[@type=\"" + width
1479                         + "\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"" + count
1480                         + (gCase == CaseValues.nominative ? "" : "\"][@case=\"" + gCase)
1481                         + "\"]";
1482                     expectedOrder.add(phf.fromPath(path));
1483                 }
1484             }
1485         }
1486         for (Width width : widths) {
1487             for (Count count : Count.values()) {
1488                 for (GrammarInfo.CaseValues gCase : cases) {
1489                     if (width != Width.LONG && gCase != CaseValues.nominative) {
1490                         break;
1491                     }
1492                     for (GrammarInfo.GenderValues gGender : genders) {
1493                         if (width != Width.LONG && gGender != GenderValues.neuter) {
1494                             break;
1495                         }
1496                         String path = "//ldml/units/unitLength[@type=\"" + width
1497                             + "\"]/compoundUnit[@type=\"power2\"]/compoundUnitPattern1[@count=\"" + count
1498                             + (gGender == GenderValues.neuter ? "" : "\"][@gender=\"" + gGender)
1499                             + (gCase == CaseValues.nominative ? "" : "\"][@case=\"" + gCase)
1500                             + "\"]";
1501                         expectedOrder.add(phf.fromPath(path));
1502                     }
1503                 }
1504             }
1505         }
1506         for (Count count : Count.values()) {
1507             String path = "//ldml/numbers/minimalPairs/ordinalMinimalPairs[@ordinal=\"" + count
1508                 + "\"]";
1509             expectedOrder.add(phf.fromPath(path));
1510         }
1511         for (Count count : Count.values()) {
1512             String path = "//ldml/numbers/minimalPairs/pluralMinimalPairs[@count=\"" + count
1513                 + "\"]";
1514             expectedOrder.add(phf.fromPath(path));
1515         }
1516         for (GrammarInfo.CaseValues gCase : cases) {
1517             String path = "//ldml/numbers/minimalPairs/caseMinimalPairs[@case=\"" + gCase
1518                 + "\"]";
1519             expectedOrder.add(phf.fromPath(path));
1520         }
1521         for (GrammarInfo.GenderValues gGender : genders) {
1522             String path = "//ldml/numbers/minimalPairs/genderMinimalPairs[@gender=\"" + gGender
1523                 + "\"]";
1524             expectedOrder.add(phf.fromPath(path));
1525         }
1526 
1527         PathHeader last = null;
1528         int item = 0;
1529         int errorCount = 0;
1530         for (PathHeader pathHeader : expectedOrder) {
1531             if (last != null) {
1532                 if (!assertTrue(++item + ")\t" + last + "\t<\t" + pathHeader, last.compareTo(pathHeader) < 0)) {
1533                     errorCount++;
1534                     last.compareTo(pathHeader);
1535                 }
1536             }
1537             last = pathHeader;
1538 
1539         }
1540         if (errorCount != 0 || isVerbose()) {
1541             for (PathHeader pathHeader : expectedOrder) {
1542                 System.out.println("\"" + pathHeader.getOriginalPath().replace("\"", "\\\"") + "\",\t// " + pathHeader);
1543             }
1544         }
1545     }
1546 }
1547