blob: 01861d700f220731a94f50abc313dfc654e91120 [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.ant;
import static com.google.common.base.CharMatcher.inRange;
import static com.google.common.base.CharMatcher.is;
import static com.google.common.base.CharMatcher.whitespace;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.stream.Collectors.joining;
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.unicode.cldr.api.CldrDraftStatus;
import org.unicode.icu.tool.cldrtoicu.IcuConverterConfig;
import org.unicode.icu.tool.cldrtoicu.LdmlConverter;
import org.unicode.icu.tool.cldrtoicu.LdmlConverterConfig.IcuLocaleDir;
import com.google.common.base.Ascii;
import com.google.common.base.CaseFormat;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
// Note: Auto-magical Ant methods are listed as "unused" by IDEs, unless the warning is suppressed.
public final class ConvertIcuDataTask extends Task {
private static final Splitter LIST_SPLITTER =
Splitter.on(CharMatcher.anyOf(",\n")).trimResults(whitespace()).omitEmptyStrings();
private static final CharMatcher DIGIT_OR_UNDERSCORE = inRange('0', '9').or(is('_'));
private static final CharMatcher UPPER_UNDERSCORE = inRange('A', 'Z').or(DIGIT_OR_UNDERSCORE);
private static final CharMatcher LOWER_UNDERSCORE = inRange('a', 'z').or(DIGIT_OR_UNDERSCORE);
private static final CharMatcher VALID_ENUM_CHAR = LOWER_UNDERSCORE.or(UPPER_UNDERSCORE);
private final IcuConverterConfig.Builder config = IcuConverterConfig.builder();
@SuppressWarnings("unused")
public void setOutputDir(Path path) {
config.setOutputDir(path);
}
@SuppressWarnings("unused")
public void setCldrDir(Path path) {
config.setCldrDir(path);
}
@SuppressWarnings("unused")
public void setMinimalDraftStatus(String status) {
config.setMinimalDraftStatus(resolve(CldrDraftStatus.class, status));
}
@SuppressWarnings("unused")
public void setOutputTypes(String types) {
config.setOutputTypes(
LIST_SPLITTER
.splitToList(types).stream()
.map(s -> resolve(LdmlConverter.OutputType.class, s))
.collect(toImmutableList()));
}
@SuppressWarnings("unused")
public void setSpecialsDir(Path path) {
config.setSpecialsDir(path);
}
@SuppressWarnings("unused")
public void setEmitReport(boolean emit) {
config.setEmitReport(emit);
}
public static final class LocaleIds extends Task {
private ImmutableList<IcuLocaleDir> dirs = ImmutableList.of();
private ImmutableList<String> ids = ImmutableList.of();
@SuppressWarnings("unused")
public void setDirs(String directories) {
this.dirs = LIST_SPLITTER.splitToList(directories).stream()
.map(s -> resolve(IcuLocaleDir.class, s))
.collect(toImmutableList());
}
@SuppressWarnings("unused")
public void addText(String localeIds) {
// Need to filter out '//' style end-of-line comments first (replace with \n to avoid
// inadvertantly joining two elements.
localeIds = localeIds.replaceAll("//[^\n]*\n", "\n");
this.ids = ImmutableList.copyOf(LIST_SPLITTER.splitToList(localeIds));
}
}
public static final class ForcedAlias extends Task {
private IcuLocaleDir dir;
private String source;
private String target;
@SuppressWarnings("unused")
public void setDir(String directory) {
this.dir = resolve(IcuLocaleDir.class, directory);
}
@SuppressWarnings("unused")
public void setSource(String source) {
this.source = checkNotNull(source);
}
@SuppressWarnings("unused")
public void setTarget(String target) {
this.target = checkNotNull(target);
}
}
@SuppressWarnings("unused")
public void addConfiguredLocaleIds(LocaleIds localeIds) {
localeIds.dirs.forEach(d -> config.addLocaleIds(d, localeIds.ids));
}
@SuppressWarnings("unused")
public void addConfiguredForcedAlias(ForcedAlias alias) {
config.addForcedAlias(alias.dir, alias.source, alias.target);
}
@SuppressWarnings("unused")
public void execute() throws BuildException {
LdmlConverter.convert(config.build());
}
private static <T extends Enum<T>> T resolve(Class<T> enumClass, String name) {
checkArgument(!name.isEmpty(), "enumeration name cannot be empty");
checkArgument(VALID_ENUM_CHAR.matchesAllOf(name),
"invalid enumeration name '%s'; expected only ASCII letters or '_'", name);
CaseFormat format;
if (UPPER_UNDERSCORE.matchesAllOf(name)) {
format = CaseFormat.UPPER_UNDERSCORE;
} else if (LOWER_UNDERSCORE.matchesAllOf(name)) {
format = CaseFormat.LOWER_UNDERSCORE;
} else {
// Mixed case with '_' is not permitted.
checkArgument(!name.contains("_"),
"invalid enumeration name '%s'; mixed case with underscore not allowed: %s", name);
format =
Ascii.isLowerCase(name.charAt(0)) ? CaseFormat.LOWER_CAMEL : CaseFormat.UPPER_CAMEL;
}
try {
return Enum.valueOf(enumClass, format.to(CaseFormat.UPPER_UNDERSCORE, name));
} catch (IllegalArgumentException e) {
String validNames =
Arrays.stream(enumClass.getEnumConstants())
.map(Object::toString)
.collect(joining(", "));
throw new IllegalArgumentException(
"invalid enumeration name " + name + "; expected one of; " + validNames);
}
}
}