UnicodeVersionEmitter.java
/*
* Copyright (C) 2019-2020 Google, LLC.
* SPDX-License-Identifier: BSD-3-Clause
*/
package de.jflex.ucd_generator.emitter.unicode_version;
import static de.jflex.ucd_generator.util.JavaStrings.escapedUTF16Char;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Maps.EntryTransformer;
import de.jflex.ucd.CodepointRange;
import de.jflex.ucd.UcdVersion;
import de.jflex.ucd_generator.emitter.common.UcdEmitter;
import de.jflex.ucd_generator.ucd.CodepointRangeSet;
import de.jflex.ucd_generator.ucd.UnicodeData;
import de.jflex.ucd_generator.util.JavaStrings;
import de.jflex.util.javac.JavaPackageUtils;
import de.jflex.velocity.Velocity;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import org.apache.velocity.runtime.parser.ParseException;
/** Emitter for a {@code Unicode_x_y.java}. */
public class UnicodeVersionEmitter extends UcdEmitter {
private static final String TEMPLATE_NAME = "Unicode_x_y.java";
private static final String UNICODE_VERSION_TEMPLATE =
JavaPackageUtils.getPathForClass(UnicodeVersionEmitter.class) + "/" + TEMPLATE_NAME + ".vm";
private static final String CP_ZERO = escapedUTF16Char(0);
private final UcdVersion ucdVersion;
private final UnicodeData unicodeData;
public UnicodeVersionEmitter(String packageName, UcdVersion ucdVersion, UnicodeData unicodeData) {
super(packageName);
this.ucdVersion = ucdVersion;
this.unicodeData = unicodeData;
}
public void emitUnicodeVersion(OutputStream output) throws IOException, ParseException {
UnicodeVersionVars unicodeVersionVars = createUnicodeVersionVars();
Velocity.render(
readResource(UNICODE_VERSION_TEMPLATE), TEMPLATE_NAME, unicodeVersionVars, output);
}
private UnicodeVersionVars createUnicodeVersionVars() {
UnicodeVersionVars unicodeVersionVars = new UnicodeVersionVars();
unicodeVersionVars.templateName = TEMPLATE_NAME;
unicodeVersionVars.packageName = getTargetPackage();
unicodeVersionVars.className = ucdVersion.version().unicodeClassName();
unicodeVersionVars.maxCodePoint = unicodeData.maximumCodePoint();
ImmutableSortedMap<String, CodepointRangeSet> intervals = unicodeData.intervals();
unicodeVersionVars.propertyValues =
ImmutableList.copyOf(intervals.keySet()).stream()
.map(v -> String.format("\"%s\"", v))
.collect(joining(",\n "));
unicodeVersionVars.intervals = String.join(",\n ", intervalsToCodesource(intervals));
unicodeVersionVars.propertyValueAliases =
unicodeData.usedPropertyValueAliases().stream()
.map(e -> String.format("\"%s\", \"%s\"", e.getKey(), e.getValue()))
.collect(joining(",\n "));
unicodeVersionVars.maxCaselessMatchPartitionSize = unicodeData.maxCaselessMatchPartitionSize();
unicodeVersionVars.caselessMatchPartitions =
unicodeData.uniqueCaselessMatchPartitions().stream()
.map(
partition ->
partitionToCodesource(
partition, unicodeVersionVars.maxCaselessMatchPartitionSize))
.collect(joining("\"\n + \""));
return unicodeVersionVars;
}
private Collection<String> intervalsToCodesource(Map<String, CodepointRangeSet> intervals) {
EntryTransformer<String, CodepointRangeSet, String> function =
new EntryTransformer<String, CodepointRangeSet, String>() {
@Override
public String transformEntry(String propertyValue, CodepointRangeSet rangeSet) {
String codeComment =
String.format(
"// Unicode %s property value: {%s}\n",
ucdVersion.version().toMajorMinorString(), propertyValue);
String value =
rangeSet.ranges().stream()
.map(this::intervalToCodesource)
.collect(joining("\n + "));
return codeComment + " " + value;
}
private String intervalToCodesource(CodepointRange interval) {
return "\""
+ escapedUTF16Char(interval.start())
+ escapedUTF16Char(interval.end())
+ "\"";
}
};
return Maps.transformEntries(intervals, function).values();
}
private static String partitionToCodesource(
SortedSet<Integer> partition, int caselessMatchPartitionSize) {
StringBuilder sb = new StringBuilder();
Iterator<String> escapedChars =
partition.stream().map(JavaStrings::escapedUTF16Char).iterator();
while (escapedChars.hasNext()) {
sb.append(escapedChars.next());
}
// Add \u0000 placeholders to fill out the fixed record size
for (int i = 0; i < caselessMatchPartitionSize - partition.size(); ++i) {
sb.append(CP_ZERO);
}
return sb.toString();
}
}