| /* |
| ****************************************************************************** |
| * Copyright (C) 2005-2014, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ****************************************************************************** |
| */ |
| |
| package org.unicode.cldr.test; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.text.ParsePosition; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype; |
| import org.unicode.cldr.util.CLDRFile; |
| import org.unicode.cldr.util.CLDRInfo.CandidateInfo; |
| import org.unicode.cldr.util.CLDRInfo.PathValueInfo; |
| import org.unicode.cldr.util.CLDRInfo.UserInfo; |
| import org.unicode.cldr.util.CLDRLocale; |
| import org.unicode.cldr.util.CldrUtility; |
| import org.unicode.cldr.util.Factory; |
| import org.unicode.cldr.util.InternalCldrException; |
| import org.unicode.cldr.util.Level; |
| import org.unicode.cldr.util.PathHeader; |
| import org.unicode.cldr.util.PathHeader.SurveyToolStatus; |
| import org.unicode.cldr.util.PatternCache; |
| import org.unicode.cldr.util.RegexFileParser; |
| import org.unicode.cldr.util.RegexFileParser.RegexLineParser; |
| import org.unicode.cldr.util.StandardCodes; |
| import org.unicode.cldr.util.TransliteratorUtilities; |
| import org.unicode.cldr.util.VoteResolver; |
| |
| import com.ibm.icu.dev.util.ElapsedTimer; |
| import com.ibm.icu.impl.Row.R3; |
| import com.ibm.icu.text.ListFormatter; |
| import com.ibm.icu.text.MessageFormat; |
| import com.ibm.icu.text.Transliterator; |
| import com.ibm.icu.util.ICUUncheckedIOException; |
| |
| /** |
| * This class provides a foundation for both console-driven CLDR tests, and |
| * Survey Tool Tests. |
| * <p> |
| * To add a test, subclass CLDRFile and override handleCheck and possibly setCldrFileToCheck. Then put the test into |
| * getCheckAll. |
| * <p> |
| * To use the test, take a look at the main in ConsoleCheckCLDR. Note that you need to call setDisplayInformation with |
| * the CLDRFile for the locale that you want the display information (eg names for codes) to be in.<br> |
| * Some options are passed in the Map options. Examples: boolean SHOW_TIMES = options.containsKey("SHOW_TIMES"); // for |
| * printing times for doing setCldrFileToCheck. |
| * <p> |
| * Some errors/warnings will be explicitly filtered out when calling CheckCLDR's check() method. |
| * The full list of filters can be found in org/unicode/cldr/util/data/CheckCLDR-exceptions.txt. |
| * |
| * @author davis |
| */ |
| abstract public class CheckCLDR { |
| private static CLDRFile displayInformation; |
| |
| private CLDRFile cldrFileToCheck; |
| private CLDRFile englishFile = null; |
| |
| private boolean skipTest = false; |
| private Phase phase; |
| private Map<Subtype, List<Pattern>> filtersForLocale = new HashMap<Subtype, List<Pattern>>(); |
| |
| public enum InputMethod { |
| DIRECT, BULK |
| } |
| |
| public enum StatusAction { |
| /** |
| * Allow voting and add new values (in Change column). |
| */ |
| ALLOW, |
| /** |
| * Allow voting and ticket (in Change column). |
| */ |
| ALLOW_VOTING_AND_TICKET, |
| /** |
| * Allow voting but no add new values (in Change column). |
| */ |
| ALLOW_VOTING_BUT_NO_ADD, |
| /** |
| * Only allow filing a ticket. |
| */ |
| ALLOW_TICKET_ONLY, |
| /** |
| * Disallow (for various reasons) |
| */ |
| FORBID_ERRORS(true), |
| FORBID_READONLY(true), |
| FORBID_UNLESS_DATA_SUBMISSION(true), |
| FORBID_COVERAGE(true), |
| FORBID_NEEDS_TICKET(true); |
| |
| private final boolean isForbidden; |
| |
| private StatusAction() { |
| isForbidden = false; |
| }; |
| |
| private StatusAction(boolean isForbidden) { |
| this.isForbidden = isForbidden; |
| }; |
| |
| public boolean isForbidden() { |
| return isForbidden; |
| } |
| |
| public boolean canShow() { |
| return !isForbidden; |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public static final StatusAction FORBID = FORBID_READONLY; |
| /** |
| * @deprecated |
| */ |
| public static final StatusAction SHOW_VOTING_AND_ADD = ALLOW; |
| /** |
| * @deprecated |
| */ |
| public static final StatusAction SHOW_VOTING_AND_TICKET = ALLOW_VOTING_AND_TICKET; |
| /** |
| * @deprecated |
| */ |
| public static final StatusAction SHOW_VOTING_BUT_NO_ADD = ALLOW_VOTING_BUT_NO_ADD; |
| /** |
| * @deprecated |
| */ |
| public static final StatusAction FORBID_HAS_ERROR = FORBID_ERRORS; |
| } |
| |
| private static final HashMap<String, Phase> PHASE_NAMES = new HashMap<String, Phase>(); |
| |
| public enum Phase { |
| BUILD, SUBMISSION, VETTING, FINAL_TESTING("RESOLUTION"); |
| Phase(String... alternateName) { |
| for (String name : alternateName) { |
| PHASE_NAMES.put(name.toUpperCase(Locale.ENGLISH), this); |
| } |
| } |
| |
| public static Phase forString(String value) { |
| if (value == null) { |
| return org.unicode.cldr.util.CLDRConfig.getInstance().getPhase(); |
| } |
| value = value.toUpperCase(Locale.ENGLISH); |
| Phase result = PHASE_NAMES.get(value); |
| return result != null ? result |
| : Phase.valueOf(value); |
| } |
| |
| /** |
| * Return whether or not to show a row, and if so, how. |
| * |
| * @param pathValueInfo |
| * @param inputMethod |
| * @param status |
| * @param userInfo |
| * null if there is no userInfo (nobody logged in). |
| * @return |
| */ |
| public StatusAction getShowRowAction( |
| PathValueInfo pathValueInfo, |
| InputMethod inputMethod, |
| PathHeader.SurveyToolStatus status, |
| UserInfo userInfo // can get voterInfo from this. |
| ) { |
| |
| // always forbid deprecated items - don't show. |
| if (status == SurveyToolStatus.DEPRECATED) { |
| return StatusAction.FORBID_READONLY; |
| } |
| |
| if (status == SurveyToolStatus.READ_ONLY) { |
| return StatusAction.ALLOW_TICKET_ONLY; |
| } |
| |
| // always forbid bulk import except in data submission. |
| if (inputMethod == InputMethod.BULK && this != Phase.SUBMISSION) { |
| return StatusAction.FORBID_UNLESS_DATA_SUBMISSION; |
| } |
| |
| // if TC+, allow anything else, even suppressed items and errors |
| if (userInfo != null && userInfo.getVoterInfo().getLevel().compareTo(VoteResolver.Level.tc) >= 0) { |
| return StatusAction.ALLOW; |
| } |
| |
| // if the coverage level is optional, disallow everything |
| if (pathValueInfo.getCoverageLevel().compareTo(Level.COMPREHENSIVE) > 0) { |
| return StatusAction.FORBID_COVERAGE; |
| } |
| |
| if (status == SurveyToolStatus.HIDE) { |
| return StatusAction.FORBID_READONLY; |
| } |
| |
| if (this == Phase.SUBMISSION) { |
| return (status == SurveyToolStatus.READ_WRITE || status == SurveyToolStatus.LTR_ALWAYS) |
| ? StatusAction.ALLOW |
| : StatusAction.ALLOW_VOTING_AND_TICKET; |
| } |
| |
| // We are not in submission. |
| // Only allow ADD if we have an error or warning |
| ValueStatus valueStatus = ValueStatus.NONE; |
| CandidateInfo winner = pathValueInfo.getCurrentItem(); |
| // Only check winning value for errors/warnings per ticket #8677 |
| // We used to check all candidates. |
| // for (CandidateInfo value : pathValueInfo.getValues()) { |
| valueStatus = getValueStatus(winner, valueStatus); |
| if (valueStatus != ValueStatus.NONE) { |
| return (status == SurveyToolStatus.READ_WRITE || status == SurveyToolStatus.LTR_ALWAYS) |
| ? StatusAction.ALLOW |
| : StatusAction.ALLOW_VOTING_AND_TICKET; |
| } |
| // } |
| |
| // No warnings, so allow just voting. |
| return StatusAction.ALLOW_VOTING_BUT_NO_ADD; |
| } |
| |
| /** |
| * getAcceptNewItemAction. MUST only be called if getShowRowAction(...).canShow() |
| * TODO Consider moving Phase, StatusAction, etc into CLDRInfo. |
| * |
| * @param enteredValue |
| * If null, means an abstention. |
| * If voting for an existing value, pathValueInfo.getValues().contains(enteredValue) MUST be true |
| * @param pathValueInfo |
| * @param inputMethod |
| * @param status |
| * @param userInfo |
| * @return |
| */ |
| public StatusAction getAcceptNewItemAction( |
| CandidateInfo enteredValue, |
| PathValueInfo pathValueInfo, |
| InputMethod inputMethod, |
| PathHeader.SurveyToolStatus status, |
| UserInfo userInfo // can get voterInfo from this. |
| ) { |
| if (status != SurveyToolStatus.READ_WRITE && status != SurveyToolStatus.LTR_ALWAYS) { |
| return StatusAction.FORBID_READONLY; // not writable. |
| } |
| |
| // only logged in users can add items. |
| if (userInfo == null) { |
| return StatusAction.FORBID_ERRORS; |
| } |
| |
| // we can always abstain |
| if (enteredValue == null) { |
| return StatusAction.ALLOW; |
| } |
| |
| // if TC+, allow anything else, even suppressed items and errors |
| if (userInfo.getVoterInfo().getLevel().compareTo(VoteResolver.Level.tc) >= 0) { |
| return StatusAction.ALLOW; |
| } |
| |
| // Disallow errors. |
| ValueStatus valueStatus = getValueStatus(enteredValue, ValueStatus.NONE); |
| if (valueStatus == ValueStatus.ERROR) { |
| return StatusAction.FORBID_ERRORS; |
| } |
| |
| // Allow items if submission |
| if (this == Phase.SUBMISSION) { |
| return StatusAction.ALLOW; |
| } |
| |
| // Voting for an existing value is ok |
| valueStatus = ValueStatus.NONE; |
| for (CandidateInfo value : pathValueInfo.getValues()) { |
| if (value == enteredValue) { |
| return StatusAction.ALLOW; |
| } |
| valueStatus = getValueStatus(value, valueStatus); |
| } |
| |
| // If there were any errors/warnings on other values, allow |
| if (valueStatus != ValueStatus.NONE) { |
| return StatusAction.ALLOW; |
| } |
| |
| // Otherwise (we are vetting, but with no errors or warnings) |
| // DISALLOW NEW STUFF |
| |
| return StatusAction.FORBID_UNLESS_DATA_SUBMISSION; |
| } |
| |
| enum ValueStatus { |
| ERROR, WARNING, NONE |
| } |
| |
| private ValueStatus getValueStatus(CandidateInfo value, ValueStatus previous) { |
| if (previous == ValueStatus.ERROR || value == null) { |
| return previous; |
| } |
| |
| for (CheckStatus item : value.getCheckStatusList()) { |
| CheckStatus.Type type = item.getType(); |
| if (type.equals(CheckStatus.Type.Error)) { |
| if (CheckStatus.crossCheckSubtypes.contains(item.getSubtype())) { |
| return ValueStatus.WARNING; |
| } else { |
| return ValueStatus.ERROR; |
| } |
| } else if (type.equals(CheckStatus.Type.Warning)) { |
| previous = ValueStatus.WARNING; |
| } |
| } |
| return previous; |
| } |
| } |
| |
| public static final class Options implements Comparable<Options> { |
| |
| public enum Option { |
| locale, |
| CoverageLevel_requiredLevel("CoverageLevel.requiredLevel"), |
| CoverageLevel_localeType("CoverageLevel.localeType"), |
| SHOW_TIMES, |
| phase, |
| lgWarningCheck, |
| CheckCoverage_skip("CheckCoverage.skip"), |
| exemplarErrors |
| ; |
| |
| private String key; |
| |
| public String getKey() { |
| return key; |
| } |
| |
| Option(String key) { |
| this.key = key; |
| } |
| |
| Option() { |
| this.key = name(); |
| } |
| }; |
| |
| private static StandardCodes sc = StandardCodes.make(); |
| |
| private final boolean DEBUG_OPTS = false; |
| |
| String options[] = new String[Option.values().length]; |
| CLDRLocale locale = null; |
| |
| private final String key; // for fast compare |
| |
| /** |
| * Adopt some other map |
| * @param fromOptions |
| */ |
| public Options(Map<String, String> fromOptions) { |
| clear(); |
| setAll(fromOptions); |
| key = null; // no key = slow compare |
| } |
| |
| private void setAll(Map<String, String> fromOptions) { |
| for (Map.Entry<String, String> e : fromOptions.entrySet()) { |
| set(e.getKey(), e.getValue()); |
| } |
| } |
| |
| /** |
| * @param key |
| * @param value |
| */ |
| public void set(String key, String value) { |
| // TODO- cache the map |
| for (Option o : Option.values()) { |
| if (o.getKey().equals(key)) { |
| set(o, value); |
| return; |
| } |
| } |
| throw new IllegalArgumentException("Unknown CLDR option: '" + key + "' - valid keys are: " + Options.getValidKeys()); |
| } |
| |
| private static String getValidKeys() { |
| Set<String> allkeys = new TreeSet<String>(); |
| for (Option o : Option.values()) { |
| allkeys.add(o.getKey()); |
| } |
| return ListFormatter.getInstance().format(allkeys); |
| } |
| |
| public Options() { |
| clear(); |
| key = "".intern(); // null Options. |
| } |
| |
| /** |
| * Deep clone |
| * @param options2 |
| */ |
| public Options(Options options2) { |
| this.options = Arrays.copyOf(options2.options, options2.options.length); |
| this.key = options2.key; |
| } |
| |
| public Options(CLDRLocale locale, CheckCLDR.Phase testPhase, String requiredLevel, String localeType) { |
| this.locale = locale; |
| options = new String[Option.values().length]; |
| StringBuilder sb = new StringBuilder(); |
| set(Option.locale, locale.getBaseName()); |
| sb.append(locale.getBaseName()).append('/'); |
| set(Option.CoverageLevel_requiredLevel, requiredLevel); |
| sb.append(requiredLevel).append('/'); |
| set(Option.CoverageLevel_localeType, localeType); |
| sb.append(localeType).append('/'); |
| set(Option.phase, testPhase.name().toLowerCase()); |
| sb.append(localeType).append('/'); |
| key = sb.toString().intern(); |
| } |
| |
| @Override |
| public Options clone() { |
| return new Options(this); |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) return true; |
| if (!(other instanceof Options)) return false; |
| if (this.key != null && ((Options) other).key != null) { |
| return (this.key == ((Options) other).key); |
| } else { |
| return this.compareTo((Options) other) == 0; |
| } |
| } |
| |
| private Options clear(Option o) { |
| set(o, null); |
| return this; |
| } |
| |
| private Options clear() { |
| for (int i = 0; i < options.length; i++) { |
| options[i] = null; |
| } |
| return this; |
| } |
| |
| private Options set(Option o, String v) { |
| options[o.ordinal()] = v; |
| if (DEBUG_OPTS) System.err.println("Setting " + o + " = " + v); |
| return this; |
| } |
| |
| public String get(Option o) { |
| final String v = options[o.ordinal()]; |
| if (DEBUG_OPTS) System.err.println("Getting " + o + " = " + v); |
| return v; |
| } |
| |
| public CLDRLocale getLocale() { |
| if (locale != null) return locale; |
| return CLDRLocale.getInstance(get(Option.locale)); |
| } |
| |
| public Level getRequiredLevel(String localeID) { |
| Level result; |
| // see if there is an explicit level |
| String localeType = get(Option.CoverageLevel_requiredLevel); |
| if (localeType != null) { |
| result = Level.get(localeType); |
| if (result != Level.UNDETERMINED) { |
| return result; |
| } |
| } |
| // otherwise, see if there is an organization level |
| return sc.getLocaleCoverageLevel("Cldr", localeID); |
| } |
| |
| public boolean contains(Option o) { |
| String s = get(o); |
| return (s != null && !s.isEmpty()); |
| } |
| |
| @Override |
| public int compareTo(Options other) { |
| if (other == this) return 0; |
| if (key != null && other.key != null) { |
| if (key == other.key) return 0; |
| return key.compareTo(other.key); |
| } |
| for (int i = 0; i < options.length; i++) { |
| final String s1 = options[i]; |
| final String s2 = other.options[i]; |
| if (s1 == null && s2 == null) { |
| // no difference |
| } else if (s1 == null) { |
| return -1; |
| } else if (s2 == null) { |
| return 1; |
| } else { |
| int rv = s1.compareTo(s2); |
| if (rv != 0) { |
| return rv; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| @Override |
| public int hashCode() { |
| if (key != null) return key.hashCode(); |
| |
| int h = 1; |
| for (int i = 0; i < options.length; i++) { |
| if (options[i] == null) { |
| h *= 11; |
| } else { |
| h = (h * 11) + options[i].hashCode(); |
| } |
| } |
| return h; |
| } |
| |
| @Override |
| public String toString() { |
| if (key != null) return "Options:" + key; |
| StringBuilder sb = new StringBuilder(); |
| for (Option o : Option.values()) { |
| if (options[o.ordinal()] != null) { |
| sb.append(o) |
| .append('=') |
| .append(options[o.ordinal()]) |
| .append(' '); |
| } |
| } |
| return sb.toString(); |
| } |
| }; |
| |
| public boolean isSkipTest() { |
| return skipTest; |
| } |
| |
| // this should only be set for the test in setCldrFileToCheck |
| public void setSkipTest(boolean skipTest) { |
| this.skipTest = skipTest; |
| } |
| |
| /** |
| * Here is where the list of all checks is found. |
| * |
| * @param nameMatcher |
| * Regex pattern that determines which checks are run, |
| * based on their class name (such as .* for all checks, .*Collisions.* for CheckDisplayCollisions, etc.) |
| * @return |
| */ |
| public static CompoundCheckCLDR getCheckAll(Factory factory, String nameMatcher) { |
| return new CompoundCheckCLDR() |
| .setFilter(Pattern.compile(nameMatcher, Pattern.CASE_INSENSITIVE).matcher("")) |
| //.add(new CheckAttributeValues(factory)) |
| .add(new CheckChildren(factory)) |
| .add(new CheckCoverage(factory)) |
| .add(new CheckDates(factory)) |
| .add(new CheckForCopy(factory)) |
| .add(new CheckDisplayCollisions(factory)) |
| .add(new CheckExemplars(factory)) |
| .add(new CheckForExemplars(factory)) |
| .add(new CheckForInheritanceMarkers()) |
| .add(new CheckNames()) |
| .add(new CheckNumbers(factory)) |
| // .add(new CheckZones()) // this doesn't work; many spurious errors that user can't correct |
| .add(new CheckMetazones()) |
| .add(new CheckLogicalGroupings(factory)) |
| .add(new CheckAlt()) |
| .add(new CheckCurrencies()) |
| .add(new CheckCasing()) |
| .add(new CheckConsistentCasing(factory)) // this doesn't work; many spurious errors that user can't correct |
| .add(new CheckQuotes()) |
| .add(new CheckUnits()) |
| .add(new CheckWidths()) |
| .add(new CheckPlaceHolders()) |
| .add(new CheckNew(factory)) // this is at the end; it will check for other certain other errors and warnings and |
| // not add a message if there are any. |
| ; |
| } |
| |
| /** |
| * These determine what language is used to display information. Must be set before use. |
| * |
| * @param locale |
| * @return |
| */ |
| public static synchronized CLDRFile getDisplayInformation() { |
| return displayInformation; |
| } |
| |
| public static synchronized void setDisplayInformation(CLDRFile inputDisplayInformation) { |
| displayInformation = inputDisplayInformation; |
| } |
| |
| /** |
| * [Warnings - please zoom in] dates/timeZoneNames/singleCountries |
| * (empty) |
| * [refs][hide] Ref: [Zoom...] |
| * [Warnings - please zoom in] dates/timeZoneNames/hours {0}/{1} {0}/{1} |
| * [refs][hide] Ref: [Zoom...] |
| * [Warnings - please zoom in] dates/timeZoneNames/hour +HH:mm;-HH:mm |
| * +HH:mm;-HH:mm |
| * [refs][hide] Ref: [Zoom...] |
| * [ok] layout/orientation (empty) |
| * [refs][hide] Ref: [Zoom...] |
| * [ok] dates/localizedPatternChars GyMdkHmsSEDFwWahKzYeugAZvcL |
| * GaMjkHmsSEDFwWxhKzAeugXZvcL |
| * [refs][hide] Ref: [Zoom...] |
| */ |
| |
| /** |
| * Get the CLDRFile. |
| * |
| * @param cldrFileToCheck |
| */ |
| public final CLDRFile getCldrFileToCheck() { |
| return cldrFileToCheck; |
| } |
| |
| /** |
| * Don't override this, use the other setCldrFileToCheck which takes an Options instead of a Map<> |
| * @param cldrFileToCheck |
| * @param options |
| * @param possibleErrors |
| * @return |
| * @see #setCldrFileToCheck(CLDRFile, Options, List) |
| */ |
| final public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Map<String, String> options, |
| List<CheckStatus> possibleErrors) { |
| return setCldrFileToCheck(cldrFileToCheck, new Options(options), possibleErrors); |
| } |
| |
| /** |
| * Set the CLDRFile. Must be done before calling check. If null is called, just skip |
| * Often subclassed for initializing. If so, make the first 2 lines: |
| * if (cldrFileToCheck == null) return this; |
| * super.setCldrFileToCheck(cldrFileToCheck); |
| * do stuff |
| * |
| * @param cldrFileToCheck |
| * @param options (not currently used) |
| * @param possibleErrors |
| * TODO |
| */ |
| public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options, |
| List<CheckStatus> possibleErrors) { |
| this.cldrFileToCheck = cldrFileToCheck; |
| |
| // Shortlist error filters for this locale. |
| loadFilters(); |
| String locale = cldrFileToCheck.getLocaleID(); |
| filtersForLocale.clear(); |
| for (R3<Pattern, Subtype, Pattern> filter : allFilters) { |
| if (filter.get0() == null || !filter.get0().matcher(locale).matches()) continue; |
| Subtype subtype = filter.get1(); |
| List<Pattern> xpaths = filtersForLocale.get(subtype); |
| if (xpaths == null) { |
| filtersForLocale.put(subtype, xpaths = new ArrayList<Pattern>()); |
| } |
| xpaths.add(filter.get2()); |
| } |
| return this; |
| } |
| |
| /** |
| * Status value returned from check |
| */ |
| public static class CheckStatus { |
| public static final Type |
| alertType = Type.Comment, |
| warningType = Type.Warning, |
| errorType = Type.Error, |
| exampleType = Type.Example, |
| demoType = Type.Demo; |
| |
| public enum Type { |
| Comment, Warning, Error, Example, Demo |
| }; |
| |
| public enum Subtype { |
| none, noUnproposedVariant, deprecatedAttribute, illegalPlural, invalidLocale, |
| incorrectCasing, valueAlwaysOverridden, nullChildFile, internalError, coverageLevel, |
| missingPluralInfo, currencySymbolTooWide, incorrectDatePattern, abbreviatedDateFieldTooWide, |
| displayCollision, illegalExemplarSet, missingAuxiliaryExemplars, extraPlaceholders, missingPlaceholders, shouldntHavePlaceholders, |
| couldNotAccessExemplars, noExemplarCharacters, modifiedEnglishValue, invalidCurrencyMatchSet, |
| multipleMetazoneMappings, noMetazoneMapping, noMetazoneMappingAfter1970, noMetazoneMappingBeforeNow, |
| cannotCreateZoneFormatter, insufficientCoverage, missingLanguageTerritoryInfo, missingEuroCountryInfo, |
| deprecatedAttributeWithReplacement, missingOrExtraDateField, internalUnicodeSetFormattingError, |
| auxiliaryExemplarsOverlap, missingPunctuationCharacters, |
| |
| charactersNotInCurrencyExemplars, asciiCharactersNotInCurrencyExemplars, |
| charactersNotInMainOrAuxiliaryExemplars, asciiCharactersNotInMainOrAuxiliaryExemplars, |
| |
| narrowDateFieldTooWide, illegalCharactersInExemplars, orientationDisagreesWithExemplars, |
| inconsistentDatePattern, inconsistentTimePattern, missingDatePattern, |
| illegalDatePattern, missingMainExemplars, |
| mustNotStartOrEndWithSpace, |
| illegalCharactersInNumberPattern, numberPatternNotCanonical, currencyPatternMissingCurrencySymbol, missingMinusSign, |
| badNumericType, |
| percentPatternMissingPercentSymbol, illegalNumberFormat, unexpectedAttributeValue, metazoneContainsDigit, |
| tooManyGroupingSeparators, inconsistentPluralFormat, |
| sameAsEnglishOrCode, dateSymbolCollision, incompleteLogicalGroup, extraMetazoneString, inconsistentDraftStatus, |
| errorOrWarningInLogicalGroup, |
| valueTooWide, |
| valueTooNarrow, |
| nameContainsYear, |
| patternCannotContainDigits, patternContainsInvalidCharacters, parenthesesNotAllowed, |
| illegalNumberingSystem, |
| unexpectedOrderOfEraYear, |
| invalidPlaceHolder, |
| asciiQuotesNotAllowed, |
| badMinimumGroupingDigits, |
| inconsistentPeriods, |
| inheritanceMarkerNotAllowed, |
| invalidDurationUnitPattern, |
| invalidDelimiter, |
| illegalCharactersInPattern, |
| badParseLenient; |
| |
| public String toString() { |
| return TO_STRING.matcher(name()).replaceAll(" $1").toLowerCase(); |
| } |
| |
| static Pattern TO_STRING = PatternCache.get("([A-Z])"); |
| }; |
| |
| public static EnumSet<Subtype> crossCheckSubtypes = |
| EnumSet.of( |
| Subtype.dateSymbolCollision, |
| Subtype.displayCollision, |
| Subtype.inconsistentDraftStatus, |
| Subtype.incompleteLogicalGroup, |
| Subtype.inconsistentPeriods, |
| Subtype.abbreviatedDateFieldTooWide, |
| Subtype.narrowDateFieldTooWide, |
| Subtype.coverageLevel); |
| |
| private Type type; |
| private Subtype subtype = Subtype.none; |
| private String messageFormat; |
| private Object[] parameters; |
| private String htmlMessage; |
| private CheckCLDR cause; |
| private boolean checkOnSubmit = true; |
| |
| public CheckStatus() { |
| |
| } |
| |
| public boolean isCheckOnSubmit() { |
| return checkOnSubmit; |
| } |
| |
| public CheckStatus setCheckOnSubmit(boolean dependent) { |
| this.checkOnSubmit = dependent; |
| return this; |
| } |
| |
| public Type getType() { |
| return type; |
| } |
| |
| public CheckStatus setMainType(CheckStatus.Type type) { |
| this.type = type; |
| return this; |
| } |
| |
| public String getMessage() { |
| String message = messageFormat; |
| if (messageFormat != null && parameters != null) { |
| try { |
| String fixedApos = MessageFormat.autoQuoteApostrophe(messageFormat); |
| MessageFormat format = new MessageFormat(fixedApos); |
| message = format.format(parameters); |
| } catch (Exception e) { |
| message = messageFormat; |
| System.err.println("MessageFormat Failure: " + subtype + "; " + messageFormat + "; " |
| + (parameters == null ? null : Arrays.asList(parameters))); |
| // throw new IllegalArgumentException(subtype + "; " + messageFormat + "; " |
| // + (parameters == null ? null : Arrays.asList(parameters)), e); |
| } |
| } |
| Exception[] exceptionParameters = getExceptionParameters(); |
| if (exceptionParameters != null) { |
| for (Exception exception : exceptionParameters) { |
| message += "; " + exception.getMessage(); // + " \t(" + exception.getClass().getName() + ")"; |
| // for (StackTraceElement item : exception.getStackTrace()) { |
| // message += "\n\t" + item; |
| // } |
| } |
| } |
| return message.replace('\t', ' '); |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public String getHTMLMessage() { |
| return htmlMessage; |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public CheckStatus setHTMLMessage(String message) { |
| htmlMessage = message; |
| return this; |
| } |
| |
| public CheckStatus setMessage(String message) { |
| if (cause == null) { |
| throw new IllegalArgumentException("Must have cause set."); |
| } |
| this.messageFormat = message; |
| this.parameters = null; |
| return this; |
| } |
| |
| public CheckStatus setMessage(String message, Object... messageArguments) { |
| if (cause == null) { |
| throw new IllegalArgumentException("Must have cause set."); |
| } |
| this.messageFormat = message; |
| this.parameters = messageArguments; |
| return this; |
| } |
| |
| public String toString() { |
| return getType() + ": " + getMessage(); |
| } |
| |
| /** |
| * Warning: don't change the contents of the parameters after retrieving. |
| */ |
| public Object[] getParameters() { |
| return parameters; |
| } |
| |
| /** |
| * Returns any Exception parameters in the status, or null if there are none. |
| * |
| * @return |
| */ |
| public Exception[] getExceptionParameters() { |
| if (parameters == null) { |
| return null; |
| } |
| |
| List<Exception> errors = new ArrayList<Exception>(); |
| for (Object o : parameters) { |
| if (o instanceof Exception) { |
| errors.add((Exception) o); |
| } |
| } |
| if (errors.size() == 0) { |
| return null; |
| } |
| return errors.toArray(new Exception[errors.size()]); |
| } |
| |
| /** |
| * Warning: don't change the contents of the parameters after passing in. |
| */ |
| public CheckStatus setParameters(Object[] parameters) { |
| if (cause == null) { |
| throw new IllegalArgumentException("Must have cause set."); |
| } |
| this.parameters = parameters; |
| return this; |
| } |
| |
| public SimpleDemo getDemo() { |
| return null; |
| } |
| |
| public CheckCLDR getCause() { |
| return cause; |
| } |
| |
| public CheckStatus setCause(CheckCLDR cause) { |
| this.cause = cause; |
| return this; |
| } |
| |
| public Subtype getSubtype() { |
| return subtype; |
| } |
| |
| public CheckStatus setSubtype(Subtype subtype) { |
| this.subtype = subtype; |
| return this; |
| } |
| |
| /** |
| * Convenience function: return true if any items in this list are of errorType |
| * |
| * @param result |
| * the list to check (could be null for empty) |
| * @return true if any items in result are of errorType |
| */ |
| public static final boolean hasError(List<CheckStatus> result) { |
| return hasType(result, errorType); |
| } |
| |
| /** |
| * Convenience function: return true if any items in this list are of errorType |
| * |
| * @param result |
| * the list to check (could be null for empty) |
| * @return true if any items in result are of errorType |
| */ |
| public static boolean hasType(List<CheckStatus> result, Type type) { |
| if (result == null) return false; |
| for (CheckStatus s : result) { |
| if (s.getType().equals(type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| public static abstract class SimpleDemo { |
| Map<String, String> internalPostArguments = new HashMap<String, String>(); |
| |
| /** |
| * @param postArguments |
| * A read-write map containing post-style arguments. eg TEXTBOX=abcd, etc. <br> |
| * The first time this is called, the Map should be empty. |
| * @return true if the map has been changed |
| */ |
| public abstract String getHTML(Map<String, String> postArguments) throws Exception; |
| |
| /** |
| * Only here for compatibiltiy. Use the other getHTML instead |
| */ |
| public final String getHTML(String path, String fullPath, String value) throws Exception { |
| return getHTML(internalPostArguments); |
| } |
| |
| /** |
| * THIS IS ONLY FOR COMPATIBILITY: you can call this, then the non-postArguments form of getHTML; or better, |
| * call |
| * getHTML with the postArguments. |
| * |
| * @param postArguments |
| * A read-write map containing post-style arguments. eg TEXTBOX=abcd, etc. |
| * @return true if the map has been changed |
| */ |
| public final boolean processPost(Map<String, String> postArguments) { |
| internalPostArguments.clear(); |
| internalPostArguments.putAll(postArguments); |
| return true; |
| } |
| // /** |
| // * Utility for setting map. Use the paradigm in CheckNumbers. |
| // */ |
| // public boolean putIfDifferent(Map inout, String key, String value) { |
| // Object oldValue = inout.put(key, value); |
| // return !value.equals(oldValue); |
| // } |
| } |
| |
| public static abstract class FormatDemo extends SimpleDemo { |
| protected String currentPattern, currentInput, currentFormatted, currentReparsed; |
| protected ParsePosition parsePosition = new ParsePosition(0); |
| |
| protected abstract String getPattern(); |
| |
| protected abstract String getSampleInput(); |
| |
| protected abstract void getArguments(Map<String, String> postArguments); |
| |
| public String getHTML(Map<String, String> postArguments) throws Exception { |
| getArguments(postArguments); |
| StringBuffer htmlMessage = new StringBuffer(); |
| FormatDemo.appendTitle(htmlMessage); |
| FormatDemo.appendLine(htmlMessage, currentPattern, currentInput, currentFormatted, currentReparsed); |
| htmlMessage.append("</table>"); |
| return htmlMessage.toString(); |
| } |
| |
| public String getPlainText(Map<String, String> postArguments) { |
| getArguments(postArguments); |
| return MessageFormat.format("<\"\u200E{0}\u200E\", \"{1}\"> \u2192 \"\u200E{2}\u200E\" \u2192 \"{3}\"", |
| (Object[]) new String[] { currentPattern, currentInput, currentFormatted, currentReparsed }); |
| } |
| |
| /** |
| * @param htmlMessage |
| * @param pattern |
| * @param input |
| * @param formatted |
| * @param reparsed |
| */ |
| public static void appendLine(StringBuffer htmlMessage, String pattern, String input, String formatted, |
| String reparsed) { |
| htmlMessage.append("<tr><td><input type='text' name='pattern' value='") |
| .append(TransliteratorUtilities.toXML.transliterate(pattern)) |
| .append("'></td><td><input type='text' name='input' value='") |
| .append(TransliteratorUtilities.toXML.transliterate(input)) |
| .append("'></td><td>") |
| .append("<input type='submit' value='Test' name='Test'>") |
| .append("</td><td>" + "<input type='text' name='formatted' value='") |
| .append(TransliteratorUtilities.toXML.transliterate(formatted)) |
| .append("'></td><td>" + "<input type='text' name='reparsed' value='") |
| .append(TransliteratorUtilities.toXML.transliterate(reparsed)) |
| .append("'></td></tr>"); |
| } |
| |
| /** |
| * @param htmlMessage |
| */ |
| public static void appendTitle(StringBuffer htmlMessage) { |
| htmlMessage.append("<table border='1' cellspacing='0' cellpadding='2'" + |
| // " style='border-collapse: collapse' style='width: 100%'" + |
| "><tr>" + |
| "<th>Pattern</th>" + |
| "<th>Unlocalized Input</th>" + |
| "<th></th>" + |
| "<th>Localized Format</th>" + |
| "<th>Re-Parsed</th>" + |
| "</tr>"); |
| } |
| } |
| |
| /** |
| * Wraps the options in an Options and delegates. |
| * @param path |
| * Must be a distinguished path, such as what comes out of CLDRFile.iterator() |
| * @param fullPath |
| * Must be the full path |
| * @param value |
| * the value associated with the path |
| * @param options |
| * A set of test-specific options. Set these with code of the form:<br> |
| * options.put("CoverageLevel.localeType", "G0")<br> |
| * That is, the key is of the form <testname>.<optiontype>, and the value is of the form <optionvalue>.<br> |
| * There is one general option; the following will select only the tests that should be run during this |
| * phase.<br> |
| * options.put("phase", Phase.<something>); |
| * It can be used for new data entry. |
| * @param result |
| * @return |
| * @deprecated use CheckCLDR#check(String, String, String, Options, List) |
| */ |
| @Deprecated |
| public final CheckCLDR check(String path, String fullPath, String value, Map<String, String> options, |
| List<CheckStatus> result) { |
| return check(path, fullPath, value, new Options(options), result); |
| } |
| |
| /** |
| * Checks the path/value in the cldrFileToCheck for correctness, according to some criterion. |
| * If the path is relevant to the check, there is an alert or warning, then a CheckStatus is added to List. |
| * |
| * @param path |
| * Must be a distinguished path, such as what comes out of CLDRFile.iterator() |
| * @param fullPath |
| * Must be the full path |
| * @param value |
| * the value associated with the path |
| * @param result |
| */ |
| public final CheckCLDR check(String path, String fullPath, String value, Options options, |
| List<CheckStatus> result) { |
| if (cldrFileToCheck == null) { |
| throw new InternalCldrException("CheckCLDR problem: cldrFileToCheck must not be null"); |
| } |
| if (path == null) { |
| throw new InternalCldrException("CheckCLDR problem: path must not be null"); |
| } |
| // if (fullPath == null) { |
| // throw new InternalError("CheckCLDR problem: fullPath must not be null"); |
| // } |
| // if (value == null) { |
| // throw new InternalError("CheckCLDR problem: value must not be null"); |
| // } |
| result.clear(); |
| // If the item is non-winning, and either inherited or it is code-fallback, then don't run |
| // any tests on this item. See http://unicode.org/cldr/trac/ticket/7574 |
| if (value == cldrFileToCheck.getBaileyValue(path, null, null) && value != cldrFileToCheck.getWinningValue(path)) { |
| return this; |
| } |
| // If we're being asked to run tests for an inheritance marker, then we need to change it |
| // to the "real" value first before running tests. Testing the value "↑↑↑" doesn't make sense. |
| if (CldrUtility.INHERITANCE_MARKER.equals(value)) { |
| value = cldrFileToCheck.getConstructedBaileyValue(path, null, null); |
| // If it hasn't changed, then don't run any tests. |
| if (CldrUtility.INHERITANCE_MARKER.equals(value)) { |
| return this; |
| } |
| } |
| CheckCLDR instance = handleCheck(path, fullPath, value, options, result); |
| Iterator<CheckStatus> iterator = result.iterator(); |
| // Filter out any errors/warnings that match the filter list in CheckCLDR-exceptions.txt. |
| while (iterator.hasNext()) { |
| CheckStatus status = iterator.next(); |
| if (shouldExcludeStatus(fullPath, status)) { |
| iterator.remove(); |
| } |
| } |
| return instance; |
| } |
| |
| /** |
| * @deprecated use {@link #getExamples(String, String, String, Options, List)} |
| * @param path |
| * @param fullPath |
| * @param value |
| * @param options |
| * @param result |
| * @return |
| */ |
| @Deprecated |
| public final CheckCLDR getExamples(String path, String fullPath, String value, Map<String, String> options, |
| List<CheckStatus> result) { |
| return getExamples(path, fullPath, value, new Options(options), result); |
| } |
| |
| /** |
| * Returns any examples in the result parameter. Both examples and demos can |
| * be returned. A demo will have getType() == CheckStatus.demoType. In that |
| * case, there will be no getMessage or getHTMLMessage available; instead, |
| * call getDemo() to get the demo, then call getHTML() to get the initial |
| * HTML. |
| */ |
| public final CheckCLDR getExamples(String path, String fullPath, String value, Options options, |
| List<CheckStatus> result) { |
| result.clear(); |
| return handleGetExamples(path, fullPath, value, options, result); |
| } |
| |
| protected CheckCLDR handleGetExamples(String path, String fullPath, String value, Options options2, |
| List<CheckStatus> result) { |
| return this; // NOOP unless overridden |
| } |
| |
| /** |
| * This is what the subclasses override. |
| * If they ever use pathParts or fullPathParts, they need to call initialize() with the respective |
| * path. Otherwise they must NOT change pathParts or fullPathParts. |
| * <p> |
| * If something is found, a CheckStatus is added to result. This can be done multiple times in one call, if multiple |
| * errors or warnings are found. The CheckStatus may return warnings, errors, examples, or demos. We may expand that |
| * in the future. |
| * <p> |
| * The code to add the CheckStatus will look something like:: |
| * |
| * <pre> |
| * result.add(new CheckStatus() |
| * .setType(CheckStatus.errorType) |
| * .setMessage("Value should be {0}", new Object[] { pattern })); |
| * </pre> |
| * |
| * @param options |
| * TODO |
| */ |
| abstract public CheckCLDR handleCheck(String path, String fullPath, String value, |
| Options options, List<CheckStatus> result); |
| |
| /** |
| * Only for use in ConsoleCheck, for debugging |
| */ |
| public void handleFinish() { |
| } |
| |
| /** |
| * Internal class used to bundle up a number of Checks. |
| * |
| * @author davis |
| * |
| */ |
| static class CompoundCheckCLDR extends CheckCLDR { |
| private Matcher filter; |
| private List<CheckCLDR> checkList = new ArrayList<CheckCLDR>(); |
| private List<CheckCLDR> filteredCheckList = new ArrayList<CheckCLDR>(); |
| |
| public CompoundCheckCLDR add(CheckCLDR item) { |
| checkList.add(item); |
| if (filter == null) { |
| filteredCheckList.add(item); |
| } else { |
| final String className = item.getClass().getName(); |
| if (filter.reset(className).find()) { |
| filteredCheckList.add(item); |
| } |
| } |
| return this; |
| } |
| |
| public CheckCLDR handleCheck(String path, String fullPath, String value, |
| Options options, List<CheckStatus> result) { |
| result.clear(); |
| // If we're being asked to run tests for an inheritance marker, then we need to change it |
| // to the "real" value first before running tests. Testing the value "↑↑↑" doesn't make sense. |
| if (CldrUtility.INHERITANCE_MARKER.equals(value)) { |
| value = getCldrFileToCheck().getConstructedBaileyValue(path, null, null); |
| } |
| for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) { |
| CheckCLDR item = it.next(); |
| // skip proposed items in final testing. |
| if (Phase.FINAL_TESTING == item.getPhase()) { |
| if (path.contains("proposed") && path.contains("[@alt=")) { |
| continue; |
| } |
| } |
| try { |
| if (!item.isSkipTest()) { |
| item.handleCheck(path, fullPath, value, options, result); |
| } |
| } catch (Exception e) { |
| addError(result, item, e); |
| return this; |
| } |
| } |
| return this; |
| } |
| |
| @Override |
| public void handleFinish() { |
| for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) { |
| CheckCLDR item = it.next(); |
| item.handleFinish(); |
| } |
| } |
| |
| protected CheckCLDR handleGetExamples(String path, String fullPath, String value, Options options, |
| List<CheckStatus> result) { |
| result.clear(); |
| for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) { |
| CheckCLDR item = it.next(); |
| try { |
| item.handleGetExamples(path, fullPath, value, options, result); |
| } catch (Exception e) { |
| addError(result, item, e); |
| return this; |
| } |
| } |
| return this; |
| } |
| |
| private void addError(List<CheckStatus> result, CheckCLDR item, Exception e) { |
| result.add(new CheckStatus() |
| .setCause(this) |
| .setMainType(CheckStatus.errorType) |
| .setSubtype(Subtype.internalError) |
| .setMessage("Internal error in {0}. Exception: {1}, Message: {2}, Trace: {3}", |
| new Object[] { item.getClass().getName(), e.getClass().getName(), e, |
| Arrays.asList(e.getStackTrace()) |
| })); |
| } |
| |
| public CheckCLDR setCldrFileToCheck(CLDRFile cldrFileToCheck, Options options, |
| List<CheckStatus> possibleErrors) { |
| ElapsedTimer testTime = null, testOverallTime = null; |
| if (cldrFileToCheck == null) return this; |
| boolean SHOW_TIMES = options.contains(Options.Option.SHOW_TIMES); |
| setPhase(Phase.forString(options.get(Options.Option.phase))); |
| if (SHOW_TIMES) testOverallTime = new ElapsedTimer("Test setup time for setCldrFileToCheck: {0}"); |
| super.setCldrFileToCheck(cldrFileToCheck, options, possibleErrors); |
| possibleErrors.clear(); |
| |
| for (Iterator<CheckCLDR> it = filteredCheckList.iterator(); it.hasNext();) { |
| CheckCLDR item = (CheckCLDR) it.next(); |
| if (SHOW_TIMES) |
| testTime = new ElapsedTimer("Test setup time for " + item.getClass().toString() + ": {0}"); |
| try { |
| item.setPhase(getPhase()); |
| item.setCldrFileToCheck(cldrFileToCheck, options, possibleErrors); |
| if (SHOW_TIMES) { |
| if (item.isSkipTest()) { |
| System.out.println("Disabled : " + testTime); |
| } else { |
| System.out.println("OK : " + testTime); |
| } |
| } |
| } catch (RuntimeException e) { |
| addError(possibleErrors, item, e); |
| if (SHOW_TIMES) System.out.println("ERR: " + testTime + " - " + e.toString()); |
| } |
| } |
| if (SHOW_TIMES) System.out.println("Overall: " + testOverallTime + ": {0}"); |
| return this; |
| } |
| |
| public Matcher getFilter() { |
| return filter; |
| } |
| |
| public CompoundCheckCLDR setFilter(Matcher filter) { |
| this.filter = filter; |
| filteredCheckList.clear(); |
| for (Iterator<CheckCLDR> it = checkList.iterator(); it.hasNext();) { |
| CheckCLDR item = it.next(); |
| if (filter == null || filter.reset(item.getClass().getName()).matches()) { |
| filteredCheckList.add(item); |
| item.setCldrFileToCheck(getCldrFileToCheck(), (Options) null, null); |
| } |
| } |
| return this; |
| } |
| |
| public String getFilteredTests() { |
| return filteredCheckList.toString(); |
| } |
| |
| public List<CheckCLDR> getFilteredTestList() { |
| return filteredCheckList; |
| } |
| } |
| |
| // static Transliterator prettyPath = getTransliteratorFromFile("ID", "prettyPath.txt"); |
| |
| public static Transliterator getTransliteratorFromFile(String ID, String file) { |
| try { |
| BufferedReader br = CldrUtility.getUTF8Data(file); |
| StringBuffer input = new StringBuffer(); |
| while (true) { |
| String line = br.readLine(); |
| if (line == null) break; |
| if (line.startsWith("\uFEFF")) line = line.substring(1); // remove BOM |
| input.append(line); |
| input.append('\n'); |
| } |
| return Transliterator.createFromRules(ID, input.toString(), Transliterator.FORWARD); |
| } catch (IOException e) { |
| throw new ICUUncheckedIOException("Can't open transliterator file " + file, e); |
| } |
| } |
| |
| public Phase getPhase() { |
| return phase; |
| } |
| |
| public void setPhase(Phase phase) { |
| this.phase = phase; |
| } |
| |
| /** |
| * A map of error/warning types to their filters. |
| */ |
| private static List<R3<Pattern, Subtype, Pattern>> allFilters; |
| |
| /** |
| * Loads the set of filters used for CheckCLDR results. |
| */ |
| private void loadFilters() { |
| if (allFilters != null) return; |
| allFilters = new ArrayList<R3<Pattern, Subtype, Pattern>>(); |
| RegexFileParser fileParser = new RegexFileParser(); |
| fileParser.setLineParser(new RegexLineParser() { |
| @Override |
| public void parse(String line) { |
| String[] fields = line.split("\\s*;\\s*"); |
| Subtype subtype = Subtype.valueOf(fields[0]); |
| Pattern locale = PatternCache.get(fields[1]); |
| Pattern xpathRegex = PatternCache.get(fields[2].replaceAll("\\[@", "\\\\[@")); |
| allFilters.add(new R3<Pattern, Subtype, Pattern>(locale, subtype, xpathRegex)); |
| } |
| }); |
| fileParser.parse(CheckCLDR.class, "/org/unicode/cldr/util/data/CheckCLDR-exceptions.txt"); |
| } |
| |
| /** |
| * Checks if a status should be excluded from the list of results returned |
| * from CheckCLDR. |
| * @param xpath the xpath that the status belongs to |
| * @param status the status |
| * @return true if the status should be included |
| */ |
| private boolean shouldExcludeStatus(String xpath, CheckStatus status) { |
| List<Pattern> xpathPatterns = filtersForLocale.get(status.getSubtype()); |
| if (xpathPatterns == null) return false; |
| for (Pattern xpathPattern : xpathPatterns) { |
| if (xpathPattern.matcher(xpath).matches()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public CLDRFile getEnglishFile() { |
| return englishFile; |
| } |
| |
| public void setEnglishFile(CLDRFile englishFile) { |
| this.englishFile = englishFile; |
| } |
| } |