Last active
July 7, 2021 21:49
-
-
Save soc/fb71876be910fbc52233fb34077855fe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.geotools.data.util; | |
import static javax.measure.MetricPrefix.*; | |
import static org.junit.Assert.assertEquals; | |
import java.lang.reflect.Field; | |
import java.util.Collection; | |
import java.util.Comparator; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import javax.measure.MetricPrefix; | |
import javax.measure.Unit; | |
import javax.measure.format.UnitFormat; | |
import org.junit.Test; | |
import tech.units.indriya.AbstractUnit; | |
import tech.units.indriya.format.SimpleUnitFormat; | |
import tech.units.indriya.unit.Units; | |
public class UnitFormatterTest { | |
@Test | |
public void unitsOnlyInOld() throws Exception { | |
SimpleUnitFormat simpleUnitFormat = SimpleUnitFormat.getInstance(); | |
List<Map.Entry<Unit<?>, String>> unitToName = toSortedList1(getUnitToNameMap(simpleUnitFormat)); | |
List<Map.Entry<Unit<?>, String>> unitToSymbol = toSortedList1(formatter.getUnitToSymbol()); | |
// indriya (2.0.2) has an inconsistent mix of μ and µ; this doesn't. | |
/* | |
List<Map.Entry<Unit<?>, String>> olds181 = unitToName.stream().filter(unit -> unit.getValue().charAt(0) == 181).collect(Collectors.toList()); | |
List<Map.Entry<Unit<?>, String>> olds956 = unitToName.stream().filter(unit -> unit.getValue().charAt(0) == 956).collect(Collectors.toList()); | |
List<Map.Entry<Unit<?>, String>> news181 = unitToSymbol.stream().filter(unit -> unit.getValue().charAt(0) == 181).collect(Collectors.toList()); | |
List<Map.Entry<Unit<?>, String>> news956 = unitToSymbol.stream().filter(unit -> unit.getValue().charAt(0) == 956).collect(Collectors.toList()); | |
System.out.println("old 181: " + olds181); | |
System.out.println("new 181: " + news181); | |
System.out.println("old 956: " + olds956); | |
System.out.println("new 956: " + news956); | |
*/ | |
List<Map.Entry<Unit<?>, String>> unitsOnlyInOld = unitToName.stream().filter(entry -> !unitToSymbol.contains(entry)).collect(Collectors.toList()); | |
List<Map.Entry<? extends Unit<?>, String>> indriyaBug = List.of( | |
Map.entry(Units.GRAM.prefix(MICRO), "µg"), | |
Map.entry(Units.LITRE.prefix(MICRO), "µl"), | |
Map.entry(Units.CELSIUS.prefix(MICRO), "µ℃")); | |
assertEquals(unitsOnlyInOld.size() + " units only in old: " + unitsOnlyInOld + "\n", indriyaBug, unitsOnlyInOld); | |
} | |
@Test | |
public void unitsOnlyInNew() throws Exception { | |
SimpleUnitFormat simpleUnitFormat = SimpleUnitFormat.getInstance(); | |
List<Map.Entry<Unit<?>, String>> unitToNameMap = toSortedList1(getUnitToNameMap(simpleUnitFormat)); | |
List<Map.Entry<Unit<?>, String>> unitToSymbol = toSortedList1(formatter.getUnitToSymbol()); | |
List<Map.Entry<Unit<?>, String>> unitsOnlyInNew = unitToSymbol.stream().filter(entry -> !unitToNameMap.contains(entry)).collect(Collectors.toList()); | |
List<Map.Entry<? extends Unit<?>, String>> indriyaBug = List.of( | |
Map.entry(Units.GRAM.prefix(MICRO), "μg"), | |
Map.entry(Units.LITRE.prefix(MICRO), "μl"), | |
Map.entry(Units.CELSIUS.prefix(MICRO), "μ℃")); | |
assertEquals(unitsOnlyInNew.size() + " units only in new: " + unitsOnlyInNew + "\n", indriyaBug, unitsOnlyInNew); | |
} | |
@Test | |
public void namesOnlyInOld() throws Exception { | |
SimpleUnitFormat simpleUnitFormat = SimpleUnitFormat.getInstance(); | |
List<Map.Entry<String, Unit<?>>> nameToUnitMap = toSortedList2(getNameToUnitMap(simpleUnitFormat)); | |
List<Map.Entry<String, Unit<?>>> symbolToUnit = toSortedList2(formatter.getSymbolToUnit()); | |
List<Map.Entry<String, Unit<?>>> unitsOnlyInOld = nameToUnitMap.stream().filter(entry -> !symbolToUnit.contains(entry)).collect(Collectors.toList()); | |
assertEquals(unitsOnlyInOld.size() + " names only in old: " + unitsOnlyInOld + "\n", List.of(), unitsOnlyInOld); | |
} | |
@Test | |
public void namesOnlyInNew() throws Exception { | |
SimpleUnitFormat simpleUnitFormat = SimpleUnitFormat.getInstance(); | |
List<Map.Entry<String, Unit<?>>> nameToUnitMap = toSortedList2(getNameToUnitMap(simpleUnitFormat)); | |
List<Map.Entry<String, Unit<?>>> symbolToUnit = toSortedList2(formatter.getSymbolToUnit()); | |
List<Map.Entry<String, Unit<?>>> unitsOnlyInNew = symbolToUnit.stream().filter(entry -> !nameToUnitMap.contains(entry)).collect(Collectors.toList()); | |
// only one kind of µ is added for those special-cased units in indriya: | |
List<Map.Entry<String, Unit<?>>> indriyaBug = List.of( | |
Map.entry("μg", Units.GRAM.prefix(MICRO)), | |
Map.entry("μl", Units.LITRE.prefix(MICRO)), | |
Map.entry("μ℃", Units.CELSIUS.prefix(MICRO)), | |
Map.entry("μ°C", Units.CELSIUS.prefix(MICRO)), | |
Map.entry("μOhm", Units.OHM.prefix(MICRO))); | |
assertEquals(unitsOnlyInNew.size() + " names only in new: " + unitsOnlyInNew + "\n", indriyaBug, unitsOnlyInNew); | |
} | |
@SuppressWarnings("unchecked") // reflection in use, cannot be type safe | |
private static HashMap<Unit<?>, String> getUnitToNameMap(UnitFormat instance) throws Exception { | |
Field unitToNameField = instance.getClass().getDeclaredField("unitToName"); | |
unitToNameField.setAccessible(true); | |
return (HashMap<Unit<?>, String>) unitToNameField.get(instance); | |
} | |
@SuppressWarnings("unchecked") // reflection in use, cannot be type safe | |
private static HashMap<String, Unit<?>> getNameToUnitMap(UnitFormat instance) throws Exception { | |
Field nameToUnitField = instance.getClass().getDeclaredField("nameToUnit"); | |
nameToUnitField.setAccessible(true); | |
return (HashMap<String, Unit<?>>) nameToUnitField.get(instance); | |
} | |
private static List<Map.Entry<Unit<?>, String>> toSortedList1(HashMap<Unit<?>, String> map) { | |
return map.entrySet().stream() | |
.sorted(Comparator.nullsFirst(Comparator.comparing(x -> x.getKey().toString()))) | |
.collect(Collectors.toList()); | |
} | |
private static List<Map.Entry<String, Unit<?>>> toSortedList2(HashMap<String, Unit<?>> map) { | |
return map.entrySet().stream() | |
.sorted(Comparator.nullsFirst(Comparator.comparing(x -> x.getValue().toString()))) | |
.collect(Collectors.toList()); | |
} | |
private static final UnitFormatter formatter = UnitFormatter.of(Stream.of( | |
UnitDefinitions.DIMENSIONLESS, | |
UnitDefinitions.BASE, | |
UnitDefinitions.DERIVED) | |
.flatMap(Collection::stream) | |
.collect(Collectors.toList())); | |
} | |
class UnitFormatter { | |
/** associates units with their symbols */ | |
private final HashMap<Unit<?>, String> unitToSymbol = new HashMap<>(); | |
/** associates symbols with their units */ | |
private final HashMap<String, Unit<?>> symbolToUnit = new HashMap<>(); | |
public static UnitFormatter of(UnitDefinition... unitDefinitions) { | |
return new UnitFormatter(List.of(unitDefinitions)); | |
} | |
public static UnitFormatter of(List<UnitDefinition> unitDefinitions) { | |
return new UnitFormatter(unitDefinitions); | |
} | |
// use this as a formatter/to initialize a formatter (like SimpleUnitFormat) | |
private UnitFormatter(List<UnitDefinition> unitDefinitions) { | |
for (UnitDefinition unitDefinition : unitDefinitions) { | |
Unit<?> unit = unitDefinition.getUnit(); | |
String unitSymbol = unitDefinition.getSymbolOverride() != null ? unitDefinition.getSymbolOverride() : unit.getSymbol(); | |
// add units | |
this.unitToSymbol.put(unit, unitSymbol); | |
this.symbolToUnit.put(unitSymbol, unit); | |
for (PrefixDefinition prefix : unitDefinition.getPrefixes()) { | |
addUnit(unit, unitSymbol, prefix); | |
} | |
// add labels | |
for(String alias : unitDefinition.getAliases()) { | |
symbolToUnit.put(alias, unit); | |
} | |
for (PrefixDefinition prefix : unitDefinition.getPrefixes()) { | |
for(String alias : unitDefinition.getAliases()) { | |
addAlias(unit, alias, prefix); | |
} | |
} | |
} | |
} | |
private void addUnit(Unit<?> unit, String unitSymbol, PrefixDefinition prefix) { | |
Unit<?> prefixedUnit = unit.prefix(prefix.getPrefix()); | |
String prefixString = prefix.getPrefixOverride() != null ? prefix.getPrefixOverride() : prefix.getPrefix().getSymbol(); | |
String prefixedSymbol = prefixString + unitSymbol; | |
this.unitToSymbol.put(prefixedUnit, prefixedSymbol); | |
this.symbolToUnit.put(prefixedSymbol, prefixedUnit); | |
for (String prefixAlias : prefix.getPrefixAliases()) { | |
this.unitToSymbol.put(prefixedUnit, prefixAlias + unitSymbol); | |
this.symbolToUnit.put(prefixAlias + unitSymbol, prefixedUnit); | |
} | |
} | |
private void addAlias(Unit<?> unit, String unitSymbol, PrefixDefinition prefix) { | |
Unit<?> prefixedUnit = unit.prefix(prefix.getPrefix()); | |
String prefixString = prefix.getPrefixOverride() != null ? prefix.getPrefixOverride() : prefix.getPrefix().getSymbol(); | |
String prefixedSymbol = prefixString + unitSymbol; | |
this.symbolToUnit.put(prefixedSymbol, prefixedUnit); | |
for (String prefixAlias : prefix.getPrefixAliases()) { | |
this.symbolToUnit.put(prefixAlias + unitSymbol, prefixedUnit); | |
} | |
} | |
public HashMap<Unit<?>, String> getUnitToSymbol() { | |
return unitToSymbol; | |
} | |
public HashMap<String, Unit<?>> getSymbolToUnit() { | |
return symbolToUnit; | |
} | |
} | |
class UnitDefinitions { | |
public static List<UnitDefinition> DIMENSIONLESS = List.of( | |
UnitDefinition.of(AbstractUnit.ONE, List.of(), "one", List.of()), | |
UnitDefinition.of(Units.PERCENT, List.of(), "%", List.of())); | |
public static List<UnitDefinition> BASE = List.of( | |
UnitDefinition.withStandardPrefixes(Units.AMPERE), | |
UnitDefinition.withStandardPrefixes(Units.CANDELA), | |
UnitDefinition.withStandardPrefixes(Units.KELVIN), | |
UnitDefinition.of(Units.KILOGRAM, List.of(), null, List.of()), | |
UnitDefinition.withStandardPrefixes(Units.METRE), | |
UnitDefinition.withStandardPrefixes(Units.MOLE), | |
UnitDefinition.withStandardPrefixes(Units.SECOND)); | |
public static List<UnitDefinition> DERIVED = List.of( | |
UnitDefinition.withStandardPrefixes(Units.BECQUEREL), | |
UnitDefinition.of(Units.CELSIUS, PrefixDefinitions.STANDARD, "℃", List.of("°C")), | |
UnitDefinition.withStandardPrefixes(Units.COULOMB), | |
// "m3" was added later, as well as (Units.SQUARE_METRE, "m2"), but not m² | |
UnitDefinition.of(Units.CUBIC_METRE, List.of(), "㎥", List.of()), | |
UnitDefinition.of(Units.DAY, List.of(), "day", List.of("d")), | |
UnitDefinition.withStandardPrefixes(Units.FARAD), | |
UnitDefinition.withPrefixesAndSymbolOverride(Units.GRAM, PrefixDefinitions.GRAM, "g"), | |
UnitDefinition.withStandardPrefixes(Units.GRAY), | |
UnitDefinition.withStandardPrefixes(Units.HENRY), | |
UnitDefinition.withStandardPrefixes(Units.HERTZ), | |
UnitDefinition.of(Units.HOUR, List.of(), "h", List.of()), | |
UnitDefinition.withStandardPrefixes(Units.JOULE), | |
UnitDefinition.withStandardPrefixes(Units.KATAL), | |
UnitDefinition.of(Units.KILOMETRE_PER_HOUR, List.of(), "km/h", List.of()), | |
UnitDefinition.withStandardPrefixes(Units.LITRE), | |
UnitDefinition.withStandardPrefixes(Units.LUMEN), | |
UnitDefinition.withStandardPrefixes(Units.LUX), | |
UnitDefinition.of(Units.MINUTE, List.of(), "min", List.of()), | |
UnitDefinition.withStandardPrefixes(Units.NEWTON), | |
UnitDefinition.of(Units.OHM, PrefixDefinitions.STANDARD, null, List.of("Ohm")), | |
UnitDefinition.withStandardPrefixes(Units.PASCAL), | |
UnitDefinition.withStandardPrefixes(Units.RADIAN), | |
UnitDefinition.withStandardPrefixes(Units.SIEMENS), | |
UnitDefinition.withStandardPrefixes(Units.SIEVERT), | |
UnitDefinition.withStandardPrefixes(Units.STERADIAN), | |
UnitDefinition.withStandardPrefixes(Units.TESLA), | |
UnitDefinition.withStandardPrefixes(Units.VOLT), | |
UnitDefinition.withStandardPrefixes(Units.WATT), | |
UnitDefinition.withStandardPrefixes(Units.WEBER), | |
UnitDefinition.of(Units.WEEK, List.of(), "week", List.of()), | |
UnitDefinition.of(Units.YEAR, List.of(), "year", List.of("days365"))); | |
} | |
class UnitDefinition { | |
private final Unit<?> unit; | |
private final List<PrefixDefinition> prefixes; | |
private final String symbolOverride; | |
private final List<String> aliases; | |
static UnitDefinition of(Unit<?> unit, List<PrefixDefinition> prefixes, String symbolOverride, List<String> aliases) { | |
return new UnitDefinition(unit, prefixes, symbolOverride, aliases); | |
} | |
static UnitDefinition withStandardPrefixes(Unit<?> unit) { | |
return new UnitDefinition(unit, PrefixDefinitions.STANDARD, null, List.of()); | |
} | |
static UnitDefinition withPrefixesAndSymbolOverride(Unit<?> unit, List<PrefixDefinition> prefixes, String symbolOverride) { | |
return new UnitDefinition(unit, prefixes, symbolOverride, List.of()); | |
} | |
private UnitDefinition(Unit<?> unit, List<PrefixDefinition> prefixes, String symbolOverride, List<String> aliases) { | |
this.unit = unit; | |
this.prefixes = prefixes; | |
this.symbolOverride = symbolOverride; | |
this.aliases = aliases; | |
} | |
public Unit<?> getUnit() { | |
return unit; | |
} | |
public List<PrefixDefinition> getPrefixes() { | |
return prefixes; | |
} | |
public String getSymbolOverride() { | |
return symbolOverride; | |
} | |
public List<String> getAliases() { | |
return aliases; | |
} | |
} | |
class PrefixDefinition { | |
private final MetricPrefix prefix; | |
private final String prefixOverride; | |
private final List<String> prefixAliases; | |
static PrefixDefinition of(MetricPrefix prefix, List<String> prefixAlias) { | |
return new PrefixDefinition(prefix, null, prefixAlias); | |
} | |
static PrefixDefinition of(MetricPrefix prefix, String... prefixAlias) { | |
return new PrefixDefinition(prefix, null, List.of(prefixAlias)); | |
} | |
private PrefixDefinition(MetricPrefix prefix, String prefixOverride, List<String> prefixAliases) { | |
this.prefix = prefix; | |
this.prefixOverride = prefixOverride; | |
this.prefixAliases = prefixAliases; | |
} | |
public MetricPrefix getPrefix() { | |
return prefix; | |
} | |
public String getPrefixOverride() { | |
return prefixOverride; | |
} | |
public List<String> getPrefixAliases() { | |
return prefixAliases; | |
} | |
} | |
class PrefixDefinitions { | |
public static List<PrefixDefinition> STANDARD = List.of( | |
PrefixDefinition.of(YOTTA), | |
PrefixDefinition.of(ZETTA), | |
PrefixDefinition.of(EXA), | |
PrefixDefinition.of(PETA), | |
PrefixDefinition.of(TERA), | |
PrefixDefinition.of(GIGA), | |
PrefixDefinition.of(MEGA), | |
PrefixDefinition.of(KILO), | |
PrefixDefinition.of(HECTO), | |
PrefixDefinition.of(DEKA), | |
PrefixDefinition.of(DECI), | |
PrefixDefinition.of(CENTI), | |
PrefixDefinition.of(MILLI), | |
PrefixDefinition.of(MICRO, "\u03BC"), | |
PrefixDefinition.of(NANO), | |
PrefixDefinition.of(PICO), | |
PrefixDefinition.of(FEMTO), | |
PrefixDefinition.of(ATTO), | |
PrefixDefinition.of(ZEPTO), | |
PrefixDefinition.of(YOCTO)); | |
public static List<PrefixDefinition> GRAM = List.of( | |
PrefixDefinition.of(YOTTA), | |
PrefixDefinition.of(ZETTA), | |
PrefixDefinition.of(EXA), | |
PrefixDefinition.of(PETA), | |
PrefixDefinition.of(TERA), | |
PrefixDefinition.of(GIGA), | |
PrefixDefinition.of(MEGA), | |
// no kilo | |
PrefixDefinition.of(HECTO), | |
PrefixDefinition.of(DEKA), | |
PrefixDefinition.of(DECI), | |
PrefixDefinition.of(CENTI), | |
PrefixDefinition.of(MILLI), | |
PrefixDefinition.of(MICRO, "\u03BC"), | |
PrefixDefinition.of(NANO), | |
PrefixDefinition.of(PICO), | |
PrefixDefinition.of(FEMTO), | |
PrefixDefinition.of(ATTO), | |
PrefixDefinition.of(ZEPTO), | |
PrefixDefinition.of(YOCTO)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment