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; 4 5 import static com.google.common.base.Preconditions.checkArgument; 6 import static com.google.common.base.Preconditions.checkNotNull; 7 import static java.util.stream.Collectors.joining; 8 9 import java.nio.file.Files; 10 import java.nio.file.Path; 11 import java.nio.file.Paths; 12 import java.util.Arrays; 13 import java.util.Optional; 14 import java.util.Set; 15 16 import org.unicode.cldr.api.CldrDataSupplier; 17 import org.unicode.cldr.api.CldrDraftStatus; 18 import org.unicode.icu.tool.cldrtoicu.LdmlConverter.OutputType; 19 20 import com.google.common.collect.ImmutableMap; 21 import com.google.common.collect.ImmutableSet; 22 import com.google.common.collect.ImmutableSetMultimap; 23 import com.google.common.collect.ImmutableTable; 24 import com.google.common.collect.SetMultimap; 25 import com.google.common.collect.Sets; 26 import com.google.common.collect.Table; 27 import com.google.common.collect.TreeBasedTable; 28 import com.google.common.collect.TreeMultimap; 29 30 /** 31 * The converter config intended to generate the standard ICU data files. This used to be something 32 * that was configured by text files such as "icu-locale-deprecates.xml" and "icu-config. 33 */ 34 public final class IcuConverterConfig implements LdmlConverterConfig { 35 private static final Optional<Path> DEFAULT_ICU_DIR = 36 Optional.ofNullable(System.getProperty("ICU_DIR", null)) 37 .map(d -> Paths.get(d).toAbsolutePath()); 38 39 /** The builder with which to specify configuration for the {@link LdmlConverter}. */ 40 @SuppressWarnings("UnusedReturnValue") 41 public static final class Builder { 42 private Path outputDir = 43 DEFAULT_ICU_DIR.map(d -> d.resolve("icu4c/source/data")).orElse(null); 44 private Path specialsDir = 45 DEFAULT_ICU_DIR.map(d -> d.resolve("icu4c/source/data/xml")).orElse(null); 46 private ImmutableSet<OutputType> outputTypes = OutputType.ALL; 47 private Optional<String> icuVersion = Optional.empty(); 48 private Optional<String> icuDataVersion = Optional.empty(); 49 private Optional<String> cldrVersion = Optional.empty(); 50 private CldrDraftStatus minimumDraftStatus = CldrDraftStatus.CONTRIBUTED; 51 private boolean emitReport = false; 52 private final SetMultimap<IcuLocaleDir, String> localeIdsMap = TreeMultimap.create(); 53 private final Table<IcuLocaleDir, String, String> forcedAliases = TreeBasedTable.create(); 54 private final Table<IcuLocaleDir, String, String> forcedParents = TreeBasedTable.create(); 55 56 /** 57 * Sets the output directory in which the ICU data directories and files will go. This is 58 * optional if the {@code ICU_DIR} system property is set, which will be used to generate 59 * the path instead (i.e. {@code "icu4c/source/data"} inside the ICU release directory). 60 */ setOutputDir(Path outputDir)61 public Builder setOutputDir(Path outputDir) { 62 this.outputDir = checkNotNull(outputDir); 63 return this; 64 } 65 66 /** 67 * Sets the "specials" directory containing additional ICU specific data to be processed. 68 * This is optional if the {@code ICU_DIR} system property is set, which will be used to 69 * generate the path instead (i.e. {@code "icu4c/source/data/xml"} inside the ICU release 70 * directory). 71 */ setSpecialsDir(Path specialsDir)72 public Builder setSpecialsDir(Path specialsDir) { 73 this.specialsDir = checkNotNull(specialsDir); 74 return this; 75 } 76 77 /** 78 * Sets the output types which will be converted. This is optional and defaults to {@link 79 * OutputType#ALL}. 80 */ setOutputTypes(Iterable<OutputType> types)81 public Builder setOutputTypes(Iterable<OutputType> types) { 82 this.outputTypes = ImmutableSet.copyOf(types); 83 return this; 84 } 85 setIcuVersion(String version)86 public Builder setIcuVersion(String version) { 87 if (!version.isEmpty()) { 88 this.icuVersion = Optional.of(version); 89 } 90 return this; 91 } 92 setIcuDataVersion(String version)93 public Builder setIcuDataVersion(String version) { 94 if (!version.isEmpty()) { 95 this.icuDataVersion = Optional.of(version); 96 } 97 return this; 98 } 99 setCldrVersion(String version)100 public Builder setCldrVersion(String version) { 101 if (!version.isEmpty()) { 102 this.cldrVersion = Optional.of(version); 103 } 104 return this; 105 } 106 setMinimumDraftStatus(CldrDraftStatus minimumDraftStatus)107 public void setMinimumDraftStatus(CldrDraftStatus minimumDraftStatus) { 108 this.minimumDraftStatus = checkNotNull(minimumDraftStatus); 109 } 110 setEmitReport(boolean emitReport)111 public Builder setEmitReport(boolean emitReport) { 112 this.emitReport = emitReport; 113 return this; 114 } 115 addLocaleIds(IcuLocaleDir dir, Iterable<String> localeIds)116 public Builder addLocaleIds(IcuLocaleDir dir, Iterable<String> localeIds) { 117 localeIdsMap.putAll(dir, localeIds); 118 return this; 119 } 120 addForcedAlias(IcuLocaleDir dir, String source, String target)121 public Builder addForcedAlias(IcuLocaleDir dir, String source, String target) { 122 forcedAliases.put(dir, source, target); 123 return this; 124 } 125 addForcedParent(IcuLocaleDir dir, String localeId, String parent)126 public Builder addForcedParent(IcuLocaleDir dir, String localeId, String parent) { 127 forcedParents.put(dir, localeId, parent); 128 return this; 129 } 130 131 /** Returns a converter config from the current builder state. */ build()132 public LdmlConverterConfig build() { 133 return new IcuConverterConfig(this); 134 } 135 } 136 137 private final Path outputDir; 138 private final Path specialsDir; 139 private final ImmutableSet<OutputType> outputTypes; 140 private final IcuVersionInfo versionInfo; 141 private final CldrDraftStatus minimumDraftStatus; 142 private final boolean emitReport; 143 private final ImmutableSet<String> allLocaleIds; 144 private final ImmutableSetMultimap<IcuLocaleDir, String> localeIdsMap; 145 private final ImmutableTable<IcuLocaleDir, String, String> forcedAliases; 146 private final ImmutableTable<IcuLocaleDir, String, String> forcedParents; 147 IcuConverterConfig(Builder builder)148 private IcuConverterConfig(Builder builder) { 149 this.outputDir = checkNotNull(builder.outputDir); 150 checkArgument(!Files.isRegularFile(outputDir), 151 "specified output directory if not a directory: %s", outputDir); 152 this.specialsDir = checkNotNull(builder.specialsDir, 153 "must specify a 'specials' XML directory"); 154 checkArgument(Files.isDirectory(specialsDir), 155 "specified specials directory does not exist: %s", specialsDir); 156 this.outputTypes = builder.outputTypes; 157 checkArgument(!this.outputTypes.isEmpty(), 158 "must specify at least one output type to be generated (possible values are: %s)", 159 Arrays.asList(OutputType.values())); 160 this.versionInfo = new IcuVersionInfo( 161 builder.icuVersion.orElseThrow(() -> new IllegalStateException("missing ICU version")), 162 builder.icuDataVersion.orElseThrow(() -> new IllegalStateException("missing ICU data version")), 163 builder.cldrVersion.orElse(CldrDataSupplier.getCldrVersionString())); 164 this.minimumDraftStatus = checkNotNull(builder.minimumDraftStatus); 165 this.emitReport = builder.emitReport; 166 // getAllLocaleIds() returns the union of all the specified IDs in the map. 167 this.allLocaleIds = ImmutableSet.copyOf(builder.localeIdsMap.values()); 168 this.localeIdsMap = ImmutableSetMultimap.copyOf(builder.localeIdsMap); 169 this.forcedAliases = ImmutableTable.copyOf(builder.forcedAliases); 170 this.forcedParents = ImmutableTable.copyOf(builder.forcedParents); 171 } 172 builder()173 public static Builder builder() { 174 return new Builder(); 175 } 176 177 @Override getOutputDir()178 public Path getOutputDir() { 179 return outputDir; 180 } 181 182 @Override getOutputTypes()183 public Set<OutputType> getOutputTypes() { 184 return outputTypes; 185 } 186 187 @Override getSpecialsDir()188 public Path getSpecialsDir() { 189 return specialsDir; 190 } 191 192 @Override getVersionInfo()193 public IcuVersionInfo getVersionInfo() { 194 return versionInfo; 195 } 196 197 @Override getMinimumDraftStatus()198 public CldrDraftStatus getMinimumDraftStatus() { 199 return minimumDraftStatus; 200 } 201 202 @Override emitReport()203 public boolean emitReport() { 204 return emitReport; 205 } 206 207 @Override getForcedAliases(IcuLocaleDir dir)208 public ImmutableMap<String, String> getForcedAliases(IcuLocaleDir dir) { 209 return forcedAliases.row(dir); 210 } 211 212 @Override getForcedParents(IcuLocaleDir dir)213 public ImmutableMap<String, String> getForcedParents(IcuLocaleDir dir) { 214 return forcedParents.row(dir); 215 } 216 getAllLocaleIds()217 @Override public ImmutableSet<String> getAllLocaleIds() { 218 return allLocaleIds; 219 } 220 getTargetLocaleIds(IcuLocaleDir dir)221 @Override public ImmutableSet<String> getTargetLocaleIds(IcuLocaleDir dir) { 222 return localeIdsMap.get(dir); 223 } 224 } 225