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("Value should be {0}", 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