From 5c98aa7c33761291db60f46a9edb63d7043ec3a2 Mon Sep 17 00:00:00 2001 From: haytenf Date: Tue, 10 Sep 2024 07:30:05 -0700 Subject: [PATCH] CLDR-15817 Update Example Generator (#3978) Co-authored-by: Steven R. Loomis --- .../unicode/cldr/test/ExampleGenerator.java | 439 +++++++++--------- .../cldr/unittest/TestExampleGenerator.java | 15 +- 2 files changed, 232 insertions(+), 222 deletions(-) diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java index 913e4884462..16702b605e6 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/ExampleGenerator.java @@ -445,6 +445,9 @@ private String getExampleHtmlExtended(String xpath, String value, boolean nonTri /** * Do the main work of getExampleHtml given that the result was not found in the cache. * + *

Creates a list that the handlers in constructExampleHtmlExtended can add examples to, and + * then formats the example list appropriately. + * * @param xpath the path; e.g., "//ldml/dates/timeZoneNames/fallbackFormat" * @param value the value; e.g., "{1} [{0}]"; not necessarily the winning value * @param nonTrivial true if we should avoid returning a trivial example (just value wrapped in @@ -452,7 +455,20 @@ private String getExampleHtmlExtended(String xpath, String value, boolean nonTri * @return the example HTML, or null */ private String constructExampleHtml(String xpath, String value, boolean nonTrivial) { - String result = null; + List examples = new ArrayList<>(); + constructExampleHtmlExtended(xpath, value, examples); + String result = formatExampleList(examples); + if (result != null) { // Handle the outcome + if (nonTrivial && value.equals(result)) { + result = null; + } else { + result = finalizeBackground(result); + } + } + return result; + } + + private void constructExampleHtmlExtended(String xpath, String value, List examples) { boolean showContexts = isRTL || BIDI_MARKS.containsSome(value); // only used for certain example types /* @@ -461,87 +477,77 @@ private String constructExampleHtml(String xpath, String value, boolean nonTrivi */ XPathParts parts = XPathParts.getFrozenInstance(xpath).cloneAsThawed(); if (parts.contains("dateRangePattern")) { // {0} - {1} - result = handleDateRangePattern(value); + handleDateRangePattern(value, examples); } else if (parts.contains("timeZoneNames")) { - result = handleTimeZoneName(parts, value); + handleTimeZoneName(parts, value, examples); } else if (parts.contains("localeDisplayNames")) { - result = handleDisplayNames(xpath, parts, value); + handleDisplayNames(xpath, parts, value, examples); } else if (parts.contains("currency")) { - result = handleCurrency(xpath, parts, value); + handleCurrency(xpath, parts, value, examples); } else if (parts.contains("dayPeriods")) { - result = handleDayPeriod(parts, value); + handleDayPeriod(parts, value, examples); } else if (parts.contains("monthContext")) { - result = handleDateSymbol(parts, value); + handleDateSymbol(parts, value, examples); } else if (parts.contains("pattern") || parts.contains("dateFormatItem")) { if (parts.contains("calendar")) { - result = handleDateFormatItem(xpath, value, showContexts); + handleDateFormatItem(xpath, value, showContexts, examples); } else if (parts.contains("miscPatterns")) { - result = handleMiscPatterns(parts, value); + handleMiscPatterns(parts, value, examples); } else if (parts.contains("numbers")) { if (parts.contains("currencyFormat")) { - result = handleCurrencyFormat(parts, value, showContexts); + handleCurrencyFormat(parts, value, showContexts, examples); } else { - result = handleDecimalFormat(parts, value, showContexts); + handleDecimalFormat(parts, value, showContexts, examples); } } } else if (parts.contains("minimumGroupingDigits")) { - result = handleMinimumGrouping(parts, value); + handleMinimumGrouping(parts, value, examples); } else if (parts.getElement(2).contains("symbols")) { - result = handleNumberSymbol(parts, value); + handleNumberSymbol(parts, value, examples); } else if (parts.contains("defaultNumberingSystem") || parts.contains("otherNumberingSystems")) { - result = handleNumberingSystem(value); + handleNumberingSystem(value, examples); } else if (parts.contains("currencyFormats") && parts.contains("unitPattern")) { - result = formatCountValue(xpath, parts, value); + formatCountValue(xpath, parts, value, examples); } else if (parts.getElement(-1).equals("compoundUnitPattern")) { - result = handleCompoundUnit(parts); + handleCompoundUnit(parts, examples); } else if (parts.getElement(-1).equals("compoundUnitPattern1") || parts.getElement(-1).equals("unitPrefixPattern")) { - result = handleCompoundUnit1(parts, value); + handleCompoundUnit1(parts, value, examples); } else if (parts.getElement(-2).equals("unit") && (parts.getElement(-1).equals("unitPattern") || parts.getElement(-1).equals("displayName"))) { - result = handleFormatUnit(parts, value); + handleFormatUnit(parts, value, examples); } else if (parts.getElement(-1).equals("perUnitPattern")) { - result = handleFormatPerUnit(value); + handleFormatPerUnit(value, examples); } else if (parts.getElement(-2).equals("minimalPairs")) { - result = handleMinimalPairs(parts, value); + handleMinimalPairs(parts, value, examples); } else if (parts.getElement(-1).equals("durationUnitPattern")) { - result = handleDurationUnit(value); + handleDurationUnit(value, examples); } else if (parts.contains("intervalFormats")) { - result = handleIntervalFormats(parts, value); + handleIntervalFormats(parts, value, examples); } else if (parts.getElement(1).equals("delimiters")) { - result = handleDelimiters(parts, xpath, value); + handleDelimiters(parts, xpath, value, examples); } else if (parts.getElement(1).equals("listPatterns")) { - result = handleListPatterns(parts, value); + handleListPatterns(parts, value, examples); } else if (parts.getElement(2).equals("ellipsis")) { - result = handleEllipsis(parts.getAttributeValue(-1, "type"), value); + handleEllipsis(parts.getAttributeValue(-1, "type"), value, examples); } else if (parts.getElement(-1).equals("monthPattern")) { - result = handleMonthPatterns(parts, value); + handleMonthPatterns(parts, value, examples); } else if (parts.getElement(-1).equals("appendItem")) { - result = handleAppendItems(parts, value); + handleAppendItems(parts, value, examples); } else if (parts.getElement(-1).equals("annotation")) { - result = handleAnnotationName(parts, value); + handleAnnotationName(parts, value, examples); } else if (parts.getElement(-1).equals("characterLabel")) { - result = handleLabel(parts, value); + handleLabel(parts, value, examples); } else if (parts.getElement(-1).equals("characterLabelPattern")) { - result = handleLabelPattern(parts, value); + handleLabelPattern(parts, value, examples); } else if (parts.getElement(1).equals("personNames")) { - result = handlePersonName(parts, value); + handlePersonName(parts, value, examples); } else if (parts.getElement(-1).equals("exemplarCharacters") || parts.getElement(-1).equals("parseLenient")) { - result = handleUnicodeSet(parts, xpath, value); + handleUnicodeSet(parts, xpath, value, examples); } - - // Handle the outcome - if (result != null) { - if (nonTrivial && value.equals(result)) { - result = null; - } else { - result = finalizeBackground(result); - } - } - return result; } // Note: may want to change to locale's order; if so, these would be instance fields @@ -559,13 +565,13 @@ private String constructExampleHtml(String xpath, String value, boolean nonTrivi * Add examples for UnicodeSets. First, show a hex format of non-spacing marks if there are any, * then show delta to the winning value if there are any. */ - private String handleUnicodeSet(XPathParts parts, String xpath, String value) { - ArrayList examples = new ArrayList<>(); + private void handleUnicodeSet( + XPathParts parts, String xpath, String value, List examples) { UnicodeSet valueSet; try { valueSet = new UnicodeSet(value); } catch (Exception e) { - return null; + return; } String winningValue = cldrFile.getWinningValue(xpath); if (!winningValue.equals(value)) { @@ -601,7 +607,6 @@ private String handleUnicodeSet(XPathParts parts, String xpath, String value) { } } examples.add(setBackground(INTERNAL) + valueSet.toPattern(false)); // internal format - return formatExampleList(examples); } /** @@ -659,7 +664,7 @@ public String toString() { private static final Function BACKGROUND_TRANSFORM = x -> backgroundStartSymbol + x + backgroundEndSymbol; - private String handlePersonName(XPathParts parts, String value) { + private void handlePersonName(XPathParts parts, String value, List examples) { // ldml/personNames/personName[@order="givenFirst"][@length="long"][@usage="addressing"][@style="formal"]/namePattern => {prefix} {surname} String debugState = "start"; try { @@ -670,12 +675,9 @@ private String handlePersonName(XPathParts parts, String value) { PersonNameFormatter.Usage.from(parts.getAttributeValue(2, "usage")), PersonNameFormatter.Formality.from( parts.getAttributeValue(2, "formality"))); - - List examples = null; final CLDRFile cldrFile2 = getCldrFile(); switch (parts.getElement(2)) { case "nameOrderLocales": - examples = new ArrayList<>(); for (String localeId : PersonNameFormatter.SPLIT_SPACE.split(value)) { final String name = localeId.equals("und") @@ -684,12 +686,7 @@ private String handlePersonName(XPathParts parts, String value) { examples.add(localeId + " = " + name); } break; - case "initialPattern": - return null; - case "sampleName": - return null; case "personName": - examples = new ArrayList<>(); Map sampleNames = personNamesCache.getSampleNames(cldrFile2); PersonNameFormatter personNameFormatter = @@ -741,7 +738,6 @@ private String handlePersonName(XPathParts parts, String value) { } break; } - return formatExampleList(examples); } catch (Exception e) { StringBuffer stackTrace; try (StringWriter sw = new StringWriter(); @@ -751,7 +747,8 @@ private String handlePersonName(XPathParts parts, String value) { } catch (Exception e2) { stackTrace = new StringBuffer("internal error"); } - return "Internal error: " + e.getMessage() + "\n" + debugState + "\n" + stackTrace; + examples.add( + "Internal error: " + e.getMessage() + "\n" + debugState + "\n" + stackTrace); } } @@ -826,9 +823,8 @@ private SimpleNameObject getBestAvailable( return null; } - private String handleLabelPattern(XPathParts parts, String value) { + private void handleLabelPattern(XPathParts parts, String value, List examples) { if ("category-list".equals(parts.getAttributeValue(-1, "type"))) { - List examples = new ArrayList<>(); CLDRFile cfile = getCldrFile(); SimpleFormatter initialPattern = SimpleFormatter.compile(setBackground(value)); String path = CLDRFile.getKey(CLDRFile.TERRITORY_NAME, "FR"); @@ -840,19 +836,16 @@ private String handleLabelPattern(XPathParts parts, String value) { EmojiConstants.getEmojiFromRegionCodes("FR") + " ⇒ " + initialPattern.format(flagName, regionName))); - return formatExampleList(examples); } - return null; } - private String handleLabel(XPathParts parts, String value) { + private void handleLabel(XPathParts parts, String value, List examples) { // "//ldml/characterLabels/characterLabel[@type=\"" + typeAttributeValue + "\"]" switch (parts.getAttributeValue(-1, "type")) { case "flag": { String value2 = backgroundStartSymbol + value + backgroundEndSymbol; CLDRFile cfile = getCldrFile(); - List examples = new ArrayList<>(); SimpleFormatter initialPattern = SimpleFormatter.compile( cfile.getStringValue( @@ -862,12 +855,11 @@ private String handleLabel(XPathParts parts, String value) { addSubdivisionFlag(value2, "gbeng", initialPattern, examples); addSubdivisionFlag(value2, "gbsct", initialPattern, examples); addSubdivisionFlag(value2, "gbwls", initialPattern, examples); - return formatExampleList(examples); + return; } case "keycap": { String value2 = backgroundStartSymbol + value + backgroundEndSymbol; - List examples = new ArrayList<>(); CLDRFile cfile = getCldrFile(); SimpleFormatter initialPattern = SimpleFormatter.compile( @@ -876,10 +868,10 @@ private String handleLabel(XPathParts parts, String value) { examples.add(invertBackground(initialPattern.format(value2, "1"))); examples.add(invertBackground(initialPattern.format(value2, "10"))); examples.add(invertBackground(initialPattern.format(value2, "#"))); - return formatExampleList(examples); + return; } default: - return null; + return; } } @@ -914,17 +906,16 @@ private void addSubdivisionFlag( + initialPattern.format(value2, subdivisionName))); } - private String handleAnnotationName(XPathParts parts, String value) { + private void handleAnnotationName(XPathParts parts, String value, List examples) { // ldml/annotations/annotation[@cp="🦰"][@type="tts"] // skip anything but the name if (!"tts".equals(parts.getAttributeValue(-1, "type"))) { - return null; + return; } String cp = parts.getAttributeValue(-1, "cp"); if (cp == null || cp.isEmpty()) { - return null; + return; } - Set examples = new LinkedHashSet<>(); int first = cp.codePointAt(0); switch (first) { case 0x1F46A: // 👪 U+1F46A FAMILY @@ -990,7 +981,6 @@ private String handleAnnotationName(XPathParts parts, String value) { } break; } - return formatExampleList(examples); } private String getEmojiName(CLDRFile cfile, String skin) { @@ -1059,13 +1049,12 @@ private void formatPeople( initialPattern.format(personName, skinName), hairName))); } - private String handleDayPeriod(XPathParts parts, String value) { + private void handleDayPeriod(XPathParts parts, String value, List examples) { // ldml/dates/calendars/calendar[@type="gregorian"]/dayPeriods/dayPeriodContext[@type="format"]/dayPeriodWidth[@type="wide"]/dayPeriod[@type="morning1"] // ldml/dates/calendars/calendar[@type="gregorian"]/dayPeriods/dayPeriodContext[@type="stand-alone"]/dayPeriodWidth[@type="wide"]/dayPeriod[@type="morning1"] - List examples = new ArrayList<>(); final String dayPeriodType = parts.getAttributeValue(5, "type"); if (dayPeriodType == null) { - return null; // formerly happened for some "/alias" paths + return; // formerly happened for some "/alias" paths } org.unicode.cldr.util.DayPeriodInfo.Type aType = dayPeriodType.equals("format") @@ -1075,7 +1064,7 @@ private String handleDayPeriod(XPathParts parts, String value) { supplementalDataInfo.getDayPeriods(aType, cldrFile.getLocaleID()); String periodString = parts.getAttributeValue(-1, "type"); if (periodString == null) { - return null; // formerly happened for some "/alias" paths + return; // formerly happened for some "/alias" paths } DayPeriod dayPeriod = DayPeriod.valueOf(periodString); String periods = dayPeriodInfo.toString(dayPeriod); @@ -1093,16 +1082,15 @@ private String handleDayPeriod(XPathParts parts, String value) { examples.add(invertBackground(timeFormatString)); } } - return formatExampleList(examples.toArray(new String[0])); } - private String handleDateSymbol(XPathParts parts, String value) { + private void handleDateSymbol(XPathParts parts, String value, List examples) { // Currently only called for month names, can expand in the future to handle other symbols. // The idea is to show format months in a yMMMM?d date format, and stand-alone months in a // yMMMM? format. String length = parts.findAttributeValue("monthWidth", "type"); // wide, abbreviated, narrow if (length.equals("narrow")) { - return null; // no examples for narrow + return; // no examples for narrow } String context = parts.findAttributeValue("monthContext", "type"); // format, stand-alone String calendarId = @@ -1137,7 +1125,7 @@ private String handleDateSymbol(XPathParts parts, String value) { dateFormat = cldrFile.getWinningValue(checkPath); } if (dateFormat == null) { - return null; + return; } SimpleDateFormat sdf = icuServiceBuilder.getDateFormat(calendarId, dateFormat); sdf.setTimeZone(ZONE_SAMPLE); @@ -1158,12 +1146,11 @@ private String handleDateSymbol(XPathParts parts, String value) { monthNames[8] = value; dfs.setMonths(monthNames, DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED); sdf.setDateFormatSymbols(dfs); - return sdf.format(DATE_SAMPLE); + examples.add(sdf.format(DATE_SAMPLE)); } - private String handleMinimalPairs(XPathParts parts, String minimalPattern) { - List examples = new ArrayList<>(); - + private void handleMinimalPairs( + XPathParts parts, String minimalPattern, List examples) { Output output = new Output<>(); String count; String otherCount; @@ -1213,7 +1200,7 @@ private String handleMinimalPairs(XPathParts parts, String minimalPattern) { sampleBad = bestMinimalPairSamples.getBestUnitWithGender(otherGender, output); break; default: - return null; + return; } String formattedUnit = format(minimalPattern, backgroundStartSymbol + sample + backgroundEndSymbol); @@ -1224,11 +1211,10 @@ private String handleMinimalPairs(XPathParts parts, String minimalPattern) { formattedUnit = format(minimalPattern, backgroundStartSymbol + sampleBad + backgroundEndSymbol); examples.add(EXAMPLE_OF_INCORRECT + formattedUnit); - return formatExampleList(examples); } private String getOtherGender(String gender) { - if (gender == null) { + if (gender == null || grammarInfo == null) { return null; } Collection unitGenders = @@ -1286,17 +1272,16 @@ private UnitLength getUnitLength(XPathParts parts) { return UnitLength.valueOf(parts.getAttributeValue(-3, "type").toUpperCase(Locale.ENGLISH)); } - private String handleFormatUnit(XPathParts parts, String unitPattern) { + private void handleFormatUnit(XPathParts parts, String unitPattern, List examples) { // Sample: // //ldml/units/unitLength[@type="long"]/unit[@type="duration-day"]/unitPattern[@count="one"][@case="accusative"] String longUnitId = parts.getAttributeValue(-2, "type"); final String shortUnitId = UNIT_CONVERTER.getShortId(longUnitId); if (UnitConverter.HACK_SKIP_UNIT_NAMES.contains(shortUnitId)) { - return null; + return; } - List examples = new ArrayList<>(); if (parts.getElement(-1).equals("unitPattern")) { String count = parts.getAttributeValue(-1, "count"); DecimalQuantity amount = getBest(Count.valueOf(count)); @@ -1340,7 +1325,6 @@ private String handleFormatUnit(XPathParts parts, String unitPattern) { relatedUnit, UnitSystem.getSystemsDisplay(systems))); } - return formatExampleList(examples); } /** @@ -1443,21 +1427,25 @@ private String getConstrastingCase( return null; } - private String handleFormatPerUnit(String value) { + private void handleFormatPerUnit(String value, List examples) { DecimalFormat numberFormat = icuServiceBuilder.getNumberFormat(1); - return format(value, backgroundStartSymbol + numberFormat.format(1) + backgroundEndSymbol); + examples.add( + format( + value, + backgroundStartSymbol + numberFormat.format(1) + backgroundEndSymbol)); } - public String handleCompoundUnit(XPathParts parts) { + public void handleCompoundUnit(XPathParts parts, List examples) { UnitLength unitLength = getUnitLength(parts); String compoundType = parts.getAttributeValue(-2, "type"); Count count = Count.valueOf(CldrUtility.ifNull(parts.getAttributeValue(-1, "count"), "other")); - return handleCompoundUnit(unitLength, compoundType, count); + handleCompoundUnit(unitLength, compoundType, count, examples); } @SuppressWarnings("deprecation") - public String handleCompoundUnit(UnitLength unitLength, String compoundType, Count count) { + public void handleCompoundUnit( + UnitLength unitLength, String compoundType, Count count, List examples) { /* * @@ -1482,7 +1470,8 @@ public String handleCompoundUnit(UnitLength unitLength, String compoundType, Cou // we want to get a number that works for the count passed in. DecimalQuantity amount = getBest(count); if (amount == null) { - return "n/a"; + examples.add("n/a"); + return; } DecimalQuantity oneValue = DecimalQuantity_DualStorageBCD.fromExponentString("1"); @@ -1490,7 +1479,8 @@ public String handleCompoundUnit(UnitLength unitLength, String compoundType, Cou String unit2mid; switch (compoundType) { default: - return "n/a"; + examples.add("n/a"); + return; case "per": unit1mid = getFormattedUnit("length-meter", unitLength, amount); unit2mid = getFormattedUnit("duration-second", unitLength, oneValue, ""); @@ -1511,20 +1501,22 @@ public String handleCompoundUnit(UnitLength unitLength, String compoundType, Cou String form = this.pluralInfo.getPluralRules().select(amount); // we rebuild a path, because we may have changed it. String perPath = makeCompoundUnitPath(unitLength, compoundType, "compoundUnitPattern"); - return format(getValueFromFormat(perPath, form), unit1, unit2); + examples.add(format(getValueFromFormat(perPath, form), unit1, unit2)); } - public String handleCompoundUnit1(XPathParts parts, String compoundPattern) { + public void handleCompoundUnit1( + XPathParts parts, String compoundPattern, List examples) { UnitLength unitLength = getUnitLength(parts); String pathCount = parts.getAttributeValue(-1, "count"); if (pathCount == null) { - return handleCompoundUnit1Name(unitLength, compoundPattern); + handleCompoundUnit1Name(unitLength, compoundPattern, examples); } else { - return handleCompoundUnit1(unitLength, Count.valueOf(pathCount), compoundPattern); + handleCompoundUnit1(unitLength, Count.valueOf(pathCount), compoundPattern, examples); } } - private String handleCompoundUnit1Name(UnitLength unitLength, String compoundPattern) { + private void handleCompoundUnit1Name( + UnitLength unitLength, String compoundPattern, List examples) { String pathFormat = "//ldml/units/unitLength" + unitLength.typeString @@ -1535,15 +1527,17 @@ private String handleCompoundUnit1Name(UnitLength unitLength, String compoundPat String modFormat = combinePrefix(meterFormat, compoundPattern, unitLength == UnitLength.LONG); - return removeEmptyRuns(modFormat); + examples.add(removeEmptyRuns(modFormat)); } - public String handleCompoundUnit1(UnitLength unitLength, Count count, String compoundPattern) { + public void handleCompoundUnit1( + UnitLength unitLength, Count count, String compoundPattern, List examples) { // we want to get a number that works for the count passed in. DecimalQuantity amount = getBest(count); if (amount == null) { - return "n/a"; + examples.add("n/a"); + return; } DecimalFormat numberFormat = icuServiceBuilder.getNumberFormat(1); @@ -1564,7 +1558,8 @@ public String handleCompoundUnit1(UnitLength unitLength, Count count, String com String modFormat = combinePrefix(meterFormat, compoundPattern, unitLength == UnitLength.LONG); - return removeEmptyRuns(format(modFormat, numberFormat.format(amount.toBigDecimal()))); + examples.add( + removeEmptyRuns(format(modFormat, numberFormat.format(amount.toBigDecimal())))); } // TODO, pass in unitLength instead of last parameter, and do work in Units.combinePattern. @@ -1612,28 +1607,30 @@ private DecimalQuantity getBest(Count count) { return range.end; } - private String handleMiscPatterns(XPathParts parts, String value) { + private void handleMiscPatterns(XPathParts parts, String value, List examples) { DecimalFormat numberFormat = icuServiceBuilder.getNumberFormat(0); String start = backgroundStartSymbol + numberFormat.format(99) + backgroundEndSymbol; if ("range".equals(parts.getAttributeValue(-1, "type"))) { String end = backgroundStartSymbol + numberFormat.format(144) + backgroundEndSymbol; - return format(value, start, end); + examples.add(format(value, start, end)); } else { - return format(value, start); + examples.add(format(value, start)); } } - private String handleIntervalFormats(XPathParts parts, String value) { + private void handleIntervalFormats(XPathParts parts, String value, List examples) { if (!parts.getAttributeValue(3, "type").equals("gregorian")) { - return null; + return; } if (parts.getElement(6).equals("intervalFormatFallback")) { SimpleDateFormat dateFormat = new SimpleDateFormat(); String fallbackFormat = invertBackground(setBackground(value)); - return format( - fallbackFormat, - dateFormat.format(FIRST_INTERVAL), - dateFormat.format(SECOND_INTERVAL.get("y"))); + examples.add( + format( + fallbackFormat, + dateFormat.format(FIRST_INTERVAL), + dateFormat.format(SECOND_INTERVAL.get("y")))); + return; } String greatestDifference = parts.getAttributeValue(-1, "id"); /* @@ -1659,12 +1656,13 @@ private String handleIntervalFormats(XPathParts parts, String value) { * Reference: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table * For now, such paths do not get examples. */ - return null; + return; } - return intervalFormat.format(FIRST_INTERVAL, later); + examples.add(intervalFormat.format(FIRST_INTERVAL, later)); } - private String handleDelimiters(XPathParts parts, String xpath, String value) { + private void handleDelimiters( + XPathParts parts, String xpath, String value, List examples) { String lastElement = parts.getElement(-1); final String[] elements = { "quotationStart", "alternateQuotationStart", @@ -1685,65 +1683,70 @@ private String handleDelimiters(XPathParts parts, String xpath, String value) { "//ldml/localeDisplayNames/types/type[@key=\"calendar\"][@type=\"gregorian\"]"); // NOTE: the example provided here is partially in English because we don't // have a translated conversational example in CLDR. - return invertBackground( - format("{0}They said {1}" + example + "{2}.{3}", (Object[]) quotes)); + examples.add( + invertBackground( + format("{0}They said {1}" + example + "{2}.{3}", (Object[]) quotes))); } - private String handleListPatterns(XPathParts parts, String value) { + private void handleListPatterns(XPathParts parts, String value, List examples) { // listPatternType is either "duration" or null/other list String listPatternType = parts.getAttributeValue(-2, "type"); if (listPatternType == null || !listPatternType.contains("unit")) { - return handleRegularListPatterns(parts, value, ListTypeLength.from(listPatternType)); + handleRegularListPatterns(parts, value, ListTypeLength.from(listPatternType), examples); } else { - return handleDurationListPatterns(parts, value, UnitLength.from(listPatternType)); + handleDurationListPatterns(parts, value, UnitLength.from(listPatternType), examples); } } - private String handleRegularListPatterns( - XPathParts parts, String value, ListTypeLength listTypeLength) { + private void handleRegularListPatterns( + XPathParts parts, String value, ListTypeLength listTypeLength, List examples) { String patternType = parts.getAttributeValue(-1, "type"); if (patternType == null) { - return null; // formerly happened for some "/alias" paths + return; // formerly happened for some "/alias" paths } String pathFormat = "//ldml/localeDisplayNames/territories/territory[@type=\"{0}\"]"; String territory1 = getValueFromFormat(pathFormat, "CH"); String territory2 = getValueFromFormat(pathFormat, "JP"); if (patternType.equals("2")) { - return invertBackground(format(setBackground(value), territory1, territory2)); + examples.add(invertBackground(format(setBackground(value), territory1, territory2))); + return; } String territory3 = getValueFromFormat(pathFormat, "EG"); String territory4 = getValueFromFormat(pathFormat, "CA"); - return longListPatternExample( - listTypeLength.getPath(), - patternType, - value, - territory1, - territory2, - territory3, - territory4); - } - - private String handleDurationListPatterns( - XPathParts parts, String value, UnitLength unitWidth) { + examples.add( + longListPatternExample( + listTypeLength.getPath(), + patternType, + value, + territory1, + territory2, + territory3, + territory4)); + } + + private void handleDurationListPatterns( + XPathParts parts, String value, UnitLength unitWidth, List examples) { String patternType = parts.getAttributeValue(-1, "type"); if (patternType == null) { - return null; // formerly happened for some "/alias" paths + return; // formerly happened for some "/alias" paths } String duration1 = getFormattedUnit("duration-day", unitWidth, 4); String duration2 = getFormattedUnit("duration-hour", unitWidth, 2); if (patternType.equals("2")) { - return invertBackground(format(setBackground(value), duration1, duration2)); + examples.add(invertBackground(format(setBackground(value), duration1, duration2))); + return; } String duration3 = getFormattedUnit("duration-minute", unitWidth, 37); String duration4 = getFormattedUnit("duration-second", unitWidth, 23); - return longListPatternExample( - unitWidth.listTypeLength.getPath(), - patternType, - value, - duration1, - duration2, - duration3, - duration4); + examples.add( + longListPatternExample( + unitWidth.listTypeLength.getPath(), + patternType, + value, + duration1, + duration2, + duration3, + duration4)); } public enum UnitLength { @@ -1843,7 +1846,7 @@ private String getValueFromFormat(String format, Object... arguments) { return cldrFile.getWinningValue(format(format, arguments)); } - public String handleEllipsis(String type, String value) { + public void handleEllipsis(String type, String value, List examples) { String pathFormat = "//ldml/localeDisplayNames/territories/territory[@type=\"{0}\"]"; // {0} … // … {0} @@ -1858,7 +1861,7 @@ public String handleEllipsis(String type, String value) { if (type.contains("initial")) { territory1 = territory2; } - return invertBackground(format(setBackground(value), territory1, territory2)); + examples.add(invertBackground(format(setBackground(value), territory1, territory2))); } public static String clip(String text, int clipStart, int clipEnd) { @@ -1883,7 +1886,7 @@ public static String clip(String text, int clipStart, int clipEnd) { * @param value * @return */ - private String handleMonthPatterns(XPathParts parts, String value) { + private void handleMonthPatterns(XPathParts parts, String value, List examples) { String calendar = parts.getAttributeValue(3, "type"); String context = parts.getAttributeValue(5, "type"); String month = "8"; @@ -1893,20 +1896,20 @@ private String handleMonthPatterns(XPathParts parts, String value) { "//ldml/dates/calendars/calendar[@type=\"{0}\"]/months/monthContext[@type=\"{1}\"]/monthWidth[@type=\"{2}\"]/month[@type=\"8\"]"; month = getValueFromFormat(xpath, calendar, context, width); } - return invertBackground(format(setBackground(value), month)); + examples.add(invertBackground(format(setBackground(value), month))); } - private String handleAppendItems(XPathParts parts, String value) { + private void handleAppendItems(XPathParts parts, String value, List examples) { String request = parts.getAttributeValue(-1, "request"); if (!"Timezone".equals(request)) { - return null; + return; } String calendar = parts.getAttributeValue(3, "type"); SimpleDateFormat sdf = icuServiceBuilder.getDateFormat(calendar, 0, DateFormat.MEDIUM, null); String zone = cldrFile.getStringValue("//ldml/dates/timeZoneNames/gmtZeroFormat"); - return format(value, setBackground(sdf.format(DATE_SAMPLE)), setBackground(zone)); + examples.add(format(value, setBackground(sdf.format(DATE_SAMPLE)), setBackground(zone))); } private class IntervalFormat { @@ -1989,22 +1992,23 @@ public IntervalFormat setPattern(XPathParts parts, String pattern) { } } - private String handleDurationUnit(String value) { + private void handleDurationUnit(String value, List examples) { DateFormat df = this.icuServiceBuilder.getDateFormat("gregorian", value.replace('h', 'H')); df.setTimeZone(TimeZone.GMT_ZONE); long time = ((5 * 60 + 37) * 60 + 23) * 1000; try { - return df.format(new Date(time)); + examples.add(df.format(new Date(time))); } catch (IllegalArgumentException e) { // e.g., Illegal pattern character 'o' in "aɖabaƒoƒo m:ss" - return null; + return; } } @SuppressWarnings("deprecation") - private String formatCountValue(String xpath, XPathParts parts, String value) { + private void formatCountValue( + String xpath, XPathParts parts, String value, List examples) { if (!parts.containsAttribute("count")) { // no examples for items that don't format - return null; + return; } final PluralInfo plurals = supplementalDataInfo.getPlurals(PluralType.cardinal, cldrFile.getLocaleID()); @@ -2021,12 +2025,12 @@ private String formatCountValue(String xpath, XPathParts parts, String value) { final LinkedHashSet exampleCount = new LinkedHashSet<>(CURRENCY_SAMPLES); String countString = parts.getAttributeValue(-1, "count"); if (countString == null) { - return null; + return; } else { try { count = Count.valueOf(countString); } catch (Exception e) { - return null; // counts like 0 + return; // counts like 0 } } @@ -2102,7 +2106,7 @@ private String formatCountValue(String xpath, XPathParts parts, String value) { } } } - return result.isEmpty() ? null : result; + examples.add(result.isEmpty() ? null : result); } @SuppressWarnings("deprecation") @@ -2214,7 +2218,7 @@ public String countAttribute(Count count) { return "[@count=\"" + count + "\"]"; } - private String handleMinimumGrouping(XPathParts parts, String value) { + private void handleMinimumGrouping(XPathParts parts, String value, List examples) { String numberSystem = cldrFile.getWinningValue("//ldml/numbers/defaultNumberingSystem"); String checkPath = "//ldml/numbers/decimalFormats[@numberSystem=\"" @@ -2227,14 +2231,12 @@ private String handleMinimumGrouping(XPathParts parts, String value) { double sampleNum1 = 543.21; double sampleNum2 = 6543.21; double sampleNum3 = 76543.21; - String example = ""; - example = addExampleResult(formatNumber(numberFormat, sampleNum1), example); - example = addExampleResult(formatNumber(numberFormat, sampleNum2), example); - example = addExampleResult(formatNumber(numberFormat, sampleNum3), example); - return example; + examples.add(formatNumber(numberFormat, sampleNum1)); + examples.add(formatNumber(numberFormat, sampleNum2)); + examples.add(formatNumber(numberFormat, sampleNum3)); } - private String handleNumberSymbol(XPathParts parts, String value) { + private void handleNumberSymbol(XPathParts parts, String value, List examples) { String symbolType = parts.getElement(-1); String numberSystem = parts.getAttributeValue(2, "numberSystem"); // null if not present int index; // dec/percent/sci @@ -2269,7 +2271,7 @@ private String handleNumberSymbol(XPathParts parts, String value) { } else { // We don't need examples for standalone symbols, i.e. infinity and nan. // We don't have an example for the list symbol either. - return null; + return; } DecimalFormat x = icuServiceBuilder.getNumberFormat(index, numberSystem); String example; @@ -2298,16 +2300,16 @@ private String handleNumberSymbol(XPathParts parts, String value) { } example = x.format(numberSample); example = example.replace(originalValue, formattedValue); - return backgroundStartSymbol + example + backgroundEndSymbol; + examples.add(backgroundStartSymbol + example + backgroundEndSymbol); } - private String handleNumberingSystem(String value) { + private void handleNumberingSystem(String value, List examples) { NumberFormat x = icuServiceBuilder.getGenericNumberFormat(value); x.setGroupingUsed(false); - return x.format(NUMBER_SAMPLE_WHOLE); + examples.add(x.format(NUMBER_SAMPLE_WHOLE)); } - private String handleTimeZoneName(XPathParts parts, String value) { + private void handleTimeZoneName(XPathParts parts, String value, List examples) { String result = null; if (parts.contains("exemplarCity")) { // ldml/dates/timeZoneNames/zone[@type="America/Los_Angeles"]/exemplarCity @@ -2319,7 +2321,8 @@ private String handleTimeZoneName(XPathParts parts, String value) { } else { result = value; // trivial -- is this beneficial? } - return result; + examples.add(result); + return; } if (countryCode.equals("001")) { // GMT code, so format. @@ -2328,7 +2331,7 @@ private String handleTimeZoneName(XPathParts parts, String value) { int hours = Integer.parseInt(hourOffset); result = getGMTFormat(null, null, hours); } catch (RuntimeException e) { - return null; // fail, skip + return; // fail, skip } } else { result = setBackground(cldrFile.getName(CLDRFile.TERRITORY_NAME, countryCode)); @@ -2408,11 +2411,12 @@ private String handleTimeZoneName(XPathParts parts, String value) { } } } - return result; + examples.add(result); } @SuppressWarnings("deprecation") - private String handleDateFormatItem(String xpath, String value, boolean showContexts) { + private void handleDateFormatItem( + String xpath, String value, boolean showContexts, List examples) { // Get here if parts contains "calendar" and either of "pattern", "dateFormatItem" String fullpath = cldrFile.getFullXPath(xpath); @@ -2459,7 +2463,7 @@ private String handleDateFormatItem(String xpath, String value, boolean showCont cldrFile.getWinningPath(xpath.replaceAll("dateTimeFormat", "timeFormat")); int tfLengthOffset = timeFormatXPathForPrefix.indexOf("timeFormatLength"); if (tfLengthOffset < 0) { - return ""; + return; } String timeFormatXPathPrefix = timeFormatXPathForPrefix.substring(0, tfLengthOffset); String timeLongerFormatXPath = @@ -2492,8 +2496,6 @@ private String handleDateFormatItem(String xpath, String value, boolean showCont "//ldml/dates/fields/field[@type=\"day\"]/relative[@type=\"0\"]"); String relativeDayValue = cldrFile.getWinningValue(relativeDayXPath); - List examples = new ArrayList<>(); - String dfResult = df.format(DATE_SAMPLE); String tlfResult = tlf.format(DATE_SAMPLE); String tsfResult = tsf.format(DATE_SAMPLE); // DATE_SAMPLE is in the afternoon @@ -2588,11 +2590,12 @@ private String handleDateFormatItem(String xpath, String value, boolean showCont examples.add(dtf.format(DATE_SAMPLE)); } - return formatExampleList(examples.toArray(new String[0])); + return; } else { String id = parts.findAttributeValue("dateFormatItem", "id"); if ("NEW".equals(id) || value == null) { - return startItalicSymbol + "n/a" + endItalicSymbol; + examples.add(startItalicSymbol + "n/a" + endItalicSymbol); + return; } else { String numbersOverride = parts.findAttributeValue("pattern", "numbers"); SimpleDateFormat sdf = @@ -2612,7 +2615,8 @@ private String handleDateFormatItem(String xpath, String value, boolean showCont // Standard date/time format, or availableFormat without dayPeriod if (value.contains("MMM") || value.contains("LLL")) { // alpha month, do not need context examples - return sdf.format(DATE_SAMPLE); + examples.add(sdf.format(DATE_SAMPLE)); + return; } else { // Use contextExamples if showContexts T String example = @@ -2622,14 +2626,13 @@ private String handleDateFormatItem(String xpath, String value, boolean showCont + exampleEndSymbol : ""; example = addExampleResult(sdf.format(DATE_SAMPLE), example, showContexts); - return example; + examples.add(example); + return; } } else { - List examples = new ArrayList<>(); examples.add(sdf.format(DATE_SAMPLE3)); examples.add(sdf.format(DATE_SAMPLE)); examples.add(sdf.format(DATE_SAMPLE4)); - return formatExampleList(examples.toArray(new String[0])); } } } @@ -2662,7 +2665,8 @@ private boolean symbolIsLetters(String currencySymbol, boolean onBothSides) { * @param value * @return */ - private String handleCurrencyFormat(XPathParts parts, String value, boolean showContexts) { + private void handleCurrencyFormat( + XPathParts parts, String value, boolean showContexts, List examples) { String example = showContexts ? exampleStartHeaderSymbol + contextheader + exampleEndSymbol : ""; @@ -2687,7 +2691,8 @@ private String handleCurrencyFormat(XPathParts parts, String value, boolean show String countValue = parts.getAttributeValue(-1, "count"); if (countValue != null) { - return formatCountDecimal(df, countValue); + examples.add(formatCountDecimal(df, countValue)); + return; } double sampleAmount = 1295.00; @@ -2711,7 +2716,7 @@ private String handleCurrencyFormat(XPathParts parts, String value, boolean show example = addExampleResult(formatNumber(df, -sampleAmount), example, showContexts); } - return example; + examples.add(example); } private String getDefaultTerritory() { @@ -2744,14 +2749,16 @@ private String getDefaultTerritory() { * @param value * @return */ - private String handleDecimalFormat(XPathParts parts, String value, boolean showContexts) { + private void handleDecimalFormat( + XPathParts parts, String value, boolean showContexts, List examples) { String example = showContexts ? exampleStartHeaderSymbol + contextheader + exampleEndSymbol : ""; String numberSystem = parts.getAttributeValue(2, "numberSystem"); // null if not present DecimalFormat numberFormat = icuServiceBuilder.getNumberFormat(value, numberSystem); String countValue = parts.getAttributeValue(-1, "count"); if (countValue != null) { - return formatCountDecimal(numberFormat, countValue); + examples.add(formatCountDecimal(numberFormat, countValue)); + return; } double sampleNum1 = 5.43; @@ -2763,7 +2770,7 @@ private String handleDecimalFormat(XPathParts parts, String value, boolean showC example = addExampleResult(formatNumber(numberFormat, sampleNum2), example, showContexts); // have positive and negative example = addExampleResult(formatNumber(numberFormat, -sampleNum2), example, showContexts); - return example; + examples.add(example); } private String formatCountDecimal(DecimalFormat numberFormat, String countValue) { @@ -2820,7 +2827,8 @@ private Double getExampleForPattern(DecimalFormat format, Count count) { return samples.get(count); } - private String handleCurrency(String xpath, XPathParts parts, String value) { + private void handleCurrency( + String xpath, XPathParts parts, String value, List examples) { String currency = parts.getAttributeValue(-2, "type"); String fullPath = cldrFile.getFullXPath(xpath, false); if (parts.contains("symbol")) { @@ -2843,22 +2851,20 @@ private String handleCurrency(String xpath, XPathParts parts, String value) { result = setBackground(result) .replace(value, backgroundEndSymbol + value + backgroundStartSymbol); - return result; + examples.add(result); } else if (parts.contains("displayName")) { - return formatCountValue(xpath, parts, value); + formatCountValue(xpath, parts, value, examples); } - return null; + return; } - private String handleDateRangePattern(String value) { - String result; + private void handleDateRangePattern(String value, List examples) { SimpleDateFormat dateFormat = icuServiceBuilder.getDateFormat("gregorian", 2, 0); - result = + examples.add( format( value, setBackground(dateFormat.format(DATE_SAMPLE)), - setBackground(dateFormat.format(DATE_SAMPLE2))); - return result; + setBackground(dateFormat.format(DATE_SAMPLE2)))); } /** @@ -2876,7 +2882,8 @@ private String getLocaleDisplayPattern(String elementToOverride, String element, } } - private String handleDisplayNames(String xpath, XPathParts parts, String value) { + private void handleDisplayNames( + String xpath, XPathParts parts, String value, List examples) { String result = null; if (parts.contains("codePatterns")) { // ldml/localeDisplayNames/codePatterns/codePattern[@type="language"] @@ -2892,6 +2899,8 @@ private String handleDisplayNames(String xpath, XPathParts parts, String value) : type.equals("script") ? "Avst" : type.equals("territory") ? "057" : "CODE")); + examples.add(result); + return; } else if (parts.contains("localeDisplayPattern")) { // ldml/localeDisplayNames/localeDisplayPattern/localePattern // ldml/localeDisplayNames/localeDisplayPattern/localeSeparator @@ -2910,18 +2919,18 @@ private String handleDisplayNames(String xpath, XPathParts parts, String value) locales.add( element.equals("localeKeyTypePattern") ? "uz-Arab-u-tz-etadd" : "uz-Arab-AF"); locales.add("uz-Arab-AF-u-tz-etadd-nu-arab"); - String[] examples = new String[locales.size()]; + // String[] examples = new String[locales.size()]; for (int i = 0; i < locales.size(); i++) { - examples[i] = + examples.add( invertBackground( cldrFile.getName( locales.get(i), false, localeKeyTypePattern, localePattern, - localeSeparator)); + localeSeparator))); } - result = formatExampleList(examples); + return; } else if (parts.contains("languages") || parts.contains("scripts") || parts.contains("territories")) { @@ -2935,9 +2944,10 @@ private String handleDisplayNames(String xpath, XPathParts parts, String value) } else { result = cldrFile.getBaileyValue(xpath, null, null); } + examples.add(result); + return; } else { value = setBackground(value); - List examples = new ArrayList<>(); String nameType = parts.getElement(3); Map likely = supplementalDataInfo.getLikelySubtags(); @@ -2948,7 +2958,7 @@ private String handleDisplayNames(String xpath, XPathParts parts, String value) String tag = "language".equals(nameType) ? type : "und_" + type; String max = LikelySubtags.maximize(tag, likely); if (max == null) { - return null; + return; } LanguageTagParser ltp = new LanguageTagParser().set(max); String languageName = null; @@ -3057,10 +3067,9 @@ private String handleDisplayNames(String xpath, XPathParts parts, String value) + "\"]"); examples.add(invertBackground(format(codePattern, value))); } - result = formatExampleList(examples.toArray(new String[0])); + return; } } - return result; } private String formatExampleList(String[] examples) { @@ -3077,7 +3086,7 @@ private String formatExampleList(String[] examples) { * @param examples * @return */ - private String formatExampleList(Collection examples) { + public String formatExampleList(Collection examples) { if (examples == null || examples.isEmpty()) { return null; } diff --git a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java index a239a63f518..8b2652c160e 100644 --- a/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java +++ b/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestExampleGenerator.java @@ -502,9 +502,10 @@ public void TestCompoundUnit() { private void checkCompoundUnits(String locale, String[][] tests) { ExampleGenerator exampleGenerator = getExampleGenerator(locale); for (String[] test : tests) { - String actual = - exampleGenerator.handleCompoundUnit( - UnitLength.valueOf(test[1]), test[0], Count.valueOf(test[2])); + List examples = new ArrayList<>(); + exampleGenerator.handleCompoundUnit( + UnitLength.valueOf(test[1]), test[0], Count.valueOf(test[2]), examples); + String actual = exampleGenerator.formatExampleList(examples); assertEquals("CompoundUnit", test[3], ExampleGenerator.simplify(actual, true)); } } @@ -564,10 +565,10 @@ public void TestCompoundUnit2() { for (String[] test : tests) { ExampleGenerator exampleGenerator = getExampleGenerator(test[0]); - - String actual = - exampleGenerator.handleCompoundUnit1( - UnitLength.valueOf(test[1]), Count.valueOf(test[2]), test[3]); + List examples = new ArrayList<>(); + exampleGenerator.handleCompoundUnit1( + UnitLength.valueOf(test[1]), Count.valueOf(test[2]), test[3], examples); + String actual = exampleGenerator.formatExampleList(examples); assertEquals("CompoundUnit", test[4], ExampleGenerator.simplify(actual, true)); } }