blob: a99ba7063a3185e8affd596b256d116bd46bff5e [file] [log] [blame]
// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package org.unicode.icu.tool.cldrtoicu;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import org.unicode.cldr.api.CldrDataSupplier;
import org.unicode.cldr.api.CldrDraftStatus;
import org.unicode.icu.tool.cldrtoicu.LdmlConverter.OutputType;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.TreeMultimap;
/**
* The converter config intended to generate the standard ICU data files. This used to be something
* that was configured by text files such as "icu-locale-deprecates.xml" and "icu-config.
*/
public final class IcuConverterConfig implements LdmlConverterConfig {
private static final Optional<Path> DEFAULT_ICU_DIR =
Optional.ofNullable(System.getProperty("ICU_DIR", null))
.map(d -> Paths.get(d).toAbsolutePath());
/** The builder with which to specify configuration for the {@link LdmlConverter}. */
@SuppressWarnings("UnusedReturnValue")
public static final class Builder {
private Path outputDir =
DEFAULT_ICU_DIR.map(d -> d.resolve("icu4c/source/data")).orElse(null);
private Path specialsDir =
DEFAULT_ICU_DIR.map(d -> d.resolve("icu4c/source/data/xml")).orElse(null);
private ImmutableSet<OutputType> outputTypes = OutputType.ALL;
private Optional<String> icuVersion = Optional.empty();
private Optional<String> icuDataVersion = Optional.empty();
private Optional<String> cldrVersion = Optional.empty();
private CldrDraftStatus minimumDraftStatus = CldrDraftStatus.CONTRIBUTED;
private boolean emitReport = false;
private final SetMultimap<IcuLocaleDir, String> localeIdsMap = TreeMultimap.create();
private final Table<IcuLocaleDir, String, String> forcedAliases = TreeBasedTable.create();
private final Table<IcuLocaleDir, String, String> forcedParents = TreeBasedTable.create();
/**
* Sets the output directory in which the ICU data directories and files will go. This is
* optional if the {@code ICU_DIR} system property is set, which will be used to generate
* the path instead (i.e. {@code "icu4c/source/data"} inside the ICU release directory).
*/
public Builder setOutputDir(Path outputDir) {
this.outputDir = checkNotNull(outputDir);
return this;
}
/**
* Sets the "specials" directory containing additional ICU specific data to be processed.
* This is optional if the {@code ICU_DIR} system property is set, which will be used to
* generate the path instead (i.e. {@code "icu4c/source/data/xml"} inside the ICU release
* directory).
*/
public Builder setSpecialsDir(Path specialsDir) {
this.specialsDir = checkNotNull(specialsDir);
return this;
}
/**
* Sets the output types which will be converted. This is optional and defaults to {@link
* OutputType#ALL}.
*/
public Builder setOutputTypes(Iterable<OutputType> types) {
this.outputTypes = ImmutableSet.copyOf(types);
return this;
}
public Builder setIcuVersion(String version) {
if (!version.isEmpty()) {
this.icuVersion = Optional.of(version);
}
return this;
}
public Builder setIcuDataVersion(String version) {
if (!version.isEmpty()) {
this.icuDataVersion = Optional.of(version);
}
return this;
}
public Builder setCldrVersion(String version) {
if (!version.isEmpty()) {
this.cldrVersion = Optional.of(version);
}
return this;
}
public void setMinimumDraftStatus(CldrDraftStatus minimumDraftStatus) {
this.minimumDraftStatus = checkNotNull(minimumDraftStatus);
}
public Builder setEmitReport(boolean emitReport) {
this.emitReport = emitReport;
return this;
}
public Builder addLocaleIds(IcuLocaleDir dir, Iterable<String> localeIds) {
localeIdsMap.putAll(dir, localeIds);
return this;
}
public Builder addForcedAlias(IcuLocaleDir dir, String source, String target) {
forcedAliases.put(dir, source, target);
return this;
}
public Builder addForcedParent(IcuLocaleDir dir, String localeId, String parent) {
forcedParents.put(dir, localeId, parent);
return this;
}
/** Returns a converter config from the current builder state. */
public LdmlConverterConfig build() {
return new IcuConverterConfig(this);
}
}
private final Path outputDir;
private final Path specialsDir;
private final ImmutableSet<OutputType> outputTypes;
private final IcuVersionInfo versionInfo;
private final CldrDraftStatus minimumDraftStatus;
private final boolean emitReport;
private final ImmutableSet<String> allLocaleIds;
private final ImmutableSetMultimap<IcuLocaleDir, String> localeIdsMap;
private final ImmutableTable<IcuLocaleDir, String, String> forcedAliases;
private final ImmutableTable<IcuLocaleDir, String, String> forcedParents;
private IcuConverterConfig(Builder builder) {
this.outputDir = checkNotNull(builder.outputDir);
checkArgument(!Files.isRegularFile(outputDir),
"specified output directory if not a directory: %s", outputDir);
this.specialsDir = checkNotNull(builder.specialsDir,
"must specify a 'specials' XML directory");
checkArgument(Files.isDirectory(specialsDir),
"specified specials directory does not exist: %s", specialsDir);
this.outputTypes = builder.outputTypes;
checkArgument(!this.outputTypes.isEmpty(),
"must specify at least one output type to be generated (possible values are: %s)",
Arrays.asList(OutputType.values()));
this.versionInfo = new IcuVersionInfo(
builder.icuVersion.orElseThrow(() -> new IllegalStateException("missing ICU version")),
builder.icuDataVersion.orElseThrow(() -> new IllegalStateException("missing ICU data version")),
builder.cldrVersion.orElse(CldrDataSupplier.getCldrVersionString()));
this.minimumDraftStatus = checkNotNull(builder.minimumDraftStatus);
this.emitReport = builder.emitReport;
// getAllLocaleIds() returns the union of all the specified IDs in the map.
this.allLocaleIds = ImmutableSet.copyOf(builder.localeIdsMap.values());
this.localeIdsMap = ImmutableSetMultimap.copyOf(builder.localeIdsMap);
this.forcedAliases = ImmutableTable.copyOf(builder.forcedAliases);
this.forcedParents = ImmutableTable.copyOf(builder.forcedParents);
}
public static Builder builder() {
return new Builder();
}
@Override
public Path getOutputDir() {
return outputDir;
}
@Override
public Set<OutputType> getOutputTypes() {
return outputTypes;
}
@Override
public Path getSpecialsDir() {
return specialsDir;
}
@Override
public IcuVersionInfo getVersionInfo() {
return versionInfo;
}
@Override
public CldrDraftStatus getMinimumDraftStatus() {
return minimumDraftStatus;
}
@Override
public boolean emitReport() {
return emitReport;
}
@Override
public ImmutableMap<String, String> getForcedAliases(IcuLocaleDir dir) {
return forcedAliases.row(dir);
}
@Override
public ImmutableMap<String, String> getForcedParents(IcuLocaleDir dir) {
return forcedParents.row(dir);
}
@Override public ImmutableSet<String> getAllLocaleIds() {
return allLocaleIds;
}
@Override public ImmutableSet<String> getTargetLocaleIds(IcuLocaleDir dir) {
return localeIdsMap.get(dir);
}
}