Home | History | Annotate | Download | only in util
      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 
     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> {
     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 
     60         public String getContainer() {
     61             return get1();
     62         }
     63 
     64         public String getMetazone() {
     65             return get2();
     66         }
     67 
     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;
     82             public String toString() {
     83                 return name().toLowerCase(Locale.ENGLISH);
     84             }
     85         }
     86 
     87         public enum Type {
     88             generic, standard, daylight, genericOrStandard
     89         }
     90 
     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 
    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 
    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 
    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);
    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()) {
    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");
    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);
    182             addRow(metazone, zone, 0);
    183         }
    184         //System.out.println("Zones. A = canonical zones, B = zones in metazonesToRegionToZone");
    185         //showVennSets(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);
    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 
    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 
    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 
    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")
    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 
    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      */
    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 
    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 
    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 
    498     private static boolean hasDaylight(TimeZone tz) {
    499         int dateOffset = tz.getOffset(date);
    500         return dateOffset != tz.getRawOffset() || dateOffset != tz.getOffset(date6);
    501     }
    502 
    503     private static long getStandardDate(TimeZone tz) {
    504         return tz.getOffset(date) == tz.getRawOffset() ? date : date6;
    505     }
    506 
    507     private static long getDaylightDate(TimeZone tz) {
    508         return tz.getOffset(date) == tz.getRawOffset() ? date6 : date;
    509     }
    510 
    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