1 // © 2019 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 package org.unicode.icu.tool.cldrtoicu.mapper; 4 5 import static org.unicode.cldr.api.AttributeKey.keyOf; 6 import static org.unicode.cldr.api.CldrData.PathOrder.NESTED_GROUPING; 7 import static org.unicode.cldr.api.CldrDataType.SUPPLEMENTAL; 8 9 import org.unicode.cldr.api.AttributeKey; 10 import org.unicode.cldr.api.CldrData; 11 import org.unicode.cldr.api.CldrDataSupplier; 12 import org.unicode.cldr.api.CldrDataType; 13 import org.unicode.cldr.api.CldrPath; 14 import org.unicode.cldr.api.CldrValue; 15 import org.unicode.icu.tool.cldrtoicu.IcuData; 16 import org.unicode.icu.tool.cldrtoicu.RbPath; 17 import org.unicode.icu.tool.cldrtoicu.RbValue; 18 import org.unicode.icu.tool.cldrtoicu.CldrDataProcessor; 19 20 import com.google.common.annotations.VisibleForTesting; 21 22 /** 23 * A mapper to collect plural data from {@link CldrDataType#SUPPLEMENTAL SUPPLEMENTAL} data via 24 * the paths: 25 * <pre>{@code 26 * //supplementalData/plurals/pluralRanges[@locales=*]/... 27 * }</pre> 28 */ 29 public final class PluralRangesMapper { 30 31 private static final CldrDataProcessor<PluralRangesMapper> CLDR_PROCESSOR; 32 static { 33 CldrDataProcessor.Builder<PluralRangesMapper> processor = CldrDataProcessor.builder(); 34 processor 35 .addAction( 36 "//supplementalData/plurals/pluralRanges[@locales=*]", (m, p) -> m.new Ranges(p)) 37 .addValueAction("pluralRange[@start=*][@end=*]", Ranges::visitRange); 38 CLDR_PROCESSOR = processor.build(); 39 } 40 41 private static final AttributeKey RANGES_LOCALES = keyOf("pluralRanges", "locales"); 42 private static final AttributeKey RANGE_START = keyOf("pluralRange", "start"); 43 private static final AttributeKey RANGE_END = keyOf("pluralRange", "end"); 44 private static final AttributeKey RANGE_RESULT = keyOf("pluralRange", "result"); 45 46 private static final RbPath RB_RULES = RbPath.of("rules"); 47 private static final RbPath RB_LOCALES = RbPath.of("locales"); 48 49 /** 50 * Processes data from the given supplier to generate plural-range ICU data. 51 * 52 * @param src the CLDR data supplier to process. 53 * @return the IcuData instance to be written to a file. 54 */ process(CldrDataSupplier src)55 public static IcuData process(CldrDataSupplier src) { 56 return process(src.getDataForType(SUPPLEMENTAL)); 57 } 58 59 @VisibleForTesting // It's easier to supply a fake data instance than a fake supplier. process(CldrData data)60 static IcuData process(CldrData data) { 61 return CLDR_PROCESSOR.process(data, new PluralRangesMapper(), NESTED_GROUPING).icuData; 62 } 63 64 private final IcuData icuData = new IcuData("pluralRanges", false); 65 private int setIndex = 0; 66 PluralRangesMapper()67 private PluralRangesMapper() { } 68 69 private final class Ranges { 70 private final String label; 71 Ranges(CldrPath prefix)72 Ranges(CldrPath prefix) { 73 this.label = String.format("set%02d", setIndex++); 74 RANGES_LOCALES.listOfValuesFrom(prefix) 75 .forEach(l -> icuData.add(RB_LOCALES.extendBy(l), label)); 76 } 77 visitRange(CldrValue value)78 private void visitRange(CldrValue value) { 79 // Note: "range:start" and "range:end" are optional attributes, but the CLDR DTD 80 // specifies a default via comments. They should probably be changed to just have a 81 // default in the DTD (and possibly converted to use an enum here). 82 icuData.add(RB_RULES.extendBy(label), 83 RbValue.of( 84 RANGE_START.valueFrom(value, "all"), 85 RANGE_END.valueFrom(value, "all"), 86 RANGE_RESULT.valueFrom(value))); 87 } 88 } 89 }