1 package org.unicode.cldr.util; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 import java.util.Arrays; 6 import java.util.Date; 7 import java.util.HashMap; 8 import java.util.HashSet; 9 import java.util.LinkedHashSet; 10 import java.util.List; 11 import java.util.Locale; 12 import java.util.Map; 13 import java.util.Map.Entry; 14 import java.util.Set; 15 import java.util.TreeMap; 16 import java.util.TreeSet; 17 import java.util.regex.Matcher; 18 19 import org.unicode.cldr.draft.FileUtilities; 20 import org.unicode.cldr.tool.Option; 21 import org.unicode.cldr.tool.Option.Options; 22 import org.unicode.cldr.tool.TablePrinter; 23 import org.unicode.cldr.util.SupplementalDataInfo.DateRange; 24 import org.unicode.cldr.util.SupplementalDataInfo.MetaZoneRange; 25 import org.unicode.cldr.util.TimezoneFormatter.Format; 26 27 import com.ibm.icu.impl.Row.R5; 28 import com.ibm.icu.text.MessageFormat; 29 import com.ibm.icu.text.SimpleDateFormat; 30 import com.ibm.icu.util.TimeZone; 31 import com.ibm.icu.util.ULocale; 32 33 public class VerifyZones { 34 private static final CLDRConfig CLDR_CONFIG = CLDRConfig.getInstance(); 35 36 private static final String DIR = CLDRPaths.CHART_DIRECTORY + "verify/zones/"; 37 38 private static final boolean DEBUG = false; 39 40 final static Options myOptions = new Options(); 41 42 enum MyOptions { 43 organization(".*", "CLDR", "organization"), filter(".*", ".*", "locale filter (regex)"), timezoneFilter(".*", null, "timezone filter (regex)"),; 44 // boilerplate 45 final Option option; 46 MyOptions(String argumentPattern, String defaultArgument, String helpText)47 MyOptions(String argumentPattern, String defaultArgument, String helpText) { 48 option = myOptions.add(this, argumentPattern, defaultArgument, helpText); 49 } 50 } 51 52 static long date = new Date(new Date().getYear(), 0, 15, 0, 0, 0).getTime(); 53 static long date6 = date + 182L * 24 * 60 * 60 * 1000; 54 55 static class MetazoneRow extends R5<Long, String, String, Integer, String> { MetazoneRow(Integer order, Integer rawOffset, String container, int orderInMetazone, String metazone, String zone)56 public MetazoneRow(Integer order, Integer rawOffset, String container, int orderInMetazone, String metazone, String zone) { 57 super(((long) order << 32) + rawOffset, container, metazone, orderInMetazone, zone); 58 } 59 getContainer()60 public String getContainer() { 61 return get1(); 62 } 63 getMetazone()64 public String getMetazone() { 65 return get2(); 66 } 67 getZone()68 public String getZone() { 69 return get4(); 70 } 71 } 72 73 public static class ZoneFormats { 74 private String gmtFormat; 75 private String hourFormat; 76 private String[] hourFormatPlusMinus; 77 private ICUServiceBuilder icuServiceBuilder = new ICUServiceBuilder(); 78 private CLDRFile cldrFile; 79 80 public enum Length { 81 LONG, SHORT; toString()82 public String toString() { 83 return name().toLowerCase(Locale.ENGLISH); 84 } 85 } 86 87 public enum Type { 88 generic, standard, daylight, genericOrStandard 89 } 90 set(CLDRFile cldrFile)91 public ZoneFormats set(CLDRFile cldrFile) { 92 this.cldrFile = cldrFile; 93 94 gmtFormat = cldrFile.getWinningValue("//ldml/dates/timeZoneNames/gmtFormat"); 95 hourFormat = cldrFile.getWinningValue("//ldml/dates/timeZoneNames/hourFormat"); 96 hourFormatPlusMinus = hourFormat.split(";"); 97 icuServiceBuilder.setCldrFile(cldrFile); 98 return this; 99 } 100 formatGMT(TimeZone currentZone)101 public String formatGMT(TimeZone currentZone) { 102 int tzOffset = currentZone.getRawOffset(); 103 SimpleDateFormat dateFormat = icuServiceBuilder.getDateFormat("gregorian", 104 hourFormatPlusMinus[tzOffset >= 0 ? 0 : 1]); 105 String hoursMinutes = dateFormat.format(tzOffset >= 0 ? tzOffset : -tzOffset); 106 return MessageFormat.format(gmtFormat, hoursMinutes); 107 } 108 getExemplarCity(String timezoneString)109 public String getExemplarCity(String timezoneString) { 110 String exemplarCity = cldrFile.getWinningValue("//ldml/dates/timeZoneNames/zone[@type=\"" + timezoneString 111 + "\"]/exemplarCity"); 112 if (exemplarCity == null) { 113 exemplarCity = timezoneString.substring(timezoneString.lastIndexOf('/') + 1).replace('_', ' '); 114 } 115 return exemplarCity; 116 } 117 getMetazoneName(String metazone, Length length, Type typeIn)118 public String getMetazoneName(String metazone, Length length, Type typeIn) { 119 Type type = typeIn == Type.genericOrStandard ? Type.generic : typeIn; 120 String name = cldrFile.getWinningValue("//ldml/dates/timeZoneNames/metazone[@type=\"" 121 + metazone + "\"]/" + length + "/" + type); 122 123 return name != null ? name : typeIn != Type.genericOrStandard ? "n/a" : getMetazoneName(metazone, length, 124 Type.standard); 125 } 126 } 127 128 private final static SupplementalDataInfo sdi = SupplementalDataInfo.getInstance(); 129 private final static Map<String, Map<String, String>> metazoneToRegionToZone = sdi.getMetazoneToRegionToZone(); 130 private final static Set<MetazoneRow> rows = new TreeSet<MetazoneRow>(); 131 private final static Set<String> goldenZones = new HashSet<String>(); 132 private final static Map<String, Integer> countryToOrder = new HashMap<String, Integer>(); 133 134 private final static List<Format> FORMAT_LIST = Arrays.asList(Format.VVVV, Format.vvvv, Format.v, Format.zzzz, 135 Format.z, Format.zzzz, Format.z); 136 static { 137 138 // find out which canonical zones are not in a metazone 139 Map<String, String> nameToCountry = new TreeMap<String, String>(); 140 String[] zones = TimeZone.getAvailableIDs(); 141 Set<String> zoneSet = new LinkedHashSet<String>(); 142 Set<String> noncanonical = new LinkedHashSet<String>(); 143 for (String zone : zones) { 144 String countryCode = TimeZone.getRegion(zone); 145 String englishTerritory = ULocale.getDisplayCountry("und-" + countryCode, ULocale.ENGLISH); nameToCountry.put(englishTerritory, countryCode)146 nameToCountry.put(englishTerritory, countryCode); 147 String canon = TimeZone.getCanonicalID(zone); 148 if (canon.equals(zone)) { 149 zoneSet.add(canon); 150 } else { 151 noncanonical.add(zone); 152 } 153 } 154 155 // get mapping of country names to ints 156 int i = 0; 157 for (Entry<String, String> entry : nameToCountry.entrySet()) { entry.getValue()158 countryToOrder.put(entry.getValue(), i++); 159 } 160 161 //System.out.println("Canonical zones:\t" + zoneSet.size() + "\t" + zoneSet); 162 //System.out.println("Non-canonical zones:\t" + noncanonical.size() + "\t" + noncanonical); 163 164 Set<String> metazones = sdi.getAllMetazones(); 165 if (DEBUG && !metazones.equals(metazoneToRegionToZone.keySet())) { 166 System.out.println("Mismatch between metazones"); showVennSets(metazones, metazoneToRegionToZone.keySet())167 showVennSets(metazones, metazoneToRegionToZone.keySet()); 168 } 169 170 Set<String> zonesInMetazones = new LinkedHashSet<String>(); 171 for (String metazone : metazones) { 172 //String container = PathHeader.getMetazonePageTerritory(metazone); 173 Map<String, String> regionToZone = metazoneToRegionToZone.get(metazone); 174 String zone = regionToZone.get("001"); 175 goldenZones.add(zone); 176 zonesInMetazones.add(zone); 177 // TimeZone currentZone = TimeZone.getTimeZone(tz_string); 178 // int order = Containment.getOrder(container); 179 // int offsetOrder = currentZone.getRawOffset(); 180 // MetazoneRow row = new MetazoneRow(order, offsetOrder, container, 0, metazone, tz_string); 181 // rows.add(row); addRow(metazone, zone, 0)182 addRow(metazone, zone, 0); 183 } 184 //System.out.println("Zones. A = canonical zones, B = zones in metazonesToRegionToZone"); 185 //showVennSets(zoneSet, zonesInMetazones); vennSets(zoneSet, zonesInMetazones)186 vennSets(zoneSet, zonesInMetazones); 187 Set<String> found = new LinkedHashSet<String>(); 188 for (String zone : zoneSet) { 189 Set<MetaZoneRange> metaZoneRanges = sdi.getMetaZoneRanges(zone); 190 if (metaZoneRanges == null) { 191 continue; 192 } 193 for (MetaZoneRange metaZoneRange : metaZoneRanges) { 194 if (metaZoneRange.dateRange.getTo() == DateRange.END_OF_TIME) { 195 found.add(zone); addRow(metaZoneRange.metazone, zone, 1)196 addRow(metaZoneRange.metazone, zone, 1); 197 break; 198 } 199 } 200 } 201 // zoneSet.removeAll(found); 202 // for (String zone : zoneSet) { 203 // found.add(zone); 204 // // TimeZone currentZone = TimeZone.getTimeZone(tz_string); 205 // // int offsetOrder = currentZone.getRawOffset(); 206 // // MetazoneRow row = new MetazoneRow(Integer.MAX_VALUE, offsetOrder, "001", 1, "None", tz_string); 207 // // rows.add(row); 208 // addRow("None", zone, 1); 209 // } 210 if (DEBUG) System.out.println("\nSorted"); 211 for (MetazoneRow row : rows) { 212 if (row.getMetazone().equals("Europe_Central")) { 213 if (DEBUG) System.out.println(row); 214 } 215 } 216 } 217 addRow(String metaZone, String tz_string, int orderInMetazone)218 private static void addRow(String metaZone, String tz_string, int orderInMetazone) { 219 TimeZone currentZone = TimeZone.getTimeZone(tz_string); 220 String container = PathHeader.getMetazonePageTerritory(metaZone); 221 if (container == null) { 222 return; // skip 223 } 224 int order = Containment.getOrder(container); 225 int offsetOrder = currentZone.getRawOffset(); 226 orderInMetazone = (orderInMetazone << 16) 227 | (hasDaylight(currentZone) ? 0 : 1) 228 | countryToOrder.get(TimeZone.getRegion(tz_string)); 229 MetazoneRow row = new MetazoneRow(order, offsetOrder, container, 230 orderInMetazone, metaZone, tz_string); 231 if (metaZone.equals("Europe_Central")) { 232 if (DEBUG) System.out.println(row); 233 } 234 rows.add(row); 235 } 236 showVennSets(Set<String> zoneSet, Set<String> zonesInMetazones)237 private static void showVennSets(Set<String> zoneSet, Set<String> zonesInMetazones) { 238 Set<String> common = new LinkedHashSet<String>(); 239 Set<String> firstMinusSecond = new LinkedHashSet<String>(); 240 Set<String> secondMinusFirst = new LinkedHashSet<String>(); 241 vennSets(zoneSet, zonesInMetazones, common, firstMinusSecond, secondMinusFirst); 242 if (!common.isEmpty()) System.out.println("A & B:\t" + common.size() + "\t" + common); 243 if (!firstMinusSecond.isEmpty()) System.out.println("A - B:\t" + firstMinusSecond.size() + "\t" + firstMinusSecond); 244 if (!secondMinusFirst.isEmpty()) System.out.println("B - A:\t" + secondMinusFirst.size() + "\t" + secondMinusFirst); 245 } 246 vennSets(Set<T> first, Set<T> second, Set<T> common, Set<T> firstMinusSecond, Set<T> secondMinusFirst)247 private static <T> void vennSets(Set<T> first, Set<T> second, 248 Set<T> common, Set<T> firstMinusSecond, Set<T> secondMinusFirst) { 249 common.clear(); 250 common.addAll(first); 251 common.retainAll(second); 252 firstMinusSecond.clear(); 253 firstMinusSecond.addAll(first); 254 firstMinusSecond.removeAll(common); 255 secondMinusFirst.clear(); 256 secondMinusFirst.addAll(second); 257 secondMinusFirst.removeAll(common); 258 } 259 260 @SuppressWarnings("unused") vennSets(Set<T> first, Set<T> second, Set<T> common)261 private static <T> void vennSets(Set<T> first, Set<T> second, Set<T> common) { 262 common.clear(); 263 common.addAll(first); 264 common.retainAll(second); 265 first.removeAll(common); 266 second.removeAll(common); 267 } 268 vennSets(Set<T> first, Set<T> second)269 private static <T> void vennSets(Set<T> first, Set<T> second) { 270 first.removeAll(second); 271 second.removeAll(first); 272 } 273 274 /** 275 * Produce a set of static tables from the vxml data. Only a stopgap until the above is integrated into ST. 276 * 277 * @param args 278 * @throws IOException 279 */ main(String[] args)280 public static void main(String[] args) throws IOException { 281 myOptions.parse(MyOptions.organization, args, true); 282 283 String organization = MyOptions.organization.option.getValue(); 284 String filter = MyOptions.filter.option.getValue(); 285 String timezoneFilterString = MyOptions.timezoneFilter.option.getValue(); 286 Matcher timezoneFilter = timezoneFilterString == null ? null : PatternCache.get(timezoneFilterString) 287 .matcher(""); 288 289 Factory factory2 = Factory.make(CLDRPaths.MAIN_DIRECTORY, filter); 290 CLDRFile englishCldrFile = factory2.make("en", true); 291 DateTimeFormats.writeCss(DIR); 292 final CLDRFile english = CLDR_CONFIG.getEnglish(); 293 294 Map<String, String> indexMap = new TreeMap<>(CLDR_CONFIG.getCollator()); 295 296 for (String localeID : factory2.getAvailableLanguages()) { 297 Level level = StandardCodes.make().getLocaleCoverageLevel(organization, localeID); 298 if (Level.MODERN.compareTo(level) > 0) { 299 continue; 300 } 301 CLDRFile cldrFile = factory2.make(localeID, true); 302 PrintWriter out = FileUtilities.openUTF8Writer(DIR, localeID + ".html"); 303 String title = "Verify Time Zones: " + englishCldrFile.getName(localeID); 304 out.println("<!doctype HTML PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'><html><head>\n" + 305 "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n" + 306 "<title>" + title + "</title>\n" + 307 "<link rel='stylesheet' type='text/css' href='index.css'>\n" + 308 "</head><body><h1>" + title + "</h1>\n" 309 + "<p><a href='index.html'>Index</a></p>\n"); 310 311 showZones(timezoneFilter, englishCldrFile, cldrFile, out); 312 313 out.println("</body></html>"); 314 out.close(); 315 316 indexMap.put(english.getName(localeID), localeID + ".html"); 317 } 318 try (PrintWriter index = DateTimeFormats.openIndex(DIR, "Time Zones")) { 319 DateTimeFormats.writeIndexMap(indexMap, index); 320 } 321 322 // Look at DateTimeFormats.java 323 324 if (true) return; 325 326 // Set<String> defaultContentLocales = sdi.getDefaultContentLocales(); 327 // NumberFormat enf = NumberFormat.getIntegerInstance(ULocale.ENGLISH); 328 // enf.setGroupingUsed(false); 329 // Set<String> debugCreationErrors = new LinkedHashSet<String>(); 330 // Set<String> errors = new LinkedHashSet<String>(); 331 // 332 // for (String locale : factory2.getAvailableLanguages()) { 333 // if (defaultContentLocales.contains(locale)) { 334 // continue; 335 // } 336 // Level level = StandardCodes.make().getLocaleCoverageLevel(organization, locale); 337 // if (Level.MODERN.compareTo(level) > 0) { 338 // continue; 339 // } 340 // 341 // // one path for group-3, one for group-4 342 // int factor = USES_GROUPS_OF_4.contains(locale) ? 10000 : 1000; 343 // 344 // ULocale locale2 = new ULocale(locale); 345 // NumberFormat nf = NumberFormat.getIntegerInstance(locale2); 346 // nf.setMaximumFractionDigits(0); 347 // CLDRFile cldrFile = factory2.make(locale, true, DraftStatus.contributed); 348 // PluralInfo pluralInfo = sdi.getPlurals(locale); 349 // Set<Double> samples = new TreeSet<Double>(); 350 // for (Entry<Count, List<Double>> entry : pluralInfo.getCountToExamplesMap().entrySet()) { 351 // samples.add(entry.getValue().get(0)); 352 // } 353 // String[] debugOriginals = null; 354 // CompactDecimalFormat cdf = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors, debugOriginals, 355 // Style.SHORT, locale2); 356 // captureErrors(debugCreationErrors, errors, locale, "short"); 357 // CompactDecimalFormat cdfs = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors, debugOriginals, 358 // Style.LONG, locale2); 359 // captureErrors(debugCreationErrors, errors, locale, "long"); 360 // 361 // Set<Double> samples2 = new TreeSet<Double>(); 362 // for (int i = 10; i < factor; i *= 10) { 363 // for (Double sample : samples) { 364 // samples2.add(sample*i); 365 // } 366 // } 367 // samples.addAll(samples2); 368 // samples.add(1.5d); 369 // System.out.println("———\t" + englishCldrFile.getName(locale) + "\t———"); 370 // 371 // String column12 = (locale + "\t" + englishCldrFile.getName(locale)); 372 // System.out.print(column12 + 373 // "\tNumeric\tCompact-Short\tCompact-Long\tFixed Numeric\tFixed Compact-Short\tFixed Compact-Long\n"); 374 // 375 // try { 376 // // we print the __ so that it can be imported into a spreadsheet without problems. 377 // for (long i = factor; i <= 100000000000000L; i *= factor) { 378 // for (Double sample : samples) { 379 // double source = i * sample; 380 // if (false && source == 22000000 && locale.equals("cs")) { 381 // System.out.println("**"); 382 // } 383 // System.out.print(locale + "\t__" + enf.format(source)); 384 // System.out.print("\t__" + nf.format(source)); 385 // String formatted = cdf.format(source); 386 // System.out.print("\t__" + formatted); 387 // formatted = cdfs.format(source); 388 // System.out.println("\t__" + formatted); 389 // } 390 // System.out.println(); 391 // } 392 // } catch (Exception e) { 393 // e.printStackTrace(); 394 // } 395 // } 396 // for (String s : errors) { 397 // System.out.println(s); 398 // } 399 } 400 showZones(Matcher timezoneFilter, CLDRFile englishCldrFile, CLDRFile nativeCdrFile, Appendable out)401 public static void showZones(Matcher timezoneFilter, 402 CLDRFile englishCldrFile, CLDRFile nativeCdrFile, 403 Appendable out) throws IOException { 404 TablePrinter tablePrinter = new TablePrinter() // .setCaption("Timezone Formats") 405 .setTableAttributes("class='dtf-table'") 406 .addColumn("Metazone").setHeaderCell(true).setSpanRows(true) 407 .setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'") 408 .addColumn("Region: TZID").setHeaderCell(true).setSpanRows(true) 409 .setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'") 410 //.setCellPattern(CldrUtility.getDoubleLinkMsg()) 411 // HACK because anchors don't work any more 412 // .addColumn("Region: City").setHeaderCell(true).setSpanRows(true) 413 // .addColumn("Region/City").setSpanRows(true) 414 ; 415 // .addColumn("Code", "class='source'", CldrUtility.getDoubleLinkMsg(), "class='source'", true).setSpanRows(true) 416 417 boolean daylight = false; 418 for (Format s : FORMAT_LIST) { 419 tablePrinter.addColumn(s.toString() 420 + "<br>" + s.type.toString(daylight) 421 + "<br>" + s.location 422 + "<br>" + s.length).setSpanRows(true).setHeaderAttributes("class='dtf-th'") 423 .setCellAttributes("class='dtf-s'"); 424 if (s == Format.z) { 425 daylight = true; // reset for final 2 items 426 } 427 } 428 tablePrinter.addColumn("View").setHeaderCell(true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'"); 429 ZoneFormats englishZoneFormats = new ZoneFormats().set(englishCldrFile); 430 addZones(englishZoneFormats, nativeCdrFile, timezoneFilter, tablePrinter); 431 432 out.append(tablePrinter.toString() + "\n"); 433 } 434 addZones(ZoneFormats englishZoneFormats, CLDRFile cldrFile, Matcher timezoneFilter, TablePrinter output)435 private static void addZones(ZoneFormats englishZoneFormats, CLDRFile cldrFile, Matcher timezoneFilter, 436 TablePrinter output) throws IOException { 437 CLDRFile englishCldrFile = englishZoneFormats.cldrFile; 438 //ZoneFormats nativeZoneFormats = new ZoneFormats().set(cldrFile); 439 TimezoneFormatter tzformatter = new TimezoneFormatter(cldrFile); 440 441 for (MetazoneRow row : rows) { 442 String grouping = row.getContainer(); 443 String metazone = row.getMetazone(); 444 String tzid = row.getZone(); 445 TimeZone currentZone = TimeZone.getTimeZone(tzid); 446 TimeZone tz = currentZone; 447 448 String englishGrouping = englishCldrFile.getName(CLDRFile.TERRITORY_NAME, grouping); 449 450 String metazoneInfo = englishGrouping 451 + "<br>" + englishZoneFormats.formatGMT(currentZone) 452 + "<br>" + "MZ: " + metazone; 453 454 boolean isGolden = goldenZones.contains(tzid); 455 String countryCode2 = TimeZone.getRegion(tzid); 456 if (countryCode2.equals("001")) { 457 continue; 458 } 459 String englishTerritory = englishCldrFile.getName(CLDRFile.TERRITORY_NAME, countryCode2); 460 output.addRow() 461 .addCell(metazoneInfo) 462 .addCell(englishTerritory + ": " + tzid.replace("/", "/\u200B")); 463 long date2 = getStandardDate(tz); 464 for (Format pattern : FORMAT_LIST) { 465 String formattedZone = tzformatter.getFormattedZone(tzid, pattern.toString(), date2); 466 if (isGolden) { 467 formattedZone = "<b>" + formattedZone + "</b>"; 468 } 469 output.addCell(formattedZone); 470 if (pattern == Format.z) { 471 if (!hasDaylight(tz)) { 472 output.addCell("<i>n/a</i>"); 473 output.addCell("<i>n/a</i>"); 474 break; 475 } 476 date2 = date2 == date ? date6 : date; // reverse for final 2 items 477 } 478 } 479 String view = PathHeader.getLinkedView(surveyUrl, cldrFile, METAZONE_PREFIX + metazone + METAZONE_SUFFIX); 480 if (view == null) { 481 view = PathHeader.getLinkedView(surveyUrl, cldrFile, METAZONE_PREFIX + metazone + METAZONE_SUFFIX2); 482 } 483 484 output.addCell(view == null 485 ? "" 486 : view); 487 output.finishRow(); 488 } 489 } 490 491 private static String surveyUrl = CLDR_CONFIG.getProperty("CLDR_SURVEY_URL", 492 "http://st.unicode.org/cldr-apps/survey"); 493 494 static private String METAZONE_PREFIX = "//ldml/dates/timeZoneNames/metazone[@type=\""; 495 static private String METAZONE_SUFFIX = "\"]/long/generic"; 496 static private String METAZONE_SUFFIX2 = "\"]/long/standard"; 497 hasDaylight(TimeZone tz)498 private static boolean hasDaylight(TimeZone tz) { 499 int dateOffset = tz.getOffset(date); 500 return dateOffset != tz.getRawOffset() || dateOffset != tz.getOffset(date6); 501 } 502 getStandardDate(TimeZone tz)503 private static long getStandardDate(TimeZone tz) { 504 return tz.getOffset(date) == tz.getRawOffset() ? date : date6; 505 } 506 getDaylightDate(TimeZone tz)507 private static long getDaylightDate(TimeZone tz) { 508 return tz.getOffset(date) == tz.getRawOffset() ? date6 : date; 509 } 510 captureErrors(Set<String> debugCreationErrors, Set<String> errors, String locale, String length)511 private static void captureErrors(Set<String> debugCreationErrors, Set<String> errors, String locale, String length) { 512 if (debugCreationErrors.size() != 0) { 513 for (String s : debugCreationErrors) { 514 errors.add(locale + "\t" + length + "\t" + s); 515 } 516 debugCreationErrors.clear(); 517 } 518 } 519 } 520