• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.test;
2 
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.EnumSet;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.TreeMap;
19 import java.util.TreeSet;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.regex.Matcher;
22 
23 import org.unicode.cldr.draft.FileUtilities;
24 import org.unicode.cldr.test.CheckCLDR.CheckStatus;
25 import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype;
26 import org.unicode.cldr.test.CheckCLDR.CompoundCheckCLDR;
27 import org.unicode.cldr.test.CheckCLDR.FormatDemo;
28 import org.unicode.cldr.test.CheckCLDR.Options;
29 import org.unicode.cldr.test.CheckCLDR.Phase;
30 import org.unicode.cldr.test.CheckCLDR.SimpleDemo;
31 import org.unicode.cldr.tool.Option;
32 import org.unicode.cldr.tool.Option.Params;
33 import org.unicode.cldr.tool.ShowData;
34 import org.unicode.cldr.tool.TablePrinter;
35 import org.unicode.cldr.util.CLDRConfig;
36 import org.unicode.cldr.util.CLDRConfig.Environment;
37 import org.unicode.cldr.util.CLDRFile;
38 import org.unicode.cldr.util.CLDRFile.Status;
39 import org.unicode.cldr.util.CLDRPaths;
40 import org.unicode.cldr.util.CLDRTool;
41 import org.unicode.cldr.util.CldrUtility;
42 import org.unicode.cldr.util.Counter;
43 import org.unicode.cldr.util.CoverageInfo;
44 import org.unicode.cldr.util.Factory;
45 import org.unicode.cldr.util.LanguageTagParser;
46 import org.unicode.cldr.util.Level;
47 import org.unicode.cldr.util.LocaleIDParser;
48 import org.unicode.cldr.util.LogicalGrouping;
49 import org.unicode.cldr.util.Organization;
50 import org.unicode.cldr.util.Pair;
51 import org.unicode.cldr.util.PathDescription;
52 import org.unicode.cldr.util.PathHeader;
53 import org.unicode.cldr.util.PathHeader.SectionId;
54 import org.unicode.cldr.util.PathUtilities;
55 import org.unicode.cldr.util.PatternCache;
56 import org.unicode.cldr.util.SimpleFactory;
57 import org.unicode.cldr.util.StandardCodes;
58 import org.unicode.cldr.util.StringId;
59 import org.unicode.cldr.util.SupplementalDataInfo;
60 import org.unicode.cldr.util.UnicodeSetPrettyPrinter;
61 import org.unicode.cldr.util.VoteResolver;
62 import org.unicode.cldr.util.VoteResolver.CandidateInfo;
63 import org.unicode.cldr.util.VoteResolver.UnknownVoterException;
64 import org.unicode.cldr.util.VoterInfoList;
65 import org.unicode.cldr.util.XMLSource;
66 import org.unicode.cldr.util.XMLSource.SourceLocation;
67 import org.unicode.cldr.util.XPathParts;
68 
69 import com.ibm.icu.dev.tool.UOption;
70 import com.ibm.icu.dev.util.ElapsedTimer;
71 import com.ibm.icu.impl.Relation;
72 import com.ibm.icu.impl.Row;
73 import com.ibm.icu.lang.UCharacter;
74 import com.ibm.icu.text.Collator;
75 import com.ibm.icu.text.UnicodeSet;
76 import com.ibm.icu.util.ULocale;
77 
78 /**
79  * Console test for CheckCLDR. <br>
80  * Some common source directories:
81  *
82  * <pre>
83  *  -s C:/cvsdata/unicode/cldr/incoming/vetted/main
84  *  -s C:/cvsdata/unicode/cldr/incoming/proposed/main
85  *  -s C:/cvsdata/unicode/cldr/incoming/proposed/main
86  *  -s C:/cvsdata/unicode/cldr/testdata/main
87  * </pre>
88  *
89  * @author markdavis
90  *
91  */
92 @CLDRTool(alias = "check",
93 description = "Run CheckCLDR against CLDR data")
94 public class ConsoleCheckCLDR {
95     private static final CLDRConfig CLDR_CONFIG = CLDRConfig.getInstance();
96     public static boolean showStackTrace = false;
97     public static boolean errorsOnly = false;
98     static boolean SHOW_LOCALE = true;
99     static boolean SHOW_EXAMPLES = false;
100     private static boolean CLDR_GITHUB_ANNOTATIONS = (Boolean.parseBoolean(System.getProperty("CLDR_GITHUB_ANNOTATIONS", "false")));
101 
102     // TODO get ride of these in favor of MyOptions
103 
104     private static final int
105     COVERAGE = 2,
106     EXAMPLES = 3,
107     FILE_FILTER = 4,
108     TEST_FILTER = 5,
109     DATE_FORMATS = 6,
110     ORGANIZATION = 7,
111     SHOWALL = 8,
112     PATH_FILTER = 9,
113     ERRORS_ONLY = 10,
114     CHECK_ON_SUBMIT = 11,
115     NO_ALIASES = 12,
116     SOURCE_DIRECTORY = 13,
117     USER = 14,
118     PHASE = 15,
119     GENERATE_HTML = 16,
120     VOTE_RESOLVE = 17,
121     ID_VIEW = 18,
122     SUBTYPE_FILTER = 19,
123     BAILEY = 21
124     ;
125 
126     static final String SOURCE_DIRS = CLDRPaths.MAIN_DIRECTORY + "," + CLDRPaths.ANNOTATIONS_DIRECTORY + "," + CLDRPaths.SEED_DIRECTORY;
127 
128     enum MyOptions {
129         coverage(new Params().setHelp("Set the coverage: eg -c comprehensive")
130             .setMatch("comprehensive|modern|moderate|basic")), // UOption.REQUIRES_ARG
131         examples(new Params().setHelp("Turn on examples (actually a summary of the demo)")
132             .setFlag('x')), //, 'x', UOption.NO_ARG),
133         file_filter(new Params().setHelp("Pick the locales (files) to check: arg is a regular expression, eg -f fr, or -f fr.*, or -f (fr|en-.*)")
134             .setDefault(".*").setMatch(".*")), //, 'f', UOption.REQUIRES_ARG).setDefault(".*"),
135         test_filter(new Params()
136             .setHelp("Filter the Checks: arg is a regular expression, eg -t.*number.*. To check all BUT a given test, use the style -t ((?!.*CheckZones).*)")
137             .setDefault(".*").setMatch(".*")), //, 't', UOption.REQUIRES_ARG).setDefault(".*"),
138         date_formats(new Params().setHelp("Turn on special date format checks")), //, 'd', UOption.NO_ARG),
139         organization(new Params().setHelp("Organization: ibm, google, ....; Uses Locales.txt for to filter locales and set coverage levels")
140             .setDefault(".*").setMatch(".*")), //, 'o', UOption.REQUIRES_ARG),
141         showall(new Params().setHelp("Show all paths, including aliased").setFlag('a')), //, 'a', UOption.NO_ARG),
142         path_filter(new Params().setHelp("Pick the paths to check, eg -p.*languages.*")
143             .setDefault(".*").setMatch(".*")), //, 'p', UOption.REQUIRES_ARG).setDefault(".*"),
144         errors_only(new Params().setHelp("Show errors only (with -ef, only final processing errors)")), //, 'e', UOption.NO_ARG),
145         check_on_submit(new Params().setHelp("")
146             .setFlag('k')), //, 'k', UOption.NO_ARG),
147         noaliases(new Params().setHelp("No aliases")), //, 'n', UOption.NO_ARG),
148         source_directory(new Params().setHelp("Fully qualified source directories. (Conflicts with -S.)")
149             .setDefault(SOURCE_DIRS).setMatch(".*")), //, 's', UOption.REQUIRES_ARG).setDefault(SOURCE_DIRS),
150         user(new Params().setHelp("User, eg -uu148")
151             .setMatch(".*")), //, 'u', UOption.REQUIRES_ARG),
152         phase(new Params().setHelp("?")
153             .setMatch(Phase.class).setFlag('z')), //, 'z', UOption.REQUIRES_ARG),
154         generate_html(new Params().setHelp("Generate HTML-style chart in directory.")
155             .setDefault(CLDRPaths.CHART_DIRECTORY + "/errors/").setMatch(".*")), //, 'g', UOption.OPTIONAL_ARG).setDefault(CLDRPaths.CHART_DIRECTORY + "/errors/"),
156         vote_resolution(new Params().setHelp("")), //, 'v', UOption.NO_ARG),
157         id_view(new Params().setHelp("")), //, 'i', UOption.NO_ARG),
158         subtype_filter(new Params().setHelp("error/warning subtype filter, eg unexpectedOrderOfEraYear")
159             .setDefault(".*").setMatch(".*").setFlag('y')), //, 'y', UOption.REQUIRES_ARG),
160         source_all(new Params().setHelp(
161             "Partially qualified directories. Standard subdirectories added if not specified (/main, /annotations, /subdivisions). (Conflicts with -s.)")
162             .setMatch(".*").setFlag('S').setDefault("common,seed,exemplars")), //, 'S', <changed>),
163         bailey(new Params().setHelp("check bailey values (" + CldrUtility.INHERITANCE_MARKER + ")")), //, 'b', UOption.NO_ARG)
164         exemplarError(new Params().setFlag('E').setHelp("include to force strict Exemplar check")),
165         missingPaths(new Params().setHelp("include to show missing and provisional paths, at the specified level"));
166 
167         // BOILERPLATE TO COPY
168         final Option option;
169 
MyOptions(Params params)170         private MyOptions(Params params) {
171             option = new Option(this, params);
172         }
173 
174         private static Option.Options myOptions = new Option.Options();
175         static {
176             for (MyOptions option : MyOptions.values()) {
myOptions.add(option, option.option)177                 myOptions.add(option, option.option);
178             }
179         }
180 
parse(String[] args, boolean showArguments)181         private static Set<String> parse(String[] args, boolean showArguments) {
182             return myOptions.parse(MyOptions.values()[0], args, true);
183         }
184     }
185 
186     // TODO get ride of these in favor of MyOptions
187 
188     private static final UOption[] options = {
189         UOption.HELP_H(),
190         UOption.HELP_QUESTION_MARK(),
191         UOption.create("coverage", 'c', UOption.REQUIRES_ARG),
192         UOption.create("examples", 'x', UOption.NO_ARG),
193         UOption.create("file_filter", 'f', UOption.REQUIRES_ARG).setDefault(".*"),
194         UOption.create("test_filter", 't', UOption.REQUIRES_ARG).setDefault(".*"),
195         UOption.create("date_formats", 'd', UOption.NO_ARG),
196         UOption.create("organization", 'o', UOption.REQUIRES_ARG),
197         UOption.create("showall", 'a', UOption.NO_ARG),
198         UOption.create("path_filter", 'p', UOption.REQUIRES_ARG).setDefault(".*"),
199         UOption.create("errors_only", 'e', UOption.NO_ARG),
200         UOption.create("check-on-submit", 'k', UOption.NO_ARG),
201         UOption.create("noaliases", 'n', UOption.NO_ARG),
202         UOption.create("source_directory", 's', UOption.REQUIRES_ARG).setDefault(SOURCE_DIRS),
203         UOption.create("user", 'u', UOption.REQUIRES_ARG),
204         UOption.create("phase", 'z', UOption.REQUIRES_ARG),
205         UOption.create("generate_html", 'g', UOption.OPTIONAL_ARG).setDefault(CLDRPaths.CHART_DIRECTORY + "/errors/"),
206         UOption.create("vote resolution", 'v', UOption.NO_ARG),
207         UOption.create("id view", 'i', UOption.NO_ARG),
208         UOption.create("subtype_filter", 'y', UOption.REQUIRES_ARG),
209         UOption.create("source_all", 'S', UOption.OPTIONAL_ARG).setDefault("common,seed,exemplars"),
210         UOption.create("bailey", 'b', UOption.NO_ARG),
211         UOption.create("exemplarError", 'E', UOption.NO_ARG),
212         UOption.create("missingPaths", 'm', UOption.NO_ARG)
213     };
214 
215     private static final Comparator<String> baseFirstCollator = new Comparator<String>() {
216         LanguageTagParser languageTagParser1 = new LanguageTagParser();
217         LanguageTagParser languageTagParser2 = new LanguageTagParser();
218 
219         @Override
220         public int compare(String o1, String o2) {
221             String ls1 = languageTagParser1.set(o1).getLanguageScript();
222             String ls2 = languageTagParser2.set(o2).getLanguageScript();
223             int result = ls1.compareTo(ls2);
224             if (result != 0) return result;
225             return o1.compareTo(o2);
226         }
227     };
228     private static final boolean PATH_IN_COUNT = false;
229 
230     static Counter<ErrorType> subtotalCount = new Counter<>(true); // new ErrorCount();
231     static Counter<ErrorType> totalCount = new Counter<>(true);
232 
233     private enum RawStatus {missing, provisional, present}
234     /**
235      * This will be the test framework way of using these tests.
236      *
237      * @param args
238      * @throws IOException
239      */
main(String[] args)240     public static void main(String[] args) throws IOException {
241         MyOptions.parse(args, true);
242         ElapsedTimer totalTimer = new ElapsedTimer();
243         UOption.parseArgs(args, options);
244         String factoryFilter = options[FILE_FILTER].value;
245         if (factoryFilter.equals("key")) {
246             factoryFilter = "(en|ru|nl|fr|de|it|pl|es|tr|th|ja|zh|ko|ar|bg|sr|uk|ca|hr|cs|da|fil|fi|hu|id|lv|lt|no|pt|ro|sk|sl|sv|vi|el|he|fa|hi|am|af|et|is|ms|sw|zu|bn|mr|ta|eu|gl|ur|gu|kn|ml|te|zh_Hant|pt_PT|en_GB)";
247         }
248         String checkFilter = options[TEST_FILTER].value;
249         String subtypeFilterString = options[SUBTYPE_FILTER].value;
250         EnumSet<Subtype> subtypeFilter = null;
251         if (subtypeFilterString != null) {
252             subtypeFilter = EnumSet.noneOf(Subtype.class);
253             Matcher m = PatternCache.get(subtypeFilterString).matcher("");
254             for (Subtype value : Subtype.values()) {
255                 if (m.reset(value.toString()).find() || m.reset(value.name()).find()) {
256                     subtypeFilter.add(value);
257                 }
258             }
259             if (subtypeFilter.size() == 0) {
260                 System.err.println("No subtype match for " + subtypeFilterString);
261                 return;
262             }
263         }
264 
265         errorsOnly = options[ERRORS_ONLY].doesOccur;
266         boolean showMissing = MyOptions.missingPaths.option.doesOccur();
267 
268         SHOW_EXAMPLES = options[EXAMPLES].doesOccur;
269         boolean showAll = options[SHOWALL].doesOccur;
270         boolean checkFlexibleDates = options[DATE_FORMATS].doesOccur;
271         String pathFilterString = options[PATH_FILTER].value;
272         Matcher pathFilter = null;
273         if (!pathFilterString.equals(".*")) {
274             pathFilter = PatternCache.get(pathFilterString).matcher("");
275         }
276         boolean checkOnSubmit = options[CHECK_ON_SUBMIT].doesOccur;
277         boolean noaliases = options[NO_ALIASES].doesOccur;
278 
279         Level coverageLevel = null;
280         String coverageLevelInput = options[COVERAGE].value;
281         if (coverageLevelInput != null) {
282             coverageLevel = Level.get(coverageLevelInput);
283             if (coverageLevel == Level.UNDETERMINED) {
284                 throw new IllegalArgumentException("-c" + coverageLevelInput + "\t is invalid: must be one of: "
285                     + "basic,moderate,...");
286             }
287         }
288 
289         Organization organization = options[ORGANIZATION].value == null ? null : Organization.fromString(options[ORGANIZATION].value);
290         if (organization != null) {
291             Set<Organization> organizations = StandardCodes.make().getLocaleCoverageOrganizations();
292             if (!organizations.contains(organization)) {
293                 throw new IllegalArgumentException("-o" + organization + "\t is invalid: must be one of: "
294                     + organizations);
295             }
296         }
297         final CLDRConfig cldrConf = CLDR_CONFIG;
298         cldrConf.setEnvironment(Environment.UNITTEST);
299         final Phase phase;
300         if (options[PHASE].doesOccur) {
301             String phaseVal = options[PHASE].value;
302             try {
303                 // no null check for argument; if it is is null, Phase.forString would return the one from CLDRConfig
304                 phase = Phase.forString(phaseVal);
305             } catch (IllegalArgumentException e) {
306                 StringBuilder sb = new StringBuilder("Incorrect Phase value");
307                 if (phaseVal != null && !phaseVal.isEmpty()) {
308                     sb.append(" '");
309                     sb.append(phaseVal);
310                     sb.append("'");
311                 }
312                 sb.append(": should be one of ");
313                 for (Phase curPhase : Phase.values()) {
314                     // implicitly does a toString;
315                     sb.append(curPhase);
316                     sb.append(", ");
317                 }
318                 int lastIdx = sb.lastIndexOf(",");
319                 // remove the last comma, if it occurs
320                 if (lastIdx > -1) {
321                     String tmpBuf = sb.substring(0, lastIdx);
322                     sb.setLength(0);
323                     sb.append(tmpBuf);
324                 }
325                 sb.append(".");
326                 // TODO: Reporting should be similar to an error (wrong parameter...), and not actually an Exception
327                 throw new IllegalArgumentException(sb.toString(), e);
328             }
329         } else {
330             phase = cldrConf.getPhase();
331         }
332 
333         boolean baileyTest = options[BAILEY].doesOccur;
334 
335         File sourceDirectories[] = null;
336 
337         if (MyOptions.source_all.option.doesOccur()) {
338             if (MyOptions.source_directory.option.doesOccur()) {
339                 throw new IllegalArgumentException("Don't use -s and -S together.");
340             }
341             sourceDirectories = cldrConf.addStandardSubdirectories(cldrConf.getCLDRDataDirectories(MyOptions.source_all.option.getValue()));
342         } else {
343             String[] sdirs = options[SOURCE_DIRECTORY].value.split(",\\s*");
344             sourceDirectories = new File[sdirs.length];
345             for (int i = 0; i < sdirs.length; ++i) {
346                 sourceDirectories[i] = new File(CldrUtility.checkValidDirectory(sdirs[i],
347                     "Fix with -s. Use -h for help."));
348             }
349         }
350 
351         if (options[GENERATE_HTML].doesOccur) {
352             coverageLevel = Level.MODERN; // reset
353             ErrorFile.generated_html_directory = options[GENERATE_HTML].value;
354             ErrorFile.generated_html_count = FileUtilities.openUTF8Writer(ErrorFile.generated_html_directory, "count.txt");
355         }
356 
357         idView = options[ID_VIEW].doesOccur;
358 
359         if (options[VOTE_RESOLVE].doesOccur) {
360             resolveVotesDirectory = CldrUtility.checkValidFile(CLDRPaths.BASE_DIRECTORY + "incoming/vetted/votes/",
361                 true, null);
362             voterInfoList.setVoterToInfo(CldrUtility.checkValidFile(CLDRPaths.BASE_DIRECTORY
363                 + "incoming/vetted/usersa/usersa.xml", false, null));
364             voteResolver = new VoteResolver<>(voterInfoList);
365         }
366 
367         String user = options[USER].value;
368 
369         System.out.println("Source directories:\n");
370         for (File f : sourceDirectories) {
371             System.out.println("    " + f.getPath() + "\t("
372                 + PathUtilities.getNormalizedPathString(f) + ")");
373         }
374 
375         // set up the test
376         Factory cldrFactory = SimpleFactory.make(sourceDirectories, factoryFilter)
377             .setSupplementalDirectory(new File(CLDRPaths.SUPPLEMENTAL_DIRECTORY));
378         CompoundCheckCLDR checkCldr = CheckCLDR.getCheckAll(cldrFactory, checkFilter);
379         if (checkCldr.getFilteredTestList().size() == 0) {
380             throw new IllegalArgumentException("The filter doesn't match any tests.");
381         }
382         System.out.println("filtered tests: " + checkCldr.getFilteredTests());
383         Factory backCldrFactory = CLDRConfig.getInstance().getMainAndAnnotationsFactory();
384         english = backCldrFactory.make("en", true);
385 
386         CheckCLDR.setDisplayInformation(english);
387         checkCldr.setEnglishFile(english);
388         setExampleGenerator(new ExampleGenerator(english, english, CLDRPaths.SUPPLEMENTAL_DIRECTORY));
389         PathShower pathShower = new PathShower();
390 
391         // call on the files
392         Set<String> locales = new TreeSet<>(baseFirstCollator);
393         locales.addAll(cldrFactory.getAvailable());
394 
395         List<CheckStatus> result = new ArrayList<>();
396         Set<PathHeader> paths = new TreeSet<>(); // CLDRFile.ldmlComparator);
397         Map<String, String> m = new TreeMap<>();
398         Map<String, String> options = new HashMap<>();
399         FlexibleDateFromCLDR fset = new FlexibleDateFromCLDR();
400         Set<String> englishPaths = null;
401 
402         Set<String> fatalErrors = new TreeSet<>();
403 
404         showHeaderLine();
405 
406         supplementalDataInfo = SupplementalDataInfo.getInstance(CLDRPaths.SUPPLEMENTAL_DIRECTORY);
407 
408         LocaleIDParser localeIDParser = new LocaleIDParser();
409         String lastBaseLanguage = "";
410         PathHeader.Factory pathHeaderFactory = PathHeader.getFactory(english);
411 
412         final Map<String, Level> locale_status = StandardCodes.make().getLocaleToLevel(organization);
413 
414         final List<String> specialPurposeLocales = new ArrayList<>(Arrays.asList("en_US_POSIX", "en_ZZ"));
415         for (String localeID : locales) {
416             if (CLDRFile.isSupplementalName(localeID)) continue;
417             if (supplementalDataInfo.getDefaultContentLocales().contains(localeID)) {
418                 System.out.println("# Skipping default content locale: " + localeID);
419                 continue;
420             }
421 
422             // We don't really need to check the POSIX locale, as it is a special purpose locale
423             if (specialPurposeLocales.contains(localeID)) {
424                 System.out.println("# Skipping special purpose locale: " + localeID);
425                 continue;
426             }
427 
428             boolean isLanguageLocale = localeID.equals(localeIDParser.set(localeID).getLanguageScript());
429             options.clear();
430 
431             if (MyOptions.exemplarError.option.doesOccur()) {
432                 options.put(Options.Option.exemplarErrors.toString(), "true");
433             }
434 
435             // if the organization is set, skip any locale that doesn't have a value in Locales.txt
436             Level level = coverageLevel;
437             if (level == null) {
438                 level = Level.MODERN;
439             }
440             if (organization != null) {
441                 if (locale_status == null) continue;
442                 level = locale_status.get(localeID);
443                 if (level == null) continue;
444                 if (level.compareTo(Level.BASIC) < 0) continue;
445             } else if (!isLanguageLocale) {
446                 // otherwise, skip all language locales
447                 options.put(Options.Option.CheckCoverage_skip.getKey(), "true");
448             }
449 
450             //if (organization != null) options.put(Options.Option.CoverageLevel_localeType.getKey(), organization.toString());
451             options.put(Options.Option.phase.getKey(), phase.toString());
452 
453             if (SHOW_LOCALE) System.out.println();
454 
455             CLDRFile file;
456             CLDRFile englishFile = english;
457             CLDRFile parent = null;
458 
459             ElapsedTimer timer = new ElapsedTimer();
460             try {
461                 file = cldrFactory.make(localeID, true);
462                 if (ErrorFile.voteFactory != null) {
463                     ErrorFile.voteFile = ErrorFile.voteFactory.make(localeID, true);
464                 }
465                 final String parentID = LocaleIDParser.getParent(localeID);
466                 if (parentID != null) {
467                     parent = cldrFactory.make(parentID, true);
468                 }
469             } catch (RuntimeException e) {
470                 fatalErrors.add(localeID);
471                 System.out.println("FATAL ERROR: " + localeID);
472                 e.printStackTrace(System.out);
473                 continue;
474             }
475 
476             // generate HTML if asked for
477             if (ErrorFile.generated_html_directory != null) {
478                 String baseLanguage = localeIDParser.set(localeID).getLanguageScript();
479 
480                 if (!baseLanguage.equals(lastBaseLanguage)) {
481                     lastBaseLanguage = baseLanguage;
482                     ErrorFile.openErrorFile(localeID, baseLanguage);
483                 }
484             }
485 
486             if (user != null) {
487                 file = new CLDRFile.TestUser(file, user, isLanguageLocale);
488                 if (parent != null) {
489                     parent = new CLDRFile.TestUser(parent, user, isLanguageLocale);
490                 }
491             }
492             checkCldr.setCldrFileToCheck(file, new Options(options), result);
493 
494             subtotalCount.clear();
495 
496             for (Iterator<CheckStatus> it3 = result.iterator(); it3.hasNext();) {
497                 CheckStatus status = it3.next();
498                 String statusString = status.toString(); // com.ibm.icu.impl.Utility.escape(
499                 CheckStatus.Type statusType = status.getType();
500 
501                 if (errorsOnly) {
502                     if (!statusType.equals(CheckStatus.errorType)) continue;
503                 }
504 
505                 if (subtypeFilter != null) {
506                     if (!subtypeFilter.contains(status.getSubtype())) {
507                         continue;
508                     }
509                 }
510 
511                 if (checkOnSubmit) {
512                     if (!status.isCheckOnSubmit() || !statusType.equals(CheckStatus.errorType)) continue;
513                 }
514                 showValue(file, null, localeID, null, null, null, null, statusString, status.getSubtype());
515             }
516             paths.clear();
517 
518             CoverageInfo covInfo = cldrConf.getCoverageInfo();
519             for (String path : file.fullIterable()) {
520                 if (pathFilter != null && !pathFilter.reset(path).find()) {
521                     continue;
522                 }
523                 if (level != null) {
524                     Level currentLevel = covInfo.getCoverageLevel(path, localeID);
525                     if (currentLevel.compareTo(level) > 0) {
526                         continue;
527                     }
528                 }
529                 final PathHeader pathHeader = pathHeaderFactory.fromPath(path);
530                 if (pathHeader.getSectionId() != SectionId.Special) {
531                     paths.add(pathHeader);
532                 }
533             }
534 
535             // also add the English paths
536             // initialize the first time in.
537             if (englishPaths == null) {
538                 englishPaths = new HashSet<>();
539                 final CLDRFile displayFile = CheckCLDR.getDisplayInformation();
540                 addPrettyPaths(displayFile, pathFilter, pathHeaderFactory, noaliases, true, englishPaths);
541                 addPrettyPaths(displayFile, displayFile.getExtraPaths(), pathFilter, pathHeaderFactory, noaliases,
542                     true, englishPaths);
543                 englishPaths = Collections.unmodifiableSet(englishPaths); // for robustness
544             }
545 
546             UnicodeSet missingExemplars = new UnicodeSet();
547             UnicodeSet missingCurrencyExemplars = new UnicodeSet();
548             if (checkFlexibleDates) {
549                 fset.set(file);
550             }
551             pathShower.set(localeID);
552 
553             // only create if we are going to use
554             ExampleGenerator exampleGenerator = SHOW_EXAMPLES ? new ExampleGenerator(file, englishFile,
555                 CLDRPaths.DEFAULT_SUPPLEMENTAL_DIRECTORY) : null;
556 
557             int pathCount = 0;
558             Status otherPath = new Status();
559             int rawMissingCount = 0;
560             int rawProvisionalCount = 0;
561             CLDRFile unresolved = file.getUnresolved();
562 
563             for (PathHeader pathHeader : paths) {
564                 pathCount++;
565                 String path = pathHeader.getOriginalPath();
566                 String prettyPath = pathHeader.toString().replace('\t', '|').replace(' ', '_');
567                 if (!showAll && !file.isWinningPath(path)) {
568                     continue;
569                 }
570                 final String topValue = unresolved.getStringValue(path);
571                 RawStatus rawStatus = RawStatus.present;
572 
573                 if (topValue == null) {
574                     rawStatus = RawStatus.missing;
575                     rawMissingCount++;
576                 }
577 
578                 if (!isLanguageLocale && !baileyTest) {
579                     final String sourceLocaleID = file.getSourceLocaleID(path, otherPath);
580                     if (!localeID.equals(sourceLocaleID)) {
581                         continue;
582                     }
583                     // also skip aliases
584                     if (!path.equals(otherPath.pathWhereFound)) {
585                         continue;
586                     }
587                 }
588                 if (path.contains("@alt") && path.contains("proposed")) {
589                     continue;
590                 }
591                 String value = file.getStringValue(path);
592                 if (baileyTest) {
593                     value = CldrUtility.INHERITANCE_MARKER;
594                 }
595 
596                 String fullPath = file.getFullXPath(path);
597                 if (topValue != null) {
598                     XPathParts fullParts = XPathParts.getFrozenInstance(fullPath);
599                     String draftStatus = fullParts.getAttributeValue(-1, "draft");
600                     if (draftStatus != null
601                         && !draftStatus.equals("contributed")) {
602                         rawProvisionalCount++;
603                         rawStatus = RawStatus.provisional;
604                     }
605                 }
606                 if (showMissing && rawStatus != RawStatus.present) {
607                     String englishValue = englishFile.getStringValue(path);
608                     if (englishValue == null) {
609                         englishValue = "n/a";
610                     }
611                     System.out.println(getLocaleAndName(localeID) + "\tRaw " + rawStatus
612                         + "\t" + pathHeader + "\t" + englishValue + "\t" + path);
613                 }
614 
615 
616                 String example = "";
617                 if (SHOW_EXAMPLES) {
618                     example = ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, value));
619                     showExamples(checkCldr, prettyPath, localeID, path, value, fullPath, example);
620                 }
621                 if (checkFlexibleDates) {
622                     fset.checkFlexibles(path, value, fullPath);
623                 }
624                 int limit = 1;
625                 for (int jj = 0; jj < limit; ++jj) {
626                     if (jj == 0) {
627                         checkCldr.check(path, fullPath, value, new Options(options), result);
628                     } else {
629                         checkCldr.getExamples(path, fullPath, value, new Options(options), result);
630                     }
631 
632                     boolean showedOne = false;
633                     for (Iterator<CheckStatus> it3 = result.iterator(); it3.hasNext();) {
634                         CheckStatus status = it3.next();
635                         String statusString = status.toString(); // com.ibm.icu.impl.Utility.escape(
636                         CheckStatus.Type statusType = status.getType();
637                         Object[] parameters = status.getParameters();
638 
639                         if (parameters != null) {
640                             if (parameters.length >= 1 && status.getCause().getClass() == CheckForExemplars.class) {
641                                 try {
642                                     UnicodeSet set = new UnicodeSet(parameters[0].toString());
643                                     if (status.getMessage().contains("currency")) {
644                                         missingCurrencyExemplars.addAll(set);
645                                     } else {
646                                         missingExemplars.addAll(set);
647                                     }
648                                 } catch (RuntimeException e) {
649                                 } // skip if not parseable as set
650                             }
651                         }
652 
653                         if (errorsOnly && !statusType.equals(CheckStatus.errorType)) {
654                             continue;
655                         }
656 
657                         if (subtypeFilter != null) {
658                             if (!subtypeFilter.contains(status.getSubtype())) {
659                                 continue;
660                             }
661                         }
662                         if (checkOnSubmit) {
663                             if (!status.isCheckOnSubmit() || !statusType.equals(CheckStatus.errorType)) continue;
664                         }
665 
666                         if (statusType.equals(CheckStatus.demoType)) {
667                             SimpleDemo d = status.getDemo();
668                             if (d != null && d instanceof FormatDemo) {
669                                 FormatDemo fd = (FormatDemo) d;
670                                 m.clear();
671                                 if (d.processPost(m)) System.out.println("\tDemo:\t" + fd.getPlainText(m));
672                             }
673                             continue;
674                         }
675 
676                         if (parameters != null) {
677                             for (int i = 0; i < parameters.length; ++i) {
678                                 if (showStackTrace && parameters[i] instanceof Throwable) {
679                                     ((Throwable) parameters[i]).printStackTrace();
680                                 }
681                             }
682                         }
683 
684                         showValue(file, prettyPath, localeID, example, path, value, fullPath, statusString,
685                             status.getSubtype());
686                         showedOne = true;
687                     }
688                     if (!showedOne && phase != Phase.FINAL_TESTING) {
689                         if (!showedOne && showAll) {
690                             showValue(file, prettyPath, localeID, example, path, value, fullPath, "ok", Subtype.none);
691                             showedOne = true;
692                         }
693                     }
694                 }
695             }
696 
697             if (resolveVotesDirectory != null) {
698                 LocaleVotingData.resolveErrors(localeID);
699             }
700 
701             showSummary(localeID, level, "Items:\t" + pathCount
702                 + "\tRaw Missing:\t" + rawMissingCount
703                 + "\tRaw Provisional:\t" + rawProvisionalCount);
704 
705             if (missingExemplars.size() != 0) {
706                 missingExemplars.removeAll(new UnicodeSet("[[:Uppercase:]-[İ]]")); // remove uppercase #4670
707                 if (missingExemplars.size() != 0) {
708                     Collator col = Collator.getInstance(new ULocale(localeID));
709                     showSummary(localeID, level, "Total missing from general exemplars:\t" +
710                         missingExemplars.size()
711                     + "\t" + new UnicodeSetPrettyPrinter()
712                     .setOrdering(col != null ? col : Collator.getInstance(ULocale.ROOT))
713                     .setSpaceComparator(col != null ? col : Collator.getInstance(ULocale.ROOT)
714                         .setStrength2(Collator.PRIMARY))
715                     .setCompressRanges(false)
716                     .format(missingExemplars));
717                 }
718             }
719             if (missingCurrencyExemplars.size() != 0) {
720                 Collator col = Collator.getInstance(new ULocale(localeID));
721                 showSummary(localeID, level, "Total missing from currency exemplars:\t"
722                     + new UnicodeSetPrettyPrinter()
723                     .setOrdering(col != null ? col : Collator.getInstance(ULocale.ROOT))
724                     .setSpaceComparator(col != null ? col : Collator.getInstance(ULocale.ROOT)
725                         .setStrength2(Collator.PRIMARY))
726                     .setCompressRanges(true)
727                     .format(missingCurrencyExemplars));
728             }
729             for (ErrorType type : subtotalCount.keySet()) {
730                 showSummary(localeID, level, "Subtotal " + type + ":\t" + subtotalCount.getCount(type));
731             }
732 
733             if (checkFlexibleDates) {
734                 fset.showFlexibles();
735             }
736             if (SHOW_EXAMPLES) {
737                 // ldml/dates/timeZoneNames/zone[@type="America/Argentina/San_Juan"]/exemplarCity
738                 for (String zone : StandardCodes.make().getGoodAvailableCodes("tzid")) {
739                     String path = "//ldml/dates/timeZoneNames/zone[@type=\"" + zone + "\"]/exemplarCity";
740                     PathHeader pathHeader = pathHeaderFactory.fromPath(path);
741                     String prettyPath = pathHeader.toString().replace('\t', '|').replace(' ', '_');
742                     if (pathFilter != null && !pathFilter.reset(path).matches()) {
743                         continue;
744                     }
745                     String fullPath = file.getStringValue(path);
746                     if (fullPath != null) {
747                         continue;
748                     }
749                     /*
750                      * TODO: fix this code. Calling getExampleHtml with value = null will always return null,
751                      * so what's this supposed to accomplish?
752                      */
753                     String example = ExampleGenerator.simplify(exampleGenerator.getExampleHtml(path, null /* value */));
754                     showExamples(checkCldr, prettyPath, localeID, path, null, fullPath, example);
755                 }
756             }
757             System.out.println("# Elapsed time: " + timer);
758             System.out.flush();
759         }
760 
761         if (ErrorFile.errorFileWriter != null) {
762             ErrorFile.closeErrorFile();
763         }
764 
765         if (ErrorFile.generated_html_directory != null) {
766             ErrorFile.writeErrorCountsText();
767             ErrorFile.writeErrorFileIndex();
768         }
769         System.out.println();
770         for (ErrorType type : totalCount.keySet()) {
771             System.out.println("# Total " + type + ":\t" + totalCount.getCount(type));
772         }
773 
774         System.out.println();
775         System.out.println("# Total elapsed time: " + totalTimer);
776         if (fatalErrors.size() != 0) {
777             System.out.println("# FATAL ERRORS:");
778         }
779         long errorCount = totalCount.getCount(ErrorType.error) + fatalErrors.size();
780         if (errorCount != 0) {
781             System.out.println();
782             System.out.println("<< FAILURE - Error count is " + errorCount + " . >>");
783             System.exit(-1);
784         } else {
785             System.out.println();
786             System.out.println("<< SUCCESS - No errors found. >>");
787         }
788         if (LogicalGrouping.GET_TYPE_COUNTS) {
789             for (String s : LogicalGrouping.typeCount.keySet()) {
790                 System.out.println(s + "=" + LogicalGrouping.typeCount.get(s));
791             }
792         }
793         checkCldr.handleFinish();
794     } // end of main()
795 
796     static class LocaleVotingData {
797         private int disputedCount = 0;
798         Counter<Organization> missingOrganizationCounter = new Counter<>(true);
799         Counter<Organization> goodOrganizationCounter = new Counter<>(true);
800         Counter<Organization> conflictedOrganizations = new Counter<>(true);
801         Counter<VoteResolver.Status> winningStatusCounter = new Counter<>(true);
802 
803         static Map<String, LocaleVotingData> localeToErrors = new HashMap<>();
804         private static Map<Integer, String> idToPath;
805 
resolveErrors(String locale)806         public static void resolveErrors(String locale) {
807             localeToErrors.put(locale, new LocaleVotingData(locale));
808         }
809 
LocaleVotingData(String locale)810         public LocaleVotingData(String locale) {
811 
812             Map<Organization, VoteResolver.Level> orgToMaxVote = voterInfoList.getOrganizationToMaxVote(locale);
813 
814             Map<Integer, Map<Integer, CandidateInfo>> info = VoteResolver
815                 .getBaseToAlternateToInfo(resolveVotesDirectory + locale + ".xml", voterInfoList);
816 
817             Map<String, Integer> valueToItem = new HashMap<>();
818 
819             for (int basePath : info.keySet()) {
820                 final Map<Integer, CandidateInfo> itemInfo = info.get(basePath);
821 
822                 // find the last release status and value
823                 voteResolver.clear();
824                 valueToItem.clear();
825 
826                 for (int item : itemInfo.keySet()) {
827                     String itemValue = getValue(item);
828                     valueToItem.put(itemValue, item);
829 
830                     CandidateInfo candidateInfo = itemInfo.get(item);
831                     if (candidateInfo.oldStatus != null) {
832                         voteResolver.setBaseline(itemValue, candidateInfo.oldStatus);
833                     }
834                     voteResolver.add(itemValue);
835                     for (int voter : candidateInfo.voters) {
836                         try {
837                             voteResolver.add(itemValue, voter);
838                         } catch (UnknownVoterException e) {
839                             // skip
840                         }
841                     }
842                 }
843 
844                 EnumSet<Organization> basePathConflictedOrganizations = voteResolver.getConflictedOrganizations();
845                 conflictedOrganizations.addAll(basePathConflictedOrganizations, 1);
846 
847                 VoteResolver.Status winningStatus = voteResolver.getWinningStatus();
848                 String winningValue = voteResolver.getWinningValue();
849 
850                 winningStatusCounter.add(winningStatus, 1);
851 
852                 if (winningStatus == VoteResolver.Status.approved) {
853                     continue;
854                 }
855 
856                 CandidateInfo candidateInfo = itemInfo.get(valueToItem.get(winningValue));
857                 Map<Organization, VoteResolver.Level> orgToMaxVoteHere = voterInfoList
858                     .getOrganizationToMaxVote(candidateInfo.voters);
859 
860                 // if the winning item is less than contributed, record the organizations that haven't given their
861                 // maximum vote to the winning item.
862                 if (winningStatus.compareTo(VoteResolver.Status.contributed) < 0) {
863                     // showPaths(basePath, itemInfo);
864                     for (Organization org : orgToMaxVote.keySet()) {
865                         VoteResolver.Level maxVote = orgToMaxVote.get(org);
866                         VoteResolver.Level maxVoteHere = orgToMaxVoteHere.get(org);
867                         if (maxVoteHere == null || maxVoteHere.compareTo(maxVote) < 0) {
868                             missingOrganizationCounter.add(org, 1);
869                         }
870                     }
871                     if (voteResolver.isDisputed()) {
872                         disputedCount++;
873                         String path = getIdToPath(basePath);
874                         ErrorFile.addDataToErrorFile(locale, path, ErrorType.disputed, Subtype.none);
875                     }
876                 } else {
877                     for (Organization org : orgToMaxVote.keySet()) {
878                         VoteResolver.Level maxVote = orgToMaxVote.get(org);
879                         VoteResolver.Level maxVoteHere = orgToMaxVoteHere.get(org);
880                         if (maxVoteHere == null || maxVoteHere.compareTo(maxVote) < 0) {
881                         } else {
882                             goodOrganizationCounter.add(org, 1);
883                         }
884                     }
885                 }
886             }
887             System.out.println(getLocaleAndName(locale) + "\tEnabled Organizations:\t" + orgToMaxVote);
888             if (disputedCount != 0) {
889                 System.out.println(getLocaleAndName(locale) + "\tDisputed Items:\t" + disputedCount);
890             }
891 
892             if (missingOrganizationCounter.size() > 0) {
893                 System.out.println(getLocaleAndName(locale) + "\tMIA organizations:\t" + missingOrganizationCounter);
894                 System.out
895                 .println(getLocaleAndName(locale) + "\tConflicted organizations:\t" + conflictedOrganizations);
896                 System.out.println(getLocaleAndName(locale) + "\tCool organizations!:\t" + goodOrganizationCounter);
897             }
898             System.out.println(getLocaleAndName(locale) + "\tOptimal Status:\t" + winningStatusCounter);
899         }
900 
getIdToPath(int basePath)901         private static String getIdToPath(int basePath) {
902             if (idToPath == null) {
903                 idToPath = VoteResolver.getIdToPath(resolveVotesDirectory + "xpathTable.xml");
904             }
905             return idToPath.get(basePath);
906         }
907 
get(String locale)908         public static LocaleVotingData get(String locale) {
909             return localeToErrors.get(locale);
910         }
911 
getDisputedCount()912         int getDisputedCount() {
913             return disputedCount;
914         }
915 
getConflictedHTML()916         String getConflictedHTML() {
917             String result = conflictedOrganizations.toString();
918             if (result.length() == 0) {
919                 return "";
920             }
921             result = result.substring(1, result.length() - 1);
922             result = result.replace(", ", "<br>");
923             return result;
924         }
925     }
926 
getValue(int item)927     private static String getValue(int item) {
928         return String.valueOf(item);
929     }
930 
931     static Matcher draftStatusMatcher = PatternCache.get("\\[@draft=\"(provisional|unconfirmed)\"]").matcher("");
932 
933     enum ErrorType {
934         ok, error, disputed, warning, core, posix, minimal, basic, moderate, modern, comprehensive, optional, contributed, provisional, unconfirmed, unknown;
935         static EnumSet<ErrorType> unapproved = EnumSet.range(ErrorType.contributed, ErrorType.unconfirmed);
936         static EnumSet<ErrorType> coverage = EnumSet.range(ErrorType.posix, ErrorType.optional);
937         static EnumSet<ErrorType> showInSummary = EnumSet.of(
938             ErrorType.error, ErrorType.warning, ErrorType.posix, ErrorType.minimal, ErrorType.basic);
939 
fromStatusString(String statusString)940         static ErrorType fromStatusString(String statusString) {
941             ErrorType shortStatus = statusString.equals("ok") ? ErrorType.ok
942                 : statusString.startsWith("Error") ? ErrorType.error
943                     : statusString.equals("disputed") ? ErrorType.disputed
944                         : statusString.startsWith("Warning") ? ErrorType.warning
945                             : statusString.equals("contributed") ? ErrorType.contributed
946                                 : statusString.equals("provisional") ? ErrorType.provisional
947                                     : statusString.equals("unconfirmed") ? ErrorType.unconfirmed
948                                         : ErrorType.unknown;
949             if (shortStatus == ErrorType.unknown) {
950                 throw new IllegalArgumentException("Unknown error type: " + statusString);
951             } else if (shortStatus == ErrorType.warning) {
952                 if (coverageMatcher.reset(statusString).find()) {
953                     shortStatus = ErrorType.valueOf(coverageMatcher.group(1));
954                 }
955             }
956             return shortStatus;
957         }
958     }
959 
960     static class ErrorFile {
961 
962         private static final boolean SHOW_VOTING_INFO = false;
963         public static CLDRFile voteFile;
964         public static Factory voteFactory;
965 
openErrorFile(String localeID, String baseLanguage)966         private static void openErrorFile(String localeID, String baseLanguage) throws IOException {
967             if (ErrorFile.errorFileWriter != null) {
968                 ErrorFile.closeErrorFile();
969             }
970             ErrorFile.errorFileWriter = FileUtilities.openUTF8Writer(ErrorFile.generated_html_directory, baseLanguage + ".html");
971             ErrorFile.errorFileTable = new TablePrinter();
972             errorFileCounter.clear();
973             ErrorFile.errorFileTable.setCaption("Problem Details")
974             .addColumn("Problem").setCellAttributes("align=\"left\" class=\"{0}\"").setSortPriority(0)
975             .setSpanRows(true)
976             .setBreakSpans(true).setRepeatHeader(true).setHeaderCell(true)
977             .addColumn("Subtype").setCellAttributes("align=\"left\" class=\"{1}\"").setSortPriority(1)
978             .setSpanRows(true)
979             .setBreakSpans(true).setRepeatHeader(true).setHeaderCell(true)
980             .addColumn("Locale").setCellAttributes("class=\"{1}\"")
981             .setCellPattern("<a href=\"http://unicode.org/cldr/apps/survey?_={0}\">{0}</a>").setSortPriority(2)
982             .setSpanRows(true).setBreakSpans(true)//.setRepeatDivider(true)
983             .addColumn("Name").setCellAttributes("class=\"{1}\"").setSpanRows(true)
984             .setBreakSpans(true)
985             .addColumn("Section").setCellAttributes("class=\"{1}\"").setSortPriority(3)
986             .setCellPattern("<a href=\"http://unicode.org/cldr/apps/survey?_={3}&x={0}\">{0}</a>")
987             .setSpanRows(true)
988             .addColumn("Count").setCellAttributes("class=\"{1}\" align=\"right\"");
989 
990             showIndexHead("", localeID, ErrorFile.errorFileWriter);
991         }
992 
993         static TablePrinter errorFileTable = new TablePrinter();
994         static Counter<Row.R4<String, String, ErrorType, Subtype>> errorFileCounter = new Counter<>(
995             true);
996 
addDataToErrorFile(String localeID, String path, ErrorType shortStatus, Subtype subtype)997         private static void addDataToErrorFile(String localeID, String path, ErrorType shortStatus,
998             Subtype subtype) {
999             String section = path == null ? null : XPathToMenu.xpathToMenu(path);
1000             if (section == null) {
1001                 section = "general";
1002             }
1003             errorFileCounter.add(
1004                 new Row.R4<>(localeID, section, shortStatus, subtype), 1);
1005             ErrorFile.sectionToProblemsToLocaleToCount.add(
1006                 new Row.R4<>(section, shortStatus, subtype, localeID), 1);
1007         }
1008 
closeErrorFile()1009         private static void closeErrorFile() {
1010             Set<String> locales = new TreeSet<>();
1011             for (Row.R4<String, String, ErrorType, Subtype> item : errorFileCounter.keySet()) {
1012                 String localeID = item.get0();
1013                 locales.add(localeID);
1014                 String section = item.get1();
1015                 ErrorType shortStatus = item.get2();
1016                 Subtype subtype = item.get3();
1017                 errorFileTable.addRow()
1018                 .addCell(shortStatus)
1019                 .addCell(subtype)
1020                 .addCell(localeID)
1021                 .addCell(ConsoleCheckCLDR.getLocaleName(localeID))
1022                 .addCell(section) // menuPath == null ? "" : "<a href='" + link + "'>" + menuPath + "</a>"
1023                 .addCell(errorFileCounter.getCount(item))
1024                 .finishRow();
1025             }
1026 
1027             if (SHOW_VOTING_INFO) {
1028                 TablePrinter data = new TablePrinter().setCaption("Voting Information")
1029                     .addColumn("Locale").setHeaderCell(true)
1030                     .addColumn("Name").setHeaderCell(true)
1031                     .addColumn("Organization")
1032                     .addColumn("Missing")
1033                     .addColumn("Conflicted")
1034                     ;
1035                 for (String localeID : locales) {
1036                     // now the voting info
1037                     LocaleVotingData localeVotingData = LocaleVotingData.localeToErrors.get(localeID);
1038                     if (localeVotingData != null) {
1039                         // find all the orgs with data
1040                         EnumSet<Organization> orgs = EnumSet.noneOf(Organization.class);
1041                         orgs.addAll(localeVotingData.missingOrganizationCounter.keySet());
1042                         orgs.addAll(localeVotingData.conflictedOrganizations.keySet());
1043                         orgs.addAll(localeVotingData.goodOrganizationCounter.keySet());
1044                         for (Organization org : orgs) {
1045                             data.addRow()
1046                             .addCell(ConsoleCheckCLDR.getLinkedLocale(localeID))
1047                             .addCell(ConsoleCheckCLDR.getLocaleName(localeID))
1048                             .addCell(org)
1049                             .addCell(localeVotingData.missingOrganizationCounter.getCount(org))
1050                             .addCell(localeVotingData.conflictedOrganizations.getCount(org))
1051                             .finishRow();
1052                         }
1053                     }
1054                 }
1055                 ErrorFile.errorFileWriter.println(data.toTable());
1056                 ErrorFile.errorFileWriter.println("<p></p>");
1057             }
1058 
1059             // generated_html.println("<table border='1' style='border-collapse: collapse' bordercolor='#CCCCFF'>");
1060             // Locale Group Error Warning Missing Votes: Contributed Missing Votes: Provisional Missing Votes:
1061             // Unconfirmed Missing Coverage: Posix Missing Coverage: Minimal Missing Coverage: Basic Missing Coverage:
1062             // Moderate Missing Coverage: Modern
1063             ErrorFile.errorFileWriter.println(ErrorFile.errorFileTable.toTable());
1064             ErrorFile.errorFileWriter.println(ShowData.dateFooter());
1065             ErrorFile.errorFileWriter.println(CldrUtility.ANALYTICS);
1066             ErrorFile.errorFileWriter.println("</body></html>");
1067             ErrorFile.errorFileWriter.close();
1068             ErrorFile.errorFileTable = null;
1069         }
1070 
1071         // ================ Index File ===================
1072 
showErrorFileIndex(PrintWriter generated_html_index)1073         static void showErrorFileIndex(PrintWriter generated_html_index) {
1074 
1075             // get organizations
1076             Relation<Organization, String> orgToLocales = getOrgToLocales();
1077 
1078             TablePrinter indexTablePrinter = new TablePrinter().setCaption("Problem Summary")
1079                 .setTableAttributes("border='1' style='border-collapse: collapse' bordercolor='blue'")
1080                 .addColumn("BASE").setHidden(true)//.setRepeatDivider(true)
1081                 .addColumn("Locale").setCellPattern("<a name=\"{0}\" href=\"{1}.html\">{0}</a>") // link to base, anchor
1082                 // with full
1083                 .addColumn("Name");
1084             if (SHOW_VOTING_INFO) {
1085                 indexTablePrinter.addColumn("Summary")
1086                 .addColumn("Missing");
1087             }
1088             for (Organization org : orgToLocales.keySet()) {
1089                 indexTablePrinter.addColumn(org.toString().substring(0, 2));
1090             }
1091             indexTablePrinter
1092             .addColumn("Disputed").setHeaderAttributes("class='disputed'").setCellAttributes("class='disputed'")
1093             .addColumn("Conflicted").setHeaderAttributes("class='conflicted'")
1094             .setCellAttributes("class='conflicted'");
1095 
1096             for (ConsoleCheckCLDR.ErrorType type : ConsoleCheckCLDR.ErrorType.showInSummary) {
1097                 String columnTitle = UCharacter.toTitleCase(type.toString(), null);
1098                 final boolean coverage = ConsoleCheckCLDR.ErrorType.coverage.contains(type);
1099                 if (coverage) {
1100                     columnTitle = "MC: " + columnTitle;
1101                 } else if (ConsoleCheckCLDR.ErrorType.unapproved.contains(type)) {
1102                     columnTitle = "MV: " + columnTitle;
1103                 }
1104                 indexTablePrinter.addColumn(columnTitle).setHeaderAttributes("class='" + type + "'")
1105                 .setCellAttributes("class='" + type + "'");
1106             }
1107 
1108             // now fill in the data
1109             LanguageTagParser ltp = new LanguageTagParser();
1110             for (String key : ErrorFile.errorFileIndexData.keySet()) {
1111                 Pair<String, Counter<ErrorType>> pair = ErrorFile.errorFileIndexData.get(key);
1112                 String htmlOpenedFileLanguage = pair.getFirst();
1113                 Counter<ErrorType> counts = pair.getSecond();
1114                 LocaleVotingData votingData = LocaleVotingData.get(htmlOpenedFileLanguage);
1115                 if (counts.getTotal() == 0) {
1116                     continue;
1117                 }
1118                 final String baseLanguage = ltp.set(htmlOpenedFileLanguage).getLanguage();
1119                 indexTablePrinter.addRow()
1120                 .addCell(baseLanguage)
1121                 .addCell(htmlOpenedFileLanguage)
1122                 .addCell(ConsoleCheckCLDR.getLocaleName(htmlOpenedFileLanguage));
1123                 if (SHOW_VOTING_INFO) {
1124                     indexTablePrinter.addCell(votingData == null ? "" : votingData.winningStatusCounter.toString())
1125                     .addCell(votingData == null ? "" : votingData.missingOrganizationCounter.toString());
1126                 }
1127                 for (Organization org : orgToLocales.keySet()) {
1128                     indexTablePrinter.addCell(orgToLocales.getAll(org).contains(htmlOpenedFileLanguage) ? org.toString()
1129                         .substring(0, 2) : "");
1130                 }
1131                 indexTablePrinter
1132                 .addCell(votingData == null ? "" : formatSkippingZero(votingData.getDisputedCount()))
1133                 .addCell(votingData == null ? "" : votingData.getConflictedHTML());
1134                 for (ConsoleCheckCLDR.ErrorType type : ConsoleCheckCLDR.ErrorType.showInSummary) {
1135                     indexTablePrinter.addCell(formatSkippingZero(counts.getCount(type)));
1136                 }
1137                 indexTablePrinter.finishRow();
1138             }
1139             generated_html_index.println(indexTablePrinter.toTable());
1140             generated_html_index.println(ShowData.dateFooter());
1141             generated_html_index.println(CldrUtility.ANALYTICS);
1142             generated_html_index.println("</body></html>");
1143         }
1144 
1145         static Relation<Organization, String> orgToLocales;
1146 
getOrgToLocales()1147         private static Relation<Organization, String> getOrgToLocales() {
1148             if (orgToLocales == null) {
1149                 orgToLocales = Relation.of(new TreeMap<Organization, Set<String>>(), TreeSet.class);
1150                 StandardCodes sc = StandardCodes.make();
1151                 for (Organization org : sc.getLocaleCoverageOrganizations()) {
1152                     for (String locale : sc.getLocaleCoverageLocales(org)) {
1153                         Level x = sc.getLocaleCoverageLevel(org, locale);
1154                         if (x.compareTo(Level.BASIC) > 0) {
1155                             orgToLocales.put(org, locale);
1156                         }
1157                     }
1158                 }
1159             }
1160             return orgToLocales;
1161         }
1162 
showSections()1163         static void showSections() throws IOException {
1164             Relation<Organization, String> orgToLocales = getOrgToLocales();
1165             TablePrinter indexTablePrinter = new TablePrinter().setCaption("Problem Summary")
1166                 .setTableAttributes("border='1' style='border-collapse: collapse' bordercolor='blue'")
1167                 .addColumn("Section").setSpanRows(true).setBreakSpans(true)//.setRepeatDivider(true)
1168                 .addColumn("Problems").setCellAttributes("style=\"text-align:left\" class=\"{2}\"").setSpanRows(true)
1169                 .addColumn("Subtype").setCellAttributes("style=\"text-align:left\" class=\"{2}\"").setSpanRows(true)
1170                 .addColumn("Locale").setCellAttributes("class=\"{2}\"")
1171                 .addColumn("Code").setCellAttributes("class=\"{2}\"")
1172                 .setCellPattern("<a href=\"http://unicode.org/cldr/apps/survey?_={0}&x={1}\">{0}</a>") // TODO: use CLDRConfig.urls()
1173                 .addColumn("Count").setCellAttributes("class=\"{2}\"");
1174             for (Organization org : orgToLocales.keySet()) {
1175                 indexTablePrinter.addColumn(org.toString().substring(0, 2));
1176             }
1177 
1178             for (Row.R4<String, ErrorType, Subtype, String> sectionAndProblemsAndLocale : ErrorFile.sectionToProblemsToLocaleToCount
1179                 .getKeysetSortedByKey()) {
1180                 final ErrorType problem = sectionAndProblemsAndLocale.get1();
1181                 final Subtype subtype = sectionAndProblemsAndLocale.get2();
1182                 if (!ConsoleCheckCLDR.ErrorType.showInSummary.contains(problem)) {
1183                     continue;
1184                 }
1185                 final String locale = sectionAndProblemsAndLocale.get3();
1186                 if (problem != ErrorType.error && problem != ErrorType.disputed && !orgToLocales.containsValue(locale)) {
1187                     continue;
1188                 }
1189                 long count = ErrorFile.sectionToProblemsToLocaleToCount.getCount(sectionAndProblemsAndLocale);
1190                 final String section = sectionAndProblemsAndLocale.get0();
1191                 indexTablePrinter.addRow()
1192                 .addCell(section)
1193                 .addCell(problem)
1194                 .addCell(subtype)
1195                 .addCell(ConsoleCheckCLDR.getLocaleName(locale))
1196                 .addCell(locale)
1197                 .addCell(count);
1198                 for (Organization org : orgToLocales.keySet()) {
1199                     indexTablePrinter.addCell(orgToLocales.getAll(org).contains(locale) ? org.toString().substring(0, 2) : "");
1200                 }
1201                 indexTablePrinter.finishRow();
1202             }
1203             PrintWriter generated_html_index = FileUtilities.openUTF8Writer(ErrorFile.generated_html_directory, "sections.html");
1204             ConsoleCheckCLDR.ErrorFile.showIndexHead("Error Report Index by Section", "", generated_html_index);
1205             generated_html_index.println(indexTablePrinter.toTable());
1206             generated_html_index.println(ShowData.dateFooter());
1207             generated_html_index.println(CldrUtility.ANALYTICS);
1208             generated_html_index.println("</body></html>");
1209             generated_html_index.close();
1210         }
1211 
formatSkippingZero(long count)1212         static String formatSkippingZero(long count) {
1213             if (count == 0) {
1214                 return "";
1215             }
1216             return String.valueOf(count);
1217         }
1218 
showIndexHead(String title, String localeID, PrintWriter generated_html_index)1219         static void showIndexHead(String title, String localeID, PrintWriter generated_html_index) {
1220             final boolean notLocaleSpecific = localeID.length() == 0;
1221             if ((!notLocaleSpecific)) {
1222                 title = "Errors in " + ConsoleCheckCLDR.getNameAndLocale(localeID, false);
1223             }
1224             generated_html_index
1225             .println("<html>" +
1226                 "<head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>"
1227                 + CldrUtility.LINE_SEPARATOR
1228                 +
1229                 "<title>"
1230                 + title
1231                 + "</title>"
1232                 + CldrUtility.LINE_SEPARATOR
1233                 +
1234                 "<link rel='stylesheet' href='errors.css' type='text/css'>"
1235                 + CldrUtility.LINE_SEPARATOR
1236                 +
1237                 "<base target='_blank'>"
1238                 + CldrUtility.LINE_SEPARATOR
1239                 +
1240                 "</head><body>"
1241                 + CldrUtility.LINE_SEPARATOR
1242                 +
1243                 "<h1>"
1244                 + title
1245                 + "</h1>"
1246                 + CldrUtility.LINE_SEPARATOR
1247                 +
1248                 "<p>"
1249                 +
1250                 "<a href='index.html"
1251                 + (notLocaleSpecific ? "" : "#" + localeID)
1252                 + "'>Index</a>"
1253                 +
1254                 " | "
1255                 +
1256                 "<a href='sections.html"
1257                 + (notLocaleSpecific ? "" : "#" + localeID)
1258                 + "'>Index by Section</a>"
1259                 +
1260                 " | "
1261                 +
1262                 "<a href='http://unicode.org/cldr/data/docs/survey/vetting.html'><b style='background-color: yellow;'><i>Help: How to Vet</i></b></a>"
1263                 +
1264                 "</p>"
1265                 +
1266                 "<p>The following errors have been detected in the locale"
1267                 +
1268                 (notLocaleSpecific
1269                     ? "s. " + org.unicode.cldr.test.HelpMessages.getChartMessages("error_index_header")
1270                     : " " + ConsoleCheckCLDR.getNameAndLocale(localeID, false) + ". "
1271                     + ErrorFile.ERROR_CHART_HEADER));
1272         }
1273 
writeErrorFileIndex()1274         private static void writeErrorFileIndex() throws IOException {
1275             PrintWriter generated_html_index = FileUtilities.openUTF8Writer(ErrorFile.generated_html_directory, "index.html");
1276             ConsoleCheckCLDR.ErrorFile.showIndexHead("Error Report Index", "", generated_html_index);
1277             ConsoleCheckCLDR.ErrorFile.showErrorFileIndex(generated_html_index);
1278             generated_html_index.close();
1279             showSections();
1280         }
1281 
writeErrorCountsText()1282         private static void writeErrorCountsText() {
1283             // do the plain text file
1284             ErrorFile.generated_html_count.print(ConsoleCheckCLDR.lastHtmlLocaleID + ";\tcounts");
1285             for (ConsoleCheckCLDR.ErrorType type : ConsoleCheckCLDR.ErrorType.showInSummary) {
1286                 ErrorFile.generated_html_count.print(";\t" + type + "=" + ErrorFile.htmlErrorsPerLocale.getCount(type));
1287             }
1288             ErrorFile.generated_html_count.println();
1289             ErrorFile.generated_html_count.flush();
1290 
1291             // now store the data for the index
1292             ErrorFile.errorFileIndexData.put(ConsoleCheckCLDR.lastHtmlLocaleID,
1293                 new Pair<>(ConsoleCheckCLDR.lastHtmlLocaleID, ErrorFile.htmlErrorsPerLocale));
1294             ErrorFile.htmlErrorsPerLocale = new Counter<>();
1295         }
1296 
1297         static Counter<ErrorType> htmlErrorsPerLocale = new Counter<>(); // ConsoleCheckCLDR.ErrorCount();
1298         static PrintWriter generated_html_count = null;
1299         private static TreeMap<String, Pair<String, Counter<ErrorType>>> errorFileIndexData = new TreeMap<>();
1300 
1301         static PrintWriter errorFileWriter = null;
1302         private static final String ERROR_CHART_HEADER = org.unicode.cldr.test.HelpMessages
1303             .getChartMessages("error_locale_header");
1304         static String generated_html_directory = null;
1305         public static Counter<Row.R4<String, ErrorType, Subtype, String>> sectionToProblemsToLocaleToCount = new Counter<>();
1306     }
1307 
showSummary(String localeID, Level level, String value)1308     private static void showSummary(String localeID, Level level, String value) {
1309         String line = "# " + getLocaleAndName(localeID) + "\tSummary\t" + level + "\t" + value;
1310         System.out.println(line);
1311     }
1312 
showExamples(CheckCLDR checkCldr, String prettyPath, String localeID, String path, String value, String fullPath, String example)1313     private static void showExamples(CheckCLDR checkCldr, String prettyPath, String localeID,
1314         String path, String value, String fullPath, String example) {
1315         if (example != null) {
1316             showValue(checkCldr.getCldrFileToCheck(), prettyPath, localeID, example, path, value, fullPath, "ok",
1317                 Subtype.none);
1318         }
1319     }
1320 
addPrettyPaths(CLDRFile file, Matcher pathFilter, PathHeader.Factory pathHeaderFactory, boolean noaliases, boolean filterDraft, Collection<String> target)1321     private static void addPrettyPaths(CLDRFile file, Matcher pathFilter, PathHeader.Factory pathHeaderFactory,
1322         boolean noaliases, boolean filterDraft, Collection<String> target) {
1323         for (Iterator<String> pit = file.iterator(pathFilter); pit.hasNext();) {
1324             String path = pit.next();
1325             addPrettyPath(file, pathHeaderFactory, noaliases, filterDraft, target, path);
1326         }
1327     }
1328 
addPrettyPaths(CLDRFile file, Collection<String> paths, Matcher pathFilter, PathHeader.Factory pathHeaderFactory, boolean noaliases, boolean filterDraft, Collection<String> target)1329     private static void addPrettyPaths(CLDRFile file, Collection<String> paths, Matcher pathFilter,
1330         PathHeader.Factory pathHeaderFactory, boolean noaliases, boolean filterDraft, Collection<String> target) {
1331         for (String path : paths) {
1332             if (pathFilter != null && !pathFilter.reset(path).matches()) continue;
1333             addPrettyPath(file, pathHeaderFactory, noaliases, filterDraft, target, path);
1334         }
1335     }
1336 
addPrettyPath(CLDRFile file, PathHeader.Factory pathHeaderFactory, boolean noaliases, boolean filterDraft, Collection<String> target, String path)1337     private static void addPrettyPath(CLDRFile file, PathHeader.Factory pathHeaderFactory, boolean noaliases,
1338         boolean filterDraft, Collection<String> target, String path) {
1339         if (noaliases && XMLSource.Alias.isAliasPath(path)) { // this is just for console testing, the survey tool
1340             // shouldn't do it.
1341             return;
1342         }
1343         if (filterDraft) {
1344             String newPath = CLDRFile.getNondraftNonaltXPath(path);
1345             if (!newPath.equals(path)) {
1346                 String value = file.getStringValue(newPath);
1347                 if (value != null) {
1348                     return;
1349                 }
1350             }
1351         }
1352         String prettyPath = pathHeaderFactory.fromPath(path).toString();
1353         target.add(prettyPath);
1354     }
1355 
setExampleGenerator(ExampleGenerator inputExampleGenerator)1356     private static synchronized void setExampleGenerator(ExampleGenerator inputExampleGenerator) {
1357         englishExampleGenerator = inputExampleGenerator;
1358     }
1359 
getExampleGenerator()1360     private static synchronized ExampleGenerator getExampleGenerator() {
1361         return englishExampleGenerator;
1362     }
1363 
1364     private static ExampleGenerator englishExampleGenerator;
1365 
1366     static Matcher coverageMatcher = PatternCache.get("meet ([a-z]*) coverage").matcher(""); // HACK TODO fix
1367 
showHeaderLine()1368     private static void showHeaderLine() {
1369         if (SHOW_LOCALE) {
1370             if (idView) {
1371                 System.out
1372                 .println("Locale\tID\tDesc.\t〈Eng.Value〉\t【Eng.Ex.】\t〈Loc.Value〉\t【Loc.Ex】\t⁅error/warning type⁆\t❮Error/Warning Msg❯");
1373             } else {
1374                 System.out
1375                 .println(
1376                     "Locale\tStatus\t▸PPath◂\t〈Eng.Value〉\t【Eng.Ex.】\t〈Loc.Value〉\t«fill-in»\t【Loc.Ex】\t⁅error/warning type⁆\t❮Error/Warning Msg❯\tFull Path\tAliasedSource/Path?");
1377             }
1378         }
1379     }
1380 
1381     private static PathDescription pathDescription = null;
1382 
getIdString(String path, String value)1383     private static String getIdString(String path, String value) {
1384         if (pathDescription == null) {
1385             pathDescription = new PathDescription(supplementalDataInfo, english, null, null,
1386                 PathDescription.ErrorHandling.CONTINUE);
1387         }
1388         final String description = pathDescription.getDescription(path, value, null, null);
1389         return "\t" + StringId.getId(path) + "" + "\t" + description + "";
1390     }
1391 
showValue(CLDRFile cldrFile, String prettyPath, String localeID, String example, String path, String value, String fullPath, String statusString, Subtype subtype)1392     private static void showValue(CLDRFile cldrFile, String prettyPath, String localeID, String example,
1393         String path, String value, String fullPath, String statusString, Subtype subtype) {
1394         ErrorType shortStatus = ErrorType.fromStatusString(statusString);
1395         subtotalCount.add(shortStatus, 1);
1396         totalCount.add(shortStatus, 1);
1397         if (subtype == null) {
1398             subtype = Subtype.none;
1399         }
1400         final SourceLocation location = fullPath == null ? null : cldrFile.getSourceLocation(fullPath);
1401 
1402         if (ErrorFile.errorFileWriter == null) {
1403             example = example == null ? "" : example;
1404             String englishExample = null;
1405             final String englishPathValue = path == null ? null : getEnglishPathValue(path);
1406             if (SHOW_EXAMPLES && path != null) {
1407                 englishExample = ExampleGenerator.simplify(getExampleGenerator().getExampleHtml(path, englishPathValue));
1408             }
1409             englishExample = englishExample == null ? "" : englishExample;
1410             String cleanPrettyPath = path == null ? null : prettyPath;
1411             Status status = new Status();
1412             String sourceLocaleID = path == null ? null : cldrFile.getSourceLocaleID(path, status);
1413             String fillinValue = path == null ? null : cldrFile.getFillInValue(path);
1414             fillinValue = fillinValue == null ? "" : fillinValue.equals(value) ? "=" : fillinValue;
1415 
1416             String pathLink = CLDR_CONFIG.urls().forXpath(localeID, path);
1417 
1418             final String otherSource = path == null ? null
1419                 : (sourceLocaleID.equals(localeID) ? ""
1420                     : "\t" + sourceLocaleID);
1421             final String otherPath = path == null ? null
1422                 : (status.pathWhereFound.equals(path) ? ""
1423                     : "\t" + status.pathWhereFound);
1424             if (location != null) {
1425                 System.err.println(location.toString() + shortStatus); // print full path here
1426             }
1427             String idViewString = idView ? (path == null ? "\tNO_ID" : getIdString(path, value)) : "";
1428             System.out.println(
1429                 getLocaleAndName(localeID)
1430                 + (idViewString.isEmpty() ?
1431                     // + "\t" + subtotalCount.getCount(shortStatus)
1432                     "\t" + shortStatus
1433                     + "\t▸" + cleanPrettyPath + "◂"
1434                     + "\t〈" + englishPathValue + "〉"
1435                     + "\t【" + englishExample + "】"
1436                     + "\t〈" + value + "〉"
1437                     + "\t«" + fillinValue + "»"
1438                     + "\t【" + example + "】"
1439                     + "\t⁅" + subtype + "⁆"
1440                     + "\t❮" + statusString + "❯"
1441                     + "\t" + pathLink
1442                     + otherSource
1443                     + otherPath
1444                     : idViewString
1445                     + "\t〈" + englishPathValue + "〉"
1446                     + "\t【" + englishExample + "】"
1447                     + "\t" + value + "〉"
1448                     + "\t【" + example + "】"
1449                     + "\t⁅" + subtype + "⁆"
1450                     + "\t❮" + statusString + "❯"));
1451         } else if (ErrorFile.errorFileWriter != null) {
1452             if (shortStatus == ErrorType.contributed) {
1453                 return;
1454             }
1455             if (shortStatus == ErrorType.posix) {
1456                 shortStatus = ErrorType.minimal;
1457             }
1458             if (!localeID.equals(lastHtmlLocaleID)) {
1459                 ErrorFile.writeErrorCountsText();
1460                 lastHtmlLocaleID = localeID;
1461             }
1462             addError(shortStatus);
1463             ErrorFile.addDataToErrorFile(localeID, path, shortStatus, subtype);
1464         }
1465         if (CLDR_GITHUB_ANNOTATIONS) {
1466             // Annotate anything that needs annotation
1467             if (shortStatus == ErrorType.error || shortStatus == ErrorType.warning) {
1468                 String filePath = null;
1469                 if (location != null) {
1470                     // Use accurate location
1471                     filePath = location.forGitHub(CLDRPaths.BASE_DIRECTORY); // Trim to CLDR_DIR for GitHub
1472                 } else {
1473                     // Fallback if SourceLocation fails
1474                     filePath = "file="+localeXpathToFilePath.computeIfAbsent(Pair.of(localeID, path), locPath -> guessFilePath(locPath));
1475                 }
1476                 System.out.println("::" + shortStatus + " " + filePath.trim() + ",title=" + subtype + ":: " + statusString);
1477             }
1478         }
1479         if (PATH_IN_COUNT && ErrorFile.generated_html_count != null) {
1480             ErrorFile.generated_html_count.println(lastHtmlLocaleID + ";\tpath:\t" + path);
1481         }
1482     }
1483 
addError(ErrorType shortStatus)1484     private static void addError(ErrorType shortStatus) {
1485         if (ErrorType.showInSummary.contains(shortStatus)) {
1486             ErrorFile.htmlErrorsPerLocale.increment(shortStatus);
1487         }
1488     }
1489 
1490     static String lastHtmlLocaleID = "";
1491     private static VoterInfoList voterInfoList;
1492     private static VoteResolver<String> voteResolver;
1493     private static String resolveVotesDirectory;
1494     private static boolean idView;
1495     private static SupplementalDataInfo supplementalDataInfo;
1496     private static CLDRFile english;
1497 
1498     public static class PathShower {
1499         String localeID;
1500         boolean newLocale = true;
1501         String lastPath;
1502         String[] lastSplitPath;
1503         boolean showEnglish;
1504         String splitChar = "/";
1505 
1506         static final String lead = "****************************************";
1507 
set(String localeID)1508         public void set(String localeID) {
1509             this.localeID = localeID;
1510             newLocale = true;
1511             LocaleIDParser localeIDParser = new LocaleIDParser();
1512             showEnglish = !localeIDParser.set(localeID).getLanguageScript().equals("en");
1513             lastPath = null;
1514             lastSplitPath = null;
1515         }
1516 
getSplitChar()1517         public String getSplitChar() {
1518             return splitChar;
1519         }
1520 
setSplitChar(String splitChar)1521         public PathShower setSplitChar(String splitChar) {
1522             this.splitChar = splitChar;
1523             return this;
1524         }
1525     }
1526 
getEnglishPathValue(String path)1527     private static String getEnglishPathValue(String path) {
1528         String englishValue = CheckCLDR.getDisplayInformation().getWinningValue(path);
1529         if (englishValue == null) {
1530             String path2 = CLDRFile.getNondraftNonaltXPath(path);
1531             englishValue = CheckCLDR.getDisplayInformation().getWinningValue(path2);
1532         }
1533         return englishValue;
1534     }
1535 
1536     /**
1537      * Utility for getting information.
1538      *
1539      * @param locale
1540      * @return
1541      */
getLocaleAndName(String locale)1542     private static String getLocaleAndName(String locale) {
1543         String localizedName = CheckCLDR.getDisplayInformation().getName(locale);
1544         if (localizedName == null || localizedName.equals(locale)) return locale;
1545         return locale + " [" + localizedName + "]";
1546     }
1547 
1548     /**
1549      * Utility for getting information.
1550      *
1551      * @param locale
1552      * @param linkToXml
1553      *            TODO
1554      * @return
1555      */
getNameAndLocale(String locale, boolean linkToXml)1556     private static String getNameAndLocale(String locale, boolean linkToXml) {
1557         String localizedName = CheckCLDR.getDisplayInformation().getName(locale);
1558         if (localizedName == null || localizedName.equals(locale)) return locale;
1559         if (linkToXml) {
1560             locale = "<a href='https://github.com/unicode-org/cldr/tree/main/common/main/" + locale + ".xml'>" + locale + "</a>";
1561         }
1562         return localizedName + " [" + locale + "]";
1563     }
1564 
getLocaleName(String locale)1565     private static String getLocaleName(String locale) {
1566         String localizedName = CheckCLDR.getDisplayInformation().getName(locale);
1567         if (localizedName == null || localizedName.equals(locale)) return locale;
1568         return localizedName;
1569     }
1570 
getLinkedLocale(String locale)1571     private static String getLinkedLocale(String locale) {
1572         return "<a href='http://unicode.org/cldr/apps/survey?_=" + locale + "'>" + locale + "</a>";
1573     }
1574 
1575 
1576     /**
1577      * Approximate xml path
1578      */
guessFilePath(Pair<String, String> locPath)1579     private static String guessFilePath(Pair<String, String> locPath) {
1580         final File base = new File(CLDRPaths.BASE_DIRECTORY);
1581         final String loc = locPath.getFirst();
1582         final String path = locPath.getSecond();
1583         String subdir = "main";
1584         if (path.startsWith("//ldml/annotations")) {
1585             subdir = "annotations";
1586         } else if(path.startsWith("//ldml/subdivisions")) {
1587             subdir = "subdivisions";
1588         }
1589         File inCommon = new File(base, "common");
1590         File subsub = new File(inCommon, subdir);
1591         if (subsub.isDirectory()) {
1592             File subFile = new File(subsub, loc+".xml");
1593             if (subFile.canRead()) return subFile.getAbsolutePath().substring(base.getAbsolutePath().length() + 1);
1594         }
1595 
1596         File inSeed = new File(base, "seed");
1597         subsub = new File(inSeed, subdir);
1598         if (subsub.isDirectory()) {
1599             File subFile = new File(subsub, loc+".xml");
1600             if (subFile.canRead()) return subFile.getAbsolutePath().substring(base.getAbsolutePath().length() + 1);
1601         }
1602         return loc+".xml";
1603     }
1604     final static ConcurrentHashMap<Pair<String, String>, String> localeXpathToFilePath = new ConcurrentHashMap<>();
1605 }
1606