• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ******************************************************************************
3  * Copyright (C) 2005-2014, International Business Machines Corporation and   *
4  * others. All Rights Reserved.                                               *
5  ******************************************************************************
6  */
7 
8 package org.unicode.cldr.test;
9 
10 import java.io.BufferedReader;
11 import java.io.IOException;
12 import java.text.ParsePosition;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.EnumSet;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.TreeSet;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 
26 import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype;
27 import org.unicode.cldr.util.CLDRFile;
28 import org.unicode.cldr.util.CLDRInfo.CandidateInfo;
29 import org.unicode.cldr.util.CLDRInfo.PathValueInfo;
30 import org.unicode.cldr.util.CLDRInfo.UserInfo;
31 import org.unicode.cldr.util.CLDRLocale;
32 import org.unicode.cldr.util.CldrUtility;
33 import org.unicode.cldr.util.Factory;
34 import org.unicode.cldr.util.InternalCldrException;
35 import org.unicode.cldr.util.Level;
36 import org.unicode.cldr.util.PathHeader;
37 import org.unicode.cldr.util.PathHeader.SurveyToolStatus;
38 import org.unicode.cldr.util.PatternCache;
39 import org.unicode.cldr.util.RegexFileParser;
40 import org.unicode.cldr.util.RegexFileParser.RegexLineParser;
41 import org.unicode.cldr.util.StandardCodes;
42 import org.unicode.cldr.util.TransliteratorUtilities;
43 import org.unicode.cldr.util.VoteResolver;
44 import org.unicode.cldr.util.VoteResolver.Status;
45 
46 import com.ibm.icu.dev.util.ElapsedTimer;
47 import com.ibm.icu.impl.Row.R3;
48 import com.ibm.icu.text.ListFormatter;
49 import com.ibm.icu.text.MessageFormat;
50 import com.ibm.icu.text.Transliterator;
51 import com.ibm.icu.util.ICUUncheckedIOException;
52 
53 /**
54  * This class provides a foundation for both console-driven CLDR tests, and
55  * Survey Tool Tests.
56  * <p>
57  * To add a test, subclass CLDRFile and override handleCheck and possibly setCldrFileToCheck. Then put the test into
58  * getCheckAll.
59  * <p>
60  * To use the test, take a look at the main in ConsoleCheckCLDR. Note that you need to call setDisplayInformation with
61  * the CLDRFile for the locale that you want the display information (eg names for codes) to be in.<br>
62  * Some options are passed in the Map options. Examples: boolean SHOW_TIMES = options.containsKey("SHOW_TIMES"); // for
63  * printing times for doing setCldrFileToCheck.
64  * <p>
65  * Some errors/warnings will be explicitly filtered out when calling CheckCLDR's check() method.
66  * The full list of filters can be found in org/unicode/cldr/util/data/CheckCLDR-exceptions.txt.
67  *
68  * @author davis
69  */
70 abstract public class CheckCLDR {
71 
72     public static final boolean LIMITED_SUBMISSION = false; // TODO represent differently
73 
74     private static CLDRFile displayInformation;
75 
76     private CLDRFile cldrFileToCheck;
77     private CLDRFile englishFile = null;
78 
79     private boolean skipTest = false;
80     private Phase phase;
81     private Map<Subtype, List<Pattern>> filtersForLocale = new HashMap<Subtype, List<Pattern>>();
82 
83     public enum InputMethod {
84         DIRECT, BULK
85     }
86 
87     public enum StatusAction {
88         /**
89          * Allow voting and add new values (in Change column).
90          */
91         ALLOW,
92         /**
93          * Allow voting and ticket (in Change column).
94          */
95         ALLOW_VOTING_AND_TICKET,
96         /**
97          * Allow voting but no add new values (in Change column).
98          */
99         ALLOW_VOTING_BUT_NO_ADD,
100         /**
101          * Only allow filing a ticket.
102          */
103         ALLOW_TICKET_ONLY,
104         /**
105          * Disallow (for various reasons)
106          */
107         FORBID_ERRORS(true),
108         FORBID_READONLY(true),
109         FORBID_UNLESS_DATA_SUBMISSION(true),
110         FORBID_NULL(true);
111 
112         private final boolean isForbidden;
113 
StatusAction()114         private StatusAction() {
115             isForbidden = false;
116         };
117 
StatusAction(boolean isForbidden)118         private StatusAction(boolean isForbidden) {
119             this.isForbidden = isForbidden;
120         };
121 
isForbidden()122         public boolean isForbidden() {
123             return isForbidden;
124         }
125 
canShow()126         public boolean canShow() {
127             return !isForbidden;
128         }
129     }
130 
131     private static final HashMap<String, Phase> PHASE_NAMES = new HashMap<String, Phase>();
132 
133     public enum Phase {
134         BUILD, SUBMISSION, VETTING, FINAL_TESTING("RESOLUTION");
Phase(String... alternateName)135         Phase(String... alternateName) {
136             for (String name : alternateName) {
137                 PHASE_NAMES.put(name.toUpperCase(Locale.ENGLISH), this);
138             }
139         }
140 
forString(String value)141         public static Phase forString(String value) {
142             if (value == null) {
143                 return org.unicode.cldr.util.CLDRConfig.getInstance().getPhase();
144             }
145             value = value.toUpperCase(Locale.ENGLISH);
146             Phase result = PHASE_NAMES.get(value);
147             return result != null ? result
148                 : Phase.valueOf(value);
149         }
150 
151         /**
152          * Return whether or not to show a row, and if so, how.
153          *
154          * @param pathValueInfo
155          * @param inputMethod
156          * @param status
157          * @param userInfo
158          *            null if there is no userInfo (nobody logged in).
159          * @return
160          */
getShowRowAction( PathValueInfo pathValueInfo, InputMethod inputMethod, PathHeader.SurveyToolStatus status, UserInfo userInfo )161         public StatusAction getShowRowAction(
162             PathValueInfo pathValueInfo,
163             InputMethod inputMethod,
164             PathHeader.SurveyToolStatus status,
165             UserInfo userInfo // can get voterInfo from this.
166             ) {
167 
168             // always forbid deprecated items - don't show.
169             if (status == SurveyToolStatus.DEPRECATED) {
170                 return StatusAction.FORBID_READONLY;
171             }
172 
173             if (status == SurveyToolStatus.READ_ONLY) {
174                 return StatusAction.ALLOW_TICKET_ONLY;
175             }
176 
177             // always forbid bulk import except in data submission.
178             if (inputMethod == InputMethod.BULK && this != Phase.SUBMISSION) {
179                 return StatusAction.FORBID_UNLESS_DATA_SUBMISSION;
180             }
181 
182             // if TC+, allow anything else, even suppressed items and errors
183             if (userInfo != null && userInfo.getVoterInfo().getLevel().compareTo(VoteResolver.Level.tc) >= 0) {
184                 return StatusAction.ALLOW;
185             }
186 
187             if (status == SurveyToolStatus.HIDE) {
188                 return StatusAction.FORBID_READONLY;
189             }
190 
191             CandidateInfo winner = pathValueInfo.getCurrentItem();
192             ValueStatus valueStatus = getValueStatus(winner, ValueStatus.NONE, null);
193 
194             // if limited submission, and winner doesn't have an error, limit the values
195 
196             if (LIMITED_SUBMISSION && !SubmissionLocales.allowEvenIfLimited(pathValueInfo.getLocale().toString(), pathValueInfo.getXpath(), valueStatus == ValueStatus.ERROR, pathValueInfo.getBaselineStatus() == Status.missing)) {
197                 return StatusAction.FORBID_READONLY;
198             }
199 
200             if (this == Phase.SUBMISSION) {
201                 return (status == SurveyToolStatus.READ_WRITE || status == SurveyToolStatus.LTR_ALWAYS)
202                     ? StatusAction.ALLOW
203                         : StatusAction.ALLOW_VOTING_AND_TICKET;
204             }
205 
206             // We are in vetting, not in submission
207 
208             // Only allow ADD if we have an error or warning
209             // Only check winning value for errors/warnings per ticket #8677
210             if (valueStatus != ValueStatus.NONE) {
211                 return (status == SurveyToolStatus.READ_WRITE || status == SurveyToolStatus.LTR_ALWAYS)
212                     ? StatusAction.ALLOW
213                         : StatusAction.ALLOW_VOTING_AND_TICKET;
214             }
215 //            }
216 
217             // No warnings, so allow just voting.
218             return StatusAction.ALLOW_VOTING_BUT_NO_ADD;
219         }
220 
221         /**
222          * getAcceptNewItemAction. MUST only be called if getShowRowAction(...).canShow()
223          * TODO Consider moving Phase, StatusAction, etc into CLDRInfo.
224          *
225          * @param enteredValue
226          *            If null, means an abstention.
227          *            If voting for an existing value, pathValueInfo.getValues().contains(enteredValue) MUST be true
228          * @param pathValueInfo
229          * @param inputMethod
230          * @param status
231          * @param userInfo
232          * @return
233          */
getAcceptNewItemAction( CandidateInfo enteredValue, PathValueInfo pathValueInfo, InputMethod inputMethod, PathHeader.SurveyToolStatus status, UserInfo userInfo )234         public StatusAction getAcceptNewItemAction(
235             CandidateInfo enteredValue,
236             PathValueInfo pathValueInfo,
237             InputMethod inputMethod,
238             PathHeader.SurveyToolStatus status,
239             UserInfo userInfo // can get voterInfo from this.
240             ) {
241             if (status != SurveyToolStatus.READ_WRITE && status != SurveyToolStatus.LTR_ALWAYS) {
242                 return StatusAction.FORBID_READONLY; // not writable.
243             }
244 
245             // only logged in users can add items.
246             if (userInfo == null) {
247                 return StatusAction.FORBID_ERRORS;
248             }
249 
250             // we can always abstain
251             if (enteredValue == null) {
252                 return StatusAction.ALLOW;
253             }
254 
255             // if TC+, allow anything else, even suppressed items and errors
256             if (userInfo.getVoterInfo().getLevel().compareTo(VoteResolver.Level.tc) >= 0) {
257                 return StatusAction.ALLOW;
258             }
259 
260             // Disallow errors.
261             ValueStatus valueStatus = getValueStatus(enteredValue, ValueStatus.NONE, CheckStatus.crossCheckSubtypes);
262             if (valueStatus == ValueStatus.ERROR) {
263                 return StatusAction.FORBID_ERRORS;
264             }
265 
266             // Allow items if submission
267             if (this == Phase.SUBMISSION) {
268                 return StatusAction.ALLOW;
269             }
270 
271             // Voting for an existing value is ok
272             valueStatus = ValueStatus.NONE;
273             for (CandidateInfo value : pathValueInfo.getValues()) {
274                 if (value == enteredValue) {
275                     return StatusAction.ALLOW;
276                 }
277                 valueStatus = getValueStatus(value, valueStatus, CheckStatus.crossCheckSubtypes);
278             }
279 
280             // If there were any errors/warnings on other values, allow
281             if (valueStatus != ValueStatus.NONE) {
282                 return StatusAction.ALLOW;
283             }
284 
285             // Otherwise (we are vetting, but with no errors or warnings)
286             // DISALLOW NEW STUFF
287 
288             return StatusAction.FORBID_UNLESS_DATA_SUBMISSION;
289         }
290 
291         public enum ValueStatus {
292             ERROR, WARNING, NONE
293         }
294 
getValueStatus(CandidateInfo value, ValueStatus previous, Set<Subtype> changeErrorToWarning)295         public ValueStatus getValueStatus(CandidateInfo value, ValueStatus previous, Set<Subtype> changeErrorToWarning) {
296             if (previous == ValueStatus.ERROR || value == null) {
297                 return previous;
298             }
299 
300             for (CheckStatus item : value.getCheckStatusList()) {
301                 CheckStatus.Type type = item.getType();
302                 if (type.equals(CheckStatus.Type.Error)) {
303                     if (changeErrorToWarning != null && changeErrorToWarning.contains(item.getSubtype())) {
304                         return ValueStatus.WARNING;
305                     } else {
306                         return ValueStatus.ERROR;
307                     }
308                 } else if (type.equals(CheckStatus.Type.Warning)) {
309                     previous = ValueStatus.WARNING;
310                 }
311             }
312             return previous;
313         }
314     }
315 
316     public static final class Options implements Comparable<Options> {
317 
318         public enum Option {
319             locale,
320             CoverageLevel_requiredLevel("CoverageLevel.requiredLevel"),
321             CoverageLevel_localeType("CoverageLevel.localeType"),
322             SHOW_TIMES,
323             phase,
324             lgWarningCheck,
325             CheckCoverage_skip("CheckCoverage.skip"),
326             exemplarErrors;
327 
328             private String key;
329 
getKey()330             public String getKey() {
331                 return key;
332             }
333 
Option(String key)334             Option(String key) {
335                 this.key = key;
336             }
337 
Option()338             Option() {
339                 this.key = name();
340             }
341         };
342 
343         private static StandardCodes sc = StandardCodes.make();
344 
345         private final boolean DEBUG_OPTS = false;
346 
347         String options[] = new String[Option.values().length];
348         CLDRLocale locale = null;
349 
350         private final String key; // for fast compare
351 
352         /**
353          * Adopt some other map
354          * @param fromOptions
355          */
Options(Map<String, String> fromOptions)356         public Options(Map<String, String> fromOptions) {
357             clear();
358             setAll(fromOptions);
359             key = null; // no key = slow compare
360         }
361 
setAll(Map<String, String> fromOptions)362         private void setAll(Map<String, String> fromOptions) {
363             for (Map.Entry<String, String> e : fromOptions.entrySet()) {
364                 set(e.getKey(), e.getValue());
365             }
366         }
367 
368         /**
369          * @param key
370          * @param value
371          */
set(String key, String value)372         public void set(String key, String value) {
373             // TODO- cache the map
374             for (Option o : Option.values()) {
375                 if (o.getKey().equals(key)) {
376                     set(o, value);
377                     return;
378                 }
379             }
380             throw new IllegalArgumentException("Unknown CLDR option: '" + key + "' - valid keys are: " + Options.getValidKeys());
381         }
382 
getValidKeys()383         private static String getValidKeys() {
384             Set<String> allkeys = new TreeSet<String>();
385             for (Option o : Option.values()) {
386                 allkeys.add(o.getKey());
387             }
388             return ListFormatter.getInstance().format(allkeys);
389         }
390 
Options()391         public Options() {
392             clear();
393             key = "".intern(); // null Options.
394         }
395 
396         /**
397          * Deep clone
398          * @param options2
399          */
Options(Options options2)400         public Options(Options options2) {
401             this.options = Arrays.copyOf(options2.options, options2.options.length);
402             this.key = options2.key;
403         }
404 
Options(CLDRLocale locale, CheckCLDR.Phase testPhase, String requiredLevel, String localeType)405         public Options(CLDRLocale locale, CheckCLDR.Phase testPhase, String requiredLevel, String localeType) {
406             this.locale = locale;
407             options = new String[Option.values().length];
408             StringBuilder sb = new StringBuilder();
409             set(Option.locale, locale.getBaseName());
410             sb.append(locale.getBaseName()).append('/');
411             set(Option.CoverageLevel_requiredLevel, requiredLevel);
412             sb.append(requiredLevel).append('/');
413             set(Option.CoverageLevel_localeType, localeType);
414             sb.append(localeType).append('/');
415             set(Option.phase, testPhase.name().toLowerCase());
416             sb.append(localeType).append('/');
417             key = sb.toString().intern();
418         }
419 
420         @Override
clone()421         public Options clone() {
422             return new Options(this);
423         }
424 
425         @Override
equals(Object other)426         public boolean equals(Object other) {
427             if (this == other) return true;
428             if (!(other instanceof Options)) return false;
429             if (this.key != null && ((Options) other).key != null) {
430                 return (this.key == ((Options) other).key);
431             } else {
432                 return this.compareTo((Options) other) == 0;
433             }
434         }
435 
clear()436         private Options clear() {
437             for (int i = 0; i < options.length; i++) {
438                 options[i] = null;
439             }
440             return this;
441         }
442 
set(Option o, String v)443         private Options set(Option o, String v) {
444             options[o.ordinal()] = v;
445             if (DEBUG_OPTS) System.err.println("Setting " + o + " = " + v);
446             return this;
447         }
448 
get(Option o)449         public String get(Option o) {
450             final String v = options[o.ordinal()];
451             if (DEBUG_OPTS) System.err.println("Getting " + o + " = " + v);
452             return v;
453         }
454 
getLocale()455         public CLDRLocale getLocale() {
456             if (locale != null) return locale;
457             return CLDRLocale.getInstance(get(Option.locale));
458         }
459 
460         /**
461          * Get the required coverage level for the specified locale, for this CheckCLDR object.
462          *
463          * @param localeID
464          * @return the Level
465          *
466          * Called by CheckCoverage.setCldrFileToCheck and CheckDates.setCldrFileToCheck
467          */
getRequiredLevel(String localeID)468         public Level getRequiredLevel(String localeID) {
469             Level result;
470             // see if there is an explicit level
471             String localeType = get(Option.CoverageLevel_requiredLevel);
472             if (localeType != null) {
473                 result = Level.get(localeType);
474                 if (result != Level.UNDETERMINED) {
475                     return result;
476                 }
477             }
478             // otherwise, see if there is an organization level for the "Cldr" organization.
479             // This is not user-specific.
480             return sc.getLocaleCoverageLevel("Cldr", localeID);
481         }
482 
contains(Option o)483         public boolean contains(Option o) {
484             String s = get(o);
485             return (s != null && !s.isEmpty());
486         }
487 
488         @Override
compareTo(Options other)489         public int compareTo(Options other) {
490             if (other == this) return 0;
491             if (key != null && other.key != null) {
492                 if (key == other.key) return 0;
493                 return key.compareTo(other.key);
494             }
495             for (int i = 0; i < options.length; i++) {
496                 final String s1 = options[i];
497                 final String s2 = other.options[i];
498                 if (s1 == null && s2 == null) {
499                     // no difference
500                 } else if (s1 == null) {
501                     return -1;
502                 } else if (s2 == null) {
503                     return 1;
504                 } else {
505                     int rv = s1.compareTo(s2);
506                     if (rv != 0) {
507                         return rv;
508                     }
509                 }
510             }
511             return 0;
512         }
513 
514         @Override
hashCode()515         public int hashCode() {
516             if (key != null) return key.hashCode();
517 
518             int h = 1;
519             for (int i = 0; i < options.length; i++) {
520                 if (options[i] == null) {
521                     h *= 11;
522                 } else {
523                     h = (h * 11) + options[i].hashCode();
524                 }
525             }
526             return h;
527         }
528 
529         @Override
toString()530         public String toString() {
531             if (key != null) return "Options:" + key;
532             StringBuilder sb = new StringBuilder();
533             for (Option o : Option.values()) {
534                 if (options[o.ordinal()] != null) {
535                     sb.append(o)
536                     .append('=')
537                     .append(options[o.ordinal()])
538                     .append(' ');
539                 }
540             }
541             return sb.toString();
542         }
543     };
544 
isSkipTest()545     public boolean isSkipTest() {
546         return skipTest;
547     }
548 
549     // this should only be set for the test in setCldrFileToCheck
setSkipTest(boolean skipTest)550     public void setSkipTest(boolean skipTest) {
551         this.skipTest = skipTest;
552     }
553 
554     /**
555      * Here is where the list of all checks is found.
556      *
557      * @param nameMatcher
558      *            Regex pattern that determines which checks are run,
559      *            based on their class name (such as .* for all checks, .*Collisions.* for CheckDisplayCollisions, etc.)
560      * @return
561      */
getCheckAll(Factory factory, String nameMatcher)562     public static CompoundCheckCLDR getCheckAll(Factory factory, String nameMatcher) {
563         return new CompoundCheckCLDR()
564             .setFilter(Pattern.compile(nameMatcher, Pattern.CASE_INSENSITIVE).matcher(""))
565             //.add(new CheckAttributeValues(factory))
566             .add(new CheckChildren(factory))
567             .add(new CheckCoverage(factory))
568             .add(new CheckDates(factory))
569             .add(new CheckForCopy(factory))
570             .add(new CheckDisplayCollisions(factory))
571             .add(new CheckExemplars(factory))
572             .add(new CheckForExemplars(factory))
573             .add(new CheckForInheritanceMarkers())
574             .add(new CheckNames())
575             .add(new CheckNumbers(factory))
576             // .add(new CheckZones()) // this doesn't work; many spurious errors that user can't correct
577             .add(new CheckMetazones())
578             .add(new CheckLogicalGroupings(factory))
579             .add(new CheckAlt())
580             .add(new CheckCurrencies())
581             .add(new CheckCasing())
582             .add(new CheckConsistentCasing(factory)) // this doesn't work; many spurious errors that user can't correct
583             .add(new CheckQuotes())
584             .add(new CheckUnits())
585             .add(new CheckWidths())
586             .add(new CheckPlaceHolders())
587             .add(new CheckNew(factory)) // this is at the end; it will check for other certain other errors and warnings and
588             // not add a message if there are any.
589             ;
590     }
591 
592     /**
593      * These determine what language is used to display information. Must be set before use.
594      *
595      * @param locale
596      * @return
597      */
getDisplayInformation()598     public static synchronized CLDRFile getDisplayInformation() {
599         return displayInformation;
600     }
601 
setDisplayInformation(CLDRFile inputDisplayInformation)602     public static synchronized void setDisplayInformation(CLDRFile inputDisplayInformation) {
603         displayInformation = inputDisplayInformation;
604     }
605 
606     /**
607      * [Warnings - please zoom in] dates/timeZoneNames/singleCountries
608      * (empty)
609      * [refs][hide] Ref: [Zoom...]
610      * [Warnings - please zoom in] dates/timeZoneNames/hours {0}/{1} {0}/{1}
611      * [refs][hide] Ref: [Zoom...]
612      * [Warnings - please zoom in] dates/timeZoneNames/hour +HH:mm;-HH:mm
613      * +HH:mm;-HH:mm
614      * [refs][hide] Ref: [Zoom...]
615      * [ok] layout/orientation (empty)
616      * [refs][hide] Ref: [Zoom...]
617      * [ok] dates/localizedPatternChars GyMdkHmsSEDFwWahKzYeugAZvcL
618      * GaMjkHmsSEDFwWxhKzAeugXZvcL
619      * [refs][hide] Ref: [Zoom...]
620      */
621 
622     /**
623      * Get the CLDRFile.
624      *
625      * @param cldrFileToCheck
626      */
getCldrFileToCheck()627     public final CLDRFile getCldrFileToCheck() {
628         return cldrFileToCheck;
629     }
630 
631     /**
632      * Don't override this, use the other setCldrFileToCheck which takes an Options instead of a Map<>
633      * @param cldrFileToCheck
634      * @param options
635      * @param possibleErrors
636      * @return
637      * @see #setCldrFileToCheck(CLDRFile, Options, List)
638      */
setCldrFileToCheck(CLDRFile cldrFileToCheck, Map<String, String> options, List<CheckStatus> possibleErrors)639     final public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Map<String, String> options,
640         List<CheckStatus> possibleErrors) {
641         return setCldrFileToCheck(cldrFileToCheck, new Options(options), possibleErrors);
642     }
643 
644     /**
645      * Set the CLDRFile. Must be done before calling check. If null is called, just skip
646      * Often subclassed for initializing. If so, make the first 2 lines:
647      * if (cldrFileToCheck == null) return this;
648      * super.setCldrFileToCheck(cldrFileToCheck);
649      * do stuff
650      *
651      * @param cldrFileToCheck
652      * @param options (not currently used)
653      * @param possibleErrors
654      *            TODO
655      */
setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options, List<CheckStatus> possibleErrors)656     public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options,
657         List<CheckStatus> possibleErrors) {
658         this.cldrFileToCheck = cldrFileToCheck;
659 
660         // Shortlist error filters for this locale.
661         loadFilters();
662         String locale = cldrFileToCheck.getLocaleID();
663         filtersForLocale.clear();
664         for (R3<Pattern, Subtype, Pattern> filter : allFilters) {
665             if (filter.get0() == null || !filter.get0().matcher(locale).matches()) continue;
666             Subtype subtype = filter.get1();
667             List<Pattern> xpaths = filtersForLocale.get(subtype);
668             if (xpaths == null) {
669                 filtersForLocale.put(subtype, xpaths = new ArrayList<Pattern>());
670             }
671             xpaths.add(filter.get2());
672         }
673         return this;
674     }
675 
676     /**
677      * Status value returned from check
678      */
679     public static class CheckStatus {
680         public static final Type alertType = Type.Comment,
681             warningType = Type.Warning,
682             errorType = Type.Error,
683             exampleType = Type.Example,
684             demoType = Type.Demo;
685 
686         public enum Type {
687             Comment, Warning, Error, Example, Demo
688         };
689 
690         public enum Subtype {
691             none, noUnproposedVariant, deprecatedAttribute, illegalPlural, invalidLocale, incorrectCasing,
692             valueMustBeOverridden,
693             valueAlwaysOverridden, nullChildFile, internalError, coverageLevel, missingPluralInfo,
694             currencySymbolTooWide, incorrectDatePattern, abbreviatedDateFieldTooWide, displayCollision,
695             illegalExemplarSet, missingAuxiliaryExemplars, extraPlaceholders, missingPlaceholders,
696             shouldntHavePlaceholders, couldNotAccessExemplars, noExemplarCharacters, modifiedEnglishValue,
697             invalidCurrencyMatchSet, multipleMetazoneMappings, noMetazoneMapping, noMetazoneMappingAfter1970,
698             noMetazoneMappingBeforeNow, cannotCreateZoneFormatter, insufficientCoverage, missingLanguageTerritoryInfo,
699             missingEuroCountryInfo, deprecatedAttributeWithReplacement, missingOrExtraDateField, internalUnicodeSetFormattingError,
700             auxiliaryExemplarsOverlap, missingPunctuationCharacters,
701 
702             charactersNotInCurrencyExemplars, asciiCharactersNotInCurrencyExemplars,
703             charactersNotInMainOrAuxiliaryExemplars, asciiCharactersNotInMainOrAuxiliaryExemplars,
704 
705             narrowDateFieldTooWide, illegalCharactersInExemplars, orientationDisagreesWithExemplars,
706             inconsistentDatePattern, inconsistentTimePattern, missingDatePattern, illegalDatePattern,
707             missingMainExemplars, mustNotStartOrEndWithSpace, illegalCharactersInNumberPattern,
708             numberPatternNotCanonical, currencyPatternMissingCurrencySymbol, missingMinusSign,
709             badNumericType, percentPatternMissingPercentSymbol, illegalNumberFormat, unexpectedAttributeValue,
710             metazoneContainsDigit, tooManyGroupingSeparators, inconsistentPluralFormat, sameAsEnglish, sameAsCode,
711             dateSymbolCollision, incompleteLogicalGroup, extraMetazoneString, inconsistentDraftStatus,
712             errorOrWarningInLogicalGroup, valueTooWide, valueTooNarrow, nameContainsYear, patternCannotContainDigits,
713             patternContainsInvalidCharacters, parenthesesNotAllowed, illegalNumberingSystem, unexpectedOrderOfEraYear,
714             invalidPlaceHolder, asciiQuotesNotAllowed, badMinimumGroupingDigits, inconsistentPeriods,
715             inheritanceMarkerNotAllowed, invalidDurationUnitPattern, invalidDelimiter, illegalCharactersInPattern,
716             badParseLenient, tooManyValues, invalidSymbol;
717 
toString()718             public String toString() {
719                 return TO_STRING.matcher(name()).replaceAll(" $1").toLowerCase();
720             }
721 
722             static Pattern TO_STRING = PatternCache.get("([A-Z])");
723         };
724 
725         /**
726          * These error don't prevent entry during submission, since they become valid if a different row is changed.
727          */
728         public static EnumSet<Subtype> crossCheckSubtypes = EnumSet.of(
729             Subtype.dateSymbolCollision,
730             Subtype.displayCollision,
731             Subtype.inconsistentDraftStatus,
732             Subtype.incompleteLogicalGroup,
733             Subtype.inconsistentPeriods,
734             Subtype.abbreviatedDateFieldTooWide,
735             Subtype.narrowDateFieldTooWide,
736             Subtype.coverageLevel);
737 
738         private Type type;
739         private Subtype subtype = Subtype.none;
740         private String messageFormat;
741         private Object[] parameters;
742         private String htmlMessage;
743         private CheckCLDR cause;
744         private boolean checkOnSubmit = true;
745 
CheckStatus()746         public CheckStatus() {
747 
748         }
749 
isCheckOnSubmit()750         public boolean isCheckOnSubmit() {
751             return checkOnSubmit;
752         }
753 
setCheckOnSubmit(boolean dependent)754         public CheckStatus setCheckOnSubmit(boolean dependent) {
755             this.checkOnSubmit = dependent;
756             return this;
757         }
758 
getType()759         public Type getType() {
760             return type;
761         }
762 
setMainType(CheckStatus.Type type)763         public CheckStatus setMainType(CheckStatus.Type type) {
764             this.type = type;
765             return this;
766         }
767 
getMessage()768         public String getMessage() {
769             String message = messageFormat;
770             if (messageFormat != null && parameters != null) {
771                 try {
772                     String fixedApos = MessageFormat.autoQuoteApostrophe(messageFormat);
773                     MessageFormat format = new MessageFormat(fixedApos);
774                     message = format.format(parameters);
775                 } catch (Exception e) {
776                     message = messageFormat;
777                     System.err.println("MessageFormat Failure: " + subtype + "; " + messageFormat + "; "
778                         + (parameters == null ? null : Arrays.asList(parameters)));
779                     // throw new IllegalArgumentException(subtype + "; " + messageFormat + "; "
780                     // + (parameters == null ? null : Arrays.asList(parameters)), e);
781                 }
782             }
783             Exception[] exceptionParameters = getExceptionParameters();
784             if (exceptionParameters != null) {
785                 for (Exception exception : exceptionParameters) {
786                     message += "; " + exception.getMessage(); // + " \t(" + exception.getClass().getName() + ")";
787                     // for (StackTraceElement item : exception.getStackTrace()) {
788                     // message += "\n\t" + item;
789                     // }
790                 }
791             }
792             return message.replace('\t', ' ');
793         }
794 
795         /**
796          * @deprecated
797          */
getHTMLMessage()798         public String getHTMLMessage() {
799             return htmlMessage;
800         }
801 
802         /**
803          * @deprecated
804          */
setHTMLMessage(String message)805         public CheckStatus setHTMLMessage(String message) {
806             htmlMessage = message;
807             return this;
808         }
809 
setMessage(String message)810         public CheckStatus setMessage(String message) {
811             if (cause == null) {
812                 throw new IllegalArgumentException("Must have cause set.");
813             }
814             if (message == null) {
815                 throw new IllegalArgumentException("Message cannot be null.");
816             }
817             this.messageFormat = message;
818             this.parameters = null;
819             return this;
820         }
821 
setMessage(String message, Object... messageArguments)822         public CheckStatus setMessage(String message, Object... messageArguments) {
823             if (cause == null) {
824                 throw new IllegalArgumentException("Must have cause set.");
825             }
826             this.messageFormat = message;
827             this.parameters = messageArguments;
828             return this;
829         }
830 
toString()831         public String toString() {
832             return getType() + ": " + getMessage();
833         }
834 
835         /**
836          * Warning: don't change the contents of the parameters after retrieving.
837          */
getParameters()838         public Object[] getParameters() {
839             return parameters;
840         }
841 
842         /**
843          * Returns any Exception parameters in the status, or null if there are none.
844          *
845          * @return
846          */
getExceptionParameters()847         public Exception[] getExceptionParameters() {
848             if (parameters == null) {
849                 return null;
850             }
851 
852             List<Exception> errors = new ArrayList<Exception>();
853             for (Object o : parameters) {
854                 if (o instanceof Exception) {
855                     errors.add((Exception) o);
856                 }
857             }
858             if (errors.size() == 0) {
859                 return null;
860             }
861             return errors.toArray(new Exception[errors.size()]);
862         }
863 
864         /**
865          * Warning: don't change the contents of the parameters after passing in.
866          */
setParameters(Object[] parameters)867         public CheckStatus setParameters(Object[] parameters) {
868             if (cause == null) {
869                 throw new IllegalArgumentException("Must have cause set.");
870             }
871             this.parameters = parameters;
872             return this;
873         }
874 
getDemo()875         public SimpleDemo getDemo() {
876             return null;
877         }
878 
getCause()879         public CheckCLDR getCause() {
880             return cause;
881         }
882 
setCause(CheckCLDR cause)883         public CheckStatus setCause(CheckCLDR cause) {
884             this.cause = cause;
885             return this;
886         }
887 
getSubtype()888         public Subtype getSubtype() {
889             return subtype;
890         }
891 
setSubtype(Subtype subtype)892         public CheckStatus setSubtype(Subtype subtype) {
893             this.subtype = subtype;
894             return this;
895         }
896 
897         /**
898          * Convenience function: return true if any items in this list are of errorType
899          *
900          * @param result
901          *            the list to check (could be null for empty)
902          * @return true if any items in result are of errorType
903          */
hasError(List<CheckStatus> result)904         public static final boolean hasError(List<CheckStatus> result) {
905             return hasType(result, errorType);
906         }
907 
908         /**
909          * Convenience function: return true if any items in this list are of errorType
910          *
911          * @param result
912          *            the list to check (could be null for empty)
913          * @return true if any items in result are of errorType
914          */
hasType(List<CheckStatus> result, Type type)915         public static boolean hasType(List<CheckStatus> result, Type type) {
916             if (result == null) return false;
917             for (CheckStatus s : result) {
918                 if (s.getType().equals(type)) {
919                     return true;
920                 }
921             }
922             return false;
923         }
924     }
925 
926     public static abstract class SimpleDemo {
927         Map<String, String> internalPostArguments = new HashMap<String, String>();
928 
929         /**
930          * @param postArguments
931          *            A read-write map containing post-style arguments. eg TEXTBOX=abcd, etc. <br>
932          *            The first time this is called, the Map should be empty.
933          * @return true if the map has been changed
934          */
getHTML(Map<String, String> postArguments)935         public abstract String getHTML(Map<String, String> postArguments) throws Exception;
936 
937         /**
938          * Only here for compatibility. Use the other getHTML instead
939          */
getHTML(String path, String fullPath, String value)940         public final String getHTML(String path, String fullPath, String value) throws Exception {
941             return getHTML(internalPostArguments);
942         }
943 
944         /**
945          * THIS IS ONLY FOR COMPATIBILITY: you can call this, then the non-postArguments form of getHTML; or better,
946          * call
947          * getHTML with the postArguments.
948          *
949          * @param postArguments
950          *            A read-write map containing post-style arguments. eg TEXTBOX=abcd, etc.
951          * @return true if the map has been changed
952          */
processPost(Map<String, String> postArguments)953         public final boolean processPost(Map<String, String> postArguments) {
954             internalPostArguments.clear();
955             internalPostArguments.putAll(postArguments);
956             return true;
957         }
958     }
959 
960     public static abstract class FormatDemo extends SimpleDemo {
961         protected String currentPattern, currentInput, currentFormatted, currentReparsed;
962         protected ParsePosition parsePosition = new ParsePosition(0);
963 
getPattern()964         protected abstract String getPattern();
965 
getSampleInput()966         protected abstract String getSampleInput();
967 
getArguments(Map<String, String> postArguments)968         protected abstract void getArguments(Map<String, String> postArguments);
969 
getHTML(Map<String, String> postArguments)970         public String getHTML(Map<String, String> postArguments) throws Exception {
971             getArguments(postArguments);
972             StringBuffer htmlMessage = new StringBuffer();
973             FormatDemo.appendTitle(htmlMessage);
974             FormatDemo.appendLine(htmlMessage, currentPattern, currentInput, currentFormatted, currentReparsed);
975             htmlMessage.append("</table>");
976             return htmlMessage.toString();
977         }
978 
getPlainText(Map<String, String> postArguments)979         public String getPlainText(Map<String, String> postArguments) {
980             getArguments(postArguments);
981             return MessageFormat.format("<\"\u200E{0}\u200E\", \"{1}\"> \u2192 \"\u200E{2}\u200E\" \u2192 \"{3}\"",
982                 (Object[]) new String[] { currentPattern, currentInput, currentFormatted, currentReparsed });
983         }
984 
985         /**
986          * @param htmlMessage
987          * @param pattern
988          * @param input
989          * @param formatted
990          * @param reparsed
991          */
appendLine(StringBuffer htmlMessage, String pattern, String input, String formatted, String reparsed)992         public static void appendLine(StringBuffer htmlMessage, String pattern, String input, String formatted,
993             String reparsed) {
994             htmlMessage.append("<tr><td><input type='text' name='pattern' value='")
995             .append(TransliteratorUtilities.toXML.transliterate(pattern))
996             .append("'></td><td><input type='text' name='input' value='")
997             .append(TransliteratorUtilities.toXML.transliterate(input))
998             .append("'></td><td>")
999             .append("<input type='submit' value='Test' name='Test'>")
1000             .append("</td><td>" + "<input type='text' name='formatted' value='")
1001             .append(TransliteratorUtilities.toXML.transliterate(formatted))
1002             .append("'></td><td>" + "<input type='text' name='reparsed' value='")
1003             .append(TransliteratorUtilities.toXML.transliterate(reparsed))
1004             .append("'></td></tr>");
1005         }
1006 
1007         /**
1008          * @param htmlMessage
1009          */
appendTitle(StringBuffer htmlMessage)1010         public static void appendTitle(StringBuffer htmlMessage) {
1011             htmlMessage.append("<table border='1' cellspacing='0' cellpadding='2'" +
1012                 // " style='border-collapse: collapse' style='width: 100%'" +
1013                 "><tr>" +
1014                 "<th>Pattern</th>" +
1015                 "<th>Unlocalized Input</th>" +
1016                 "<th></th>" +
1017                 "<th>Localized Format</th>" +
1018                 "<th>Re-Parsed</th>" +
1019                 "</tr>");
1020         }
1021     }
1022 
1023     /**
1024      * Wraps the options in an Options and delegates.
1025      * @param path
1026      *            Must be a distinguished path, such as what comes out of CLDRFile.iterator()
1027      * @param fullPath
1028      *            Must be the full path
1029      * @param value
1030      *            the value associated with the path
1031      * @param options
1032      *            A set of test-specific options. Set these with code of the form:<br>
1033      *            options.put("CoverageLevel.localeType", "G0")<br>
1034      *            That is, the key is of the form <testname>.<optiontype>, and the value is of the form <optionvalue>.<br>
1035      *            There is one general option; the following will select only the tests that should be run during this
1036      *            phase.<br>
1037      *            options.put("phase", Phase.<something>);
1038      *            It can be used for new data entry.
1039      * @param result
1040      * @return
1041      * @deprecated use CheckCLDR#check(String, String, String, Options, List)
1042      */
1043     @Deprecated
check(String path, String fullPath, String value, Map<String, String> options, List<CheckStatus> result)1044     public final CheckCLDR check(String path, String fullPath, String value, Map<String, String> options,
1045         List<CheckStatus> result) {
1046         return check(path, fullPath, value, new Options(options), result);
1047     }
1048 
1049     /**
1050      * Checks the path/value in the cldrFileToCheck for correctness, according to some criterion.
1051      * If the path is relevant to the check, there is an alert or warning, then a CheckStatus is added to List.
1052      *
1053      * @param path
1054      *            Must be a distinguished path, such as what comes out of CLDRFile.iterator()
1055      * @param fullPath
1056      *            Must be the full path
1057      * @param value
1058      *            the value associated with the path
1059      * @param result
1060      */
check(String path, String fullPath, String value, Options options, List<CheckStatus> result)1061     public final CheckCLDR check(String path, String fullPath, String value, Options options,
1062         List<CheckStatus> result) {
1063         if (cldrFileToCheck == null) {
1064             throw new InternalCldrException("CheckCLDR problem: cldrFileToCheck must not be null");
1065         }
1066         if (path == null) {
1067             throw new InternalCldrException("CheckCLDR problem: path must not be null");
1068         }
1069         // if (fullPath == null) {
1070         // throw new InternalError("CheckCLDR problem: fullPath must not be null");
1071         // }
1072         // if (value == null) {
1073         // throw new InternalError("CheckCLDR problem: value must not be null");
1074         // }
1075         result.clear();
1076 
1077         /*
1078          * If the item is non-winning, and either inherited or it is code-fallback, then don't run
1079          * any tests on this item.  See http://unicode.org/cldr/trac/ticket/7574
1080          *
1081          * The following conditional formerly used "value == ..." and "value != ...", which in Java doesn't
1082          * mean what it does in some other languages. The condition has been changed to use the equals() method.
1083          * Since value can be null, check for that first.
1084          */
1085         // if (value == cldrFileToCheck.getBaileyValue(path, null, null) && value != cldrFileToCheck.getWinningValue(path)) {
1086         if (value != null
1087             && value.equals(cldrFileToCheck.getBaileyValue(path, null, null))
1088             && !value.equals(cldrFileToCheck.getWinningValue(path))) {
1089             return this;
1090         }
1091         // If we're being asked to run tests for an inheritance marker, then we need to change it
1092         // to the "real" value first before running tests. Testing the value CldrUtility.INHERITANCE_MARKER ("↑↑↑") doesn't make sense.
1093         if (CldrUtility.INHERITANCE_MARKER.equals(value)) {
1094             value = cldrFileToCheck.getConstructedBaileyValue(path, null, null);
1095             // If it hasn't changed, then don't run any tests.
1096             if (CldrUtility.INHERITANCE_MARKER.equals(value)) {
1097                 return this;
1098             }
1099         }
1100         CheckCLDR instance = handleCheck(path, fullPath, value, options, result);
1101         Iterator<CheckStatus> iterator = result.iterator();
1102         // Filter out any errors/warnings that match the filter list in CheckCLDR-exceptions.txt.
1103         while (iterator.hasNext()) {
1104             CheckStatus status = iterator.next();
1105             if (shouldExcludeStatus(fullPath, status)) {
1106                 iterator.remove();
1107             }
1108         }
1109         return instance;
1110     }
1111 
1112     /**
1113      * Returns any examples in the result parameter. Both examples and demos can
1114      * be returned. A demo will have getType() == CheckStatus.demoType. In that
1115      * case, there will be no getMessage or getHTMLMessage available; instead,
1116      * call getDemo() to get the demo, then call getHTML() to get the initial
1117      * HTML.
1118      */
getExamples(String path, String fullPath, String value, Options options, List<CheckStatus> result)1119     public final CheckCLDR getExamples(String path, String fullPath, String value, Options options,
1120         List<CheckStatus> result) {
1121         result.clear();
1122         return handleGetExamples(path, fullPath, value, options, result);
1123     }
1124 
1125     @SuppressWarnings("unused")
handleGetExamples(String path, String fullPath, String value, Options options2, List<CheckStatus> result)1126     protected CheckCLDR handleGetExamples(String path, String fullPath, String value, Options options2,
1127         List<CheckStatus> result) {
1128         return this; // NOOP unless overridden
1129     }
1130 
1131     /**
1132      * This is what the subclasses override.
1133      * If they ever use pathParts or fullPathParts, they need to call initialize() with the respective
1134      * path. Otherwise they must NOT change pathParts or fullPathParts.
1135      * <p>
1136      * If something is found, a CheckStatus is added to result. This can be done multiple times in one call, if multiple
1137      * errors or warnings are found. The CheckStatus may return warnings, errors, examples, or demos. We may expand that
1138      * in the future.
1139      * <p>
1140      * The code to add the CheckStatus will look something like::
1141      *
1142      * <pre>
1143      * result.add(new CheckStatus()
1144      *     .setType(CheckStatus.errorType)
1145      *     .setMessage(&quot;Value should be {0}&quot;, new Object[] { pattern }));
1146      * </pre>
1147      *
1148      * @param options
1149      *            TODO
1150      */
handleCheck(String path, String fullPath, String value, Options options, List<CheckStatus> result)1151     abstract public CheckCLDR handleCheck(String path, String fullPath, String value,
1152         Options options, List<CheckStatus> result);
1153 
1154     /**
1155      * Only for use in ConsoleCheck, for debugging
1156      */
handleFinish()1157     public void handleFinish() {
1158     }
1159 
1160     /**
1161      * Internal class used to bundle up a number of Checks.
1162      *
1163      * @author davis
1164      *
1165      */
1166     static class CompoundCheckCLDR extends CheckCLDR {
1167         private Matcher filter;
1168         private List<CheckCLDR> checkList = new ArrayList<CheckCLDR>();
1169         private List<CheckCLDR> filteredCheckList = new ArrayList<CheckCLDR>();
1170 
add(CheckCLDR item)1171         public CompoundCheckCLDR add(CheckCLDR item) {
1172             checkList.add(item);
1173             if (filter == null) {
1174                 filteredCheckList.add(item);
1175             } else {
1176                 final String className = item.getClass().getName();
1177                 if (filter.reset(className).find()) {
1178                     filteredCheckList.add(item);
1179                 }
1180             }
1181             return this;
1182         }
1183 
handleCheck(String path, String fullPath, String value, Options options, List<CheckStatus> result)1184         public CheckCLDR handleCheck(String path, String fullPath, String value,
1185             Options options, List<CheckStatus> result) {
1186             result.clear();
1187             // If we're being asked to run tests for an inheritance marker, then we need to change it
1188             // to the "real" value first before running tests. Testing the value CldrUtility.INHERITANCE_MARKER ("↑↑↑") doesn't make sense.
1189             if (CldrUtility.INHERITANCE_MARKER.equals(value)) {
1190                 value = getCldrFileToCheck().getConstructedBaileyValue(path, null, null);
1191             }
1192             for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) {
1193                 CheckCLDR item = it.next();
1194                 // skip proposed items in final testing.
1195                 if (Phase.FINAL_TESTING == item.getPhase()) {
1196                     if (path.contains("proposed") && path.contains("[@alt=")) {
1197                         continue;
1198                     }
1199                 }
1200                 try {
1201                     if (!item.isSkipTest()) {
1202                         item.handleCheck(path, fullPath, value, options, result);
1203                     }
1204                 } catch (Exception e) {
1205                     addError(result, item, e);
1206                     return this;
1207                 }
1208             }
1209             return this;
1210         }
1211 
1212         @Override
handleFinish()1213         public void handleFinish() {
1214             for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) {
1215                 CheckCLDR item = it.next();
1216                 item.handleFinish();
1217             }
1218         }
1219 
handleGetExamples(String path, String fullPath, String value, Options options, List<CheckStatus> result)1220         protected CheckCLDR handleGetExamples(String path, String fullPath, String value, Options options,
1221             List<CheckStatus> result) {
1222             result.clear();
1223             for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) {
1224                 CheckCLDR item = it.next();
1225                 try {
1226                     item.handleGetExamples(path, fullPath, value, options, result);
1227                 } catch (Exception e) {
1228                     addError(result, item, e);
1229                     return this;
1230                 }
1231             }
1232             return this;
1233         }
1234 
addError(List<CheckStatus> result, CheckCLDR item, Exception e)1235         private void addError(List<CheckStatus> result, CheckCLDR item, Exception e) {
1236             result.add(new CheckStatus()
1237                 .setCause(this)
1238                 .setMainType(CheckStatus.errorType)
1239                 .setSubtype(Subtype.internalError)
1240                 .setMessage("Internal error in {0}. Exception: {1}, Message: {2}, Trace: {3}",
1241                     new Object[] { item.getClass().getName(), e.getClass().getName(), e,
1242                         Arrays.asList(e.getStackTrace())
1243                 }));
1244         }
1245 
setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options, List<CheckStatus> possibleErrors)1246         public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options,
1247             List<CheckStatus> possibleErrors) {
1248             ElapsedTimer testTime = null, testOverallTime = null;
1249             if (cldrFileToCheck == null) return this;
1250             boolean SHOW_TIMES = options.contains(Options.Option.SHOW_TIMES);
1251             setPhase(Phase.forString(options.get(Options.Option.phase)));
1252             if (SHOW_TIMES) testOverallTime = new ElapsedTimer("Test setup time for setCldrFileToCheck: {0}");
1253             super.setCldrFileToCheck(cldrFileToCheck, options, possibleErrors);
1254             possibleErrors.clear();
1255 
1256             for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) {
1257                 CheckCLDR item = (CheckCLDR) it.next();
1258                 if (SHOW_TIMES)
1259                     testTime = new ElapsedTimer("Test setup time for " + item.getClass().toString() + ": {0}");
1260                 try {
1261                     item.setPhase(getPhase());
1262                     item.setCldrFileToCheck(cldrFileToCheck, options, possibleErrors);
1263                     if (SHOW_TIMES) {
1264                         if (item.isSkipTest()) {
1265                             System.out.println("Disabled : " + testTime);
1266                         } else {
1267                             System.out.println("OK : " + testTime);
1268                         }
1269                     }
1270                 } catch (RuntimeException e) {
1271                     addError(possibleErrors, item, e);
1272                     if (SHOW_TIMES) System.out.println("ERR: " + testTime + " - " + e.toString());
1273                 }
1274             }
1275             if (SHOW_TIMES) System.out.println("Overall: " + testOverallTime + ": {0}");
1276             return this;
1277         }
1278 
getFilter()1279         public Matcher getFilter() {
1280             return filter;
1281         }
1282 
setFilter(Matcher filter)1283         public CompoundCheckCLDR setFilter(Matcher filter) {
1284             this.filter = filter;
1285             filteredCheckList.clear();
1286             for (Iterator<CheckCLDR> it = checkList.iterator(); it.hasNext();) {
1287                 CheckCLDR item = it.next();
1288                 if (filter == null || filter.reset(item.getClass().getName()).matches()) {
1289                     filteredCheckList.add(item);
1290                     item.setCldrFileToCheck(getCldrFileToCheck(), (Options) null, null);
1291                 }
1292             }
1293             return this;
1294         }
1295 
getFilteredTests()1296         public String getFilteredTests() {
1297             return filteredCheckList.toString();
1298         }
1299 
getFilteredTestList()1300         public List<CheckCLDR> getFilteredTestList() {
1301             return filteredCheckList;
1302         }
1303     }
1304 
getTransliteratorFromFile(String ID, String file)1305     public static Transliterator getTransliteratorFromFile(String ID, String file) {
1306         try {
1307             BufferedReader br = CldrUtility.getUTF8Data(file);
1308             StringBuffer input = new StringBuffer();
1309             while (true) {
1310                 String line = br.readLine();
1311                 if (line == null) break;
1312                 if (line.startsWith("\uFEFF")) line = line.substring(1); // remove BOM
1313                 input.append(line);
1314                 input.append('\n');
1315             }
1316             return Transliterator.createFromRules(ID, input.toString(), Transliterator.FORWARD);
1317         } catch (IOException e) {
1318             throw new ICUUncheckedIOException("Can't open transliterator file " + file, e);
1319         }
1320     }
1321 
getPhase()1322     public Phase getPhase() {
1323         return phase;
1324     }
1325 
setPhase(Phase phase)1326     public void setPhase(Phase phase) {
1327         this.phase = phase;
1328     }
1329 
1330     /**
1331      * A map of error/warning types to their filters.
1332      */
1333     private static List<R3<Pattern, Subtype, Pattern>> allFilters;
1334 
1335     /**
1336      * Loads the set of filters used for CheckCLDR results.
1337      */
loadFilters()1338     private void loadFilters() {
1339         if (allFilters != null) return;
1340         allFilters = new ArrayList<R3<Pattern, Subtype, Pattern>>();
1341         RegexFileParser fileParser = new RegexFileParser();
1342         fileParser.setLineParser(new RegexLineParser() {
1343             @Override
1344             public void parse(String line) {
1345                 String[] fields = line.split("\\s*;\\s*");
1346                 Subtype subtype = Subtype.valueOf(fields[0]);
1347                 Pattern locale = PatternCache.get(fields[1]);
1348                 Pattern xpathRegex = PatternCache.get(fields[2].replaceAll("\\[@", "\\\\[@"));
1349                 allFilters.add(new R3<Pattern, Subtype, Pattern>(locale, subtype, xpathRegex));
1350             }
1351         });
1352         fileParser.parse(CheckCLDR.class, "/org/unicode/cldr/util/data/CheckCLDR-exceptions.txt");
1353     }
1354 
1355     /**
1356      * Checks if a status should be excluded from the list of results returned
1357      * from CheckCLDR.
1358      * @param xpath the xpath that the status belongs to
1359      * @param status the status
1360      * @return true if the status should be included
1361      */
shouldExcludeStatus(String xpath, CheckStatus status)1362     private boolean shouldExcludeStatus(String xpath, CheckStatus status) {
1363         List<Pattern> xpathPatterns = filtersForLocale.get(status.getSubtype());
1364         if (xpathPatterns == null) {
1365             return false;
1366         }
1367         for (Pattern xpathPattern : xpathPatterns) {
1368             if (xpathPattern.matcher(xpath).matches()) {
1369                 return true;
1370             }
1371         }
1372         return false;
1373     }
1374 
getEnglishFile()1375     public CLDRFile getEnglishFile() {
1376         return englishFile;
1377     }
1378 
setEnglishFile(CLDRFile englishFile)1379     public void setEnglishFile(CLDRFile englishFile) {
1380         this.englishFile = englishFile;
1381     }
1382 }
1383