DerivedAgeScanner.java
/*
* Copyright (C) 2013-2017 Steve Rowe <sarowe@gmail.com>
* Copyright (C) 2017 Bruno P. Kinoshita
* Copyright (C) 2017-2020 Google, LLC.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package de.jflex.ucd_generator.scanner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import de.jflex.ucd.CodepointRange;
import de.jflex.ucd.NamedCodepointRange;
import de.jflex.ucd.SurrogateUtils;
import de.jflex.ucd_generator.ucd.CodepointRangeSet;
import de.jflex.ucd_generator.ucd.MutableCodepointRange;
import de.jflex.ucd_generator.ucd.UnicodeData;
import de.jflex.version.Version;
import java.io.Reader;
import java.util.List;
import java.util.Set;
/** Scanner for the {@code DerivedAge.txt} ucd file, that populates the UnicodeData intervals. */
class DerivedAgeScanner extends EnumeratedPropertyFileScanner {
final int maximumCodepoint;
public DerivedAgeScanner(
Reader in, UnicodeData unicodeData, String defaultPropertyName, int maximumCodepoint) {
super(in, unicodeData, defaultPropertyName, "unassigned");
this.maximumCodepoint = maximumCodepoint;
}
/**
* Overridden addPropertyValueIntervals() so that any unicode version includes all intervals from
* the previous versions.
*/
@Override
public void addPropertyValueIntervals() {
super.addPropertyValueIntervals();
includeOlderVersions();
}
@Override
public void addInterval(int start, int end, String name) {
if (acceptProperty(name) && acceptInterval(start, end)) {
super.addInterval(start, end, name);
}
}
protected boolean acceptProperty(String propertyValue) {
// For age interval, the name is the version.
Version version = new Version(propertyValue);
// Only add interval for past versions.
return Version.MAJOR_MINOR_COMPARATOR.compare(version, unicodeData.version()) <= 0;
}
/**
* Returns true if the interval has expected codepoints.
*
* <p>The {@code DerivedAge.txt} may come from UNIDATA for old Unicode versions, and these
* versions (particularly these versions) may not support the range.
*/
private boolean acceptInterval(int start, int end) {
return start <= maximumCodepoint;
}
void includeOlderVersions() {
HashMultimap<Version, CodepointRange> ageRangesPerVersion = clusterCodePointRangesPerVersion();
ImmutableList<Version> versions =
ImmutableList.sortedCopyOf(Version.EXACT_VERSION_COMPARATOR, ageRangesPerVersion.keySet());
// starting from 1, as the first version is a no-op
for (int i = 1; i < versions.size(); i++) {
Version version = versions.get(i);
List<Version> prevVersions = versions.subList(0, i); // toIndex is exclusive
includeVersionsOnVersion(version, prevVersions, ageRangesPerVersion);
}
// Version up to 3.1.x use the UNIDATA DerivedAge
// addUnassignedAge(ageRangesPerVersion);
}
/**
* Gives the Unassigned Age property value to the absolute complement of the highest version's
* range set.
*/
// TODO(regisd) Remove dead code
// The jflex-unicode-maven-plugin used to do this but this not necessary with ucd_generator?
@SuppressWarnings("unused")
private void addUnassignedAge(HashMultimap<Version, CodepointRange> ageRangesPerVersion) {
Version lastVersion =
ImmutableList.sortedCopyOf(Version.EXACT_VERSION_COMPARATOR, ageRangesPerVersion.keySet())
.reverse()
.get(0);
// TODO(regisd) Assert that lastVersion == unicodeData.version()
Set<CodepointRange> highestVersionRanges = ageRangesPerVersion.get(lastVersion);
CodepointRangeSet unassigned =
CodepointRangeSet.builder()
.add(MutableCodepointRange.create(0, unicodeData.maximumCodePoint()))
.substractAll(highestVersionRanges)
.substract(SurrogateUtils.SURROGATE_RANGE)
.build();
for (CodepointRange range : unassigned.ranges()) {
unicodeData.addEnumPropertyInterval(
propertyName, defaultPropertyValue, range.start(), range.end());
}
}
private void includeVersionsOnVersion(
Version version,
List<Version> versionsToInclude,
HashMultimap<Version, CodepointRange> ageRangesPerVersion) {
for (Version v : versionsToInclude) {
for (CodepointRange range : ageRangesPerVersion.get(v)) {
ageRangesPerVersion.put(v, range);
unicodeData.addEnumPropertyInterval(
propertyName, version.toString(), range.start(), range.end());
}
}
}
HashMultimap<Version, CodepointRange> clusterCodePointRangesPerVersion() {
HashMultimap<Version, CodepointRange> ageRangesPerVersion =
HashMultimap.create(/* expectedKeys= */ 25, /* expectedValuesPerKey= */ 128);
for (NamedCodepointRange<String> interval : intervals) {
ageRangesPerVersion.put(new Version(interval.name()), interval.range());
}
return ageRangesPerVersion;
}
}