diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyConverter.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyConverter.java index 5d4d534352c..dff2f3ac32c 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyConverter.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyConverter.java @@ -12,8 +12,10 @@ */ package org.openhab.core.internal.library.unit; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.MathContext; +import java.util.Map; import java.util.Objects; import javax.measure.UnitConverter; @@ -22,6 +24,7 @@ import org.eclipse.jdt.annotation.Nullable; import tech.units.indriya.function.AbstractConverter; +import tech.units.indriya.function.Calculus; /** * The {@link CurrencyConverter} implements an {@link UnitConverter} for @@ -82,4 +85,28 @@ public boolean isIdentity() { public boolean isLinear() { return true; } + + /** + * This is currently necessary because conversion of {@link tech.units.indriya.unit.ProductUnit}s requires a + * converter that is properly registered. This is currently not possible. We can't use the registered providers, + * because they only have package-private constructors. + * + * {@see https://github.com/unitsofmeasurement/indriya/issues/402} + */ + static { + // call to ensure map is initialized + Map, Integer> normalFormOrder = (Map, Integer>) Calculus + .getNormalFormOrder(); + try { + Field field = Calculus.class.getDeclaredField("normalFormOrder"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + Map, Integer> original = (Map, Integer>) field + .get(null); + original.put(CurrencyConverter.class, 1000); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalStateException("Could not add currency converter", e); + } + + } } diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyService.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyService.java index 4ccb778d733..6a365a5c531 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyService.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/internal/library/unit/CurrencyService.java @@ -108,8 +108,10 @@ private synchronized void enableProvider(CurrencyProvider currencyProvider) { Unit baseCurrency = currencyProvider.getBaseCurrency(); ((CurrencyUnit) BASE_CURRENCY).setSymbol(baseCurrency.getSymbol()); ((CurrencyUnit) BASE_CURRENCY).setName(baseCurrency.getName()); - unitFormatter.label(BASE_CURRENCY, - Objects.requireNonNullElse(baseCurrency.getSymbol(), baseCurrency.getName())); + unitFormatter.label(BASE_CURRENCY, baseCurrency.getName()); + if (baseCurrency.getSymbol() != null) { + unitFormatter.alias(BASE_CURRENCY, baseCurrency.getSymbol()); + } currencyProvider.getAdditionalCurrencies().forEach(CurrencyUnits::addUnit); diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/unit/CurrencyUnit.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/unit/CurrencyUnit.java index 570e290b24f..43cd1d861a2 100644 --- a/bundles/org.openhab.core/src/main/java/org/openhab/core/library/unit/CurrencyUnit.java +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/library/unit/CurrencyUnit.java @@ -19,16 +19,13 @@ import static org.openhab.core.library.unit.CurrencyUnits.BASE_CURRENCY; import static tech.units.indriya.AbstractUnit.ONE; -import java.io.Serializable; import java.math.BigDecimal; import java.math.MathContext; import java.util.Map; import java.util.Objects; import javax.measure.Dimension; -import javax.measure.IncommensurableException; import javax.measure.Prefix; -import javax.measure.Quantity; import javax.measure.UnconvertibleException; import javax.measure.Unit; import javax.measure.UnitConverter; @@ -39,18 +36,11 @@ import org.openhab.core.internal.library.unit.CurrencyService; import org.openhab.core.library.dimension.Currency; +import tech.units.indriya.AbstractUnit; import tech.units.indriya.function.AbstractConverter; -import tech.units.indriya.function.AddConverter; -import tech.units.indriya.function.Calculus; import tech.units.indriya.function.MultiplyConverter; import tech.units.indriya.function.RationalNumber; -import tech.units.indriya.unit.AlternateUnit; -import tech.units.indriya.unit.ProductUnit; -import tech.units.indriya.unit.TransformedUnit; import tech.units.indriya.unit.UnitDimension; -import tech.uom.lib.common.function.Nameable; -import tech.uom.lib.common.function.PrefixOperator; -import tech.uom.lib.common.function.SymbolSupplier; /** * The {@link CurrencyUnit} is a UoM compatible unit for currencies. @@ -58,8 +48,7 @@ * @author Jan N. Klug - Initial contribution */ @NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND }) -public final class CurrencyUnit implements Unit, Comparable>, PrefixOperator, - Nameable, Serializable, SymbolSupplier { +public final class CurrencyUnit extends AbstractUnit { private static final long serialVersionUID = -1L; private static final Dimension DIMENSION = UnitDimension.parse('$'); @@ -82,32 +71,17 @@ public CurrencyUnit(String name, @Nullable String symbol) throws IllegalArgument } public UnitConverter getSystemConverter() { - return AbstractConverter.IDENTITY; + return internalGetConverterTo(getSystemUnit()); } @Override - public String toString() { - return getName(); + protected Unit toSystemUnit() { + return BASE_CURRENCY; } @Override - public Unit getSystemUnit() { - return this; - } - - @Override - public boolean isCompatible(@NonNullByDefault({}) Unit that) { - return DIMENSION.equals(that.getDimension()); - } - - @SuppressWarnings("unchecked") - @Override - public @NonNullByDefault({}) > Unit asType(@NonNullByDefault({}) Class type) { - Dimension typeDimension = UnitDimension.of(type); - if (typeDimension != null && !typeDimension.equals(this.getDimension())) { - throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type); - } - return (Unit) this; + public String toString() { + return getName(); } @Override @@ -120,7 +94,7 @@ public Dimension getDimension() { return DIMENSION; } - public void setName(String name) { + public void setName(@NonNullByDefault({}) String name) { this.name = name; } @@ -138,41 +112,6 @@ public void setSymbol(@Nullable String s) { this.symbol = s; } - @Override - public final UnitConverter getConverterTo(@NonNullByDefault({}) Unit that) throws UnconvertibleException { - return internalGetConverterTo(that); - } - - @SuppressWarnings("unchecked") - @Override - public final @NonNullByDefault({}) UnitConverter getConverterToAny(@NonNullByDefault({}) Unit that) - throws IncommensurableException, UnconvertibleException { - if (!isCompatible(that)) { - throw new IncommensurableException(this + " is not compatible with " + that); - } - return internalGetConverterTo((Unit) that); - } - - @Override - public final Unit alternate(@NonNullByDefault({}) String newSymbol) { - return new AlternateUnit<>(this, newSymbol); - } - - @Override - public final Unit transform(@NonNullByDefault({}) UnitConverter operation) { - return operation.isIdentity() ? this : new TransformedUnit<>(null, this, this, operation); - } - - @Override - public Unit shift(@NonNullByDefault({}) Number offset) { - return Calculus.currentNumberSystem().isZero(offset) ? this : transform(new AddConverter(offset)); - } - - @Override - public Unit multiply(@NonNullByDefault({}) Number factor) { - return Calculus.currentNumberSystem().isOne(factor) ? this : transform(MultiplyConverter.of(factor)); - } - @Override public Unit shift(double offset) { return shift(RationalNumber.of(offset)); @@ -214,42 +153,6 @@ private UnitConverter internalGetConverterTo(Unit that) throws Unconve "Could not get factor for converting " + this.getName() + " to " + that.getName()); } - @Override - public final Unit multiply(@NonNullByDefault({}) Unit that) { - return that.equals(ONE) ? this : ProductUnit.ofProduct(this, that); - } - - @Override - public final Unit inverse() { - return ProductUnit.ofQuotient(ONE, this); - } - - @Override - public final Unit divide(@NonNullByDefault({}) Number divisor) { - if (Calculus.currentNumberSystem().isOne(divisor)) { - return this; - } - BigDecimal factor = BigDecimal.ONE.divide(new BigDecimal(divisor.toString()), MathContext.DECIMAL128); - return transform(MultiplyConverter.of(factor)); - } - - @Override - public final Unit divide(@NonNullByDefault({}) Unit that) { - return this.multiply(that.inverse()); - } - - @Override - public final Unit root(int n) { - if (n > 0) { - return ProductUnit.ofRoot(this, n); - } else if (n == 0) { - throw new ArithmeticException("Root's order of zero"); - } else { - // n < 0 - return ONE.divide(this.root(-n)); - } - } - @Override public Unit pow(int n) { if (n > 0) { diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/unit/CurrencyUnitTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/unit/CurrencyUnitTest.java index 1029cc1c562..89c91bc9fd5 100644 --- a/bundles/org.openhab.core/src/test/java/org/openhab/core/library/unit/CurrencyUnitTest.java +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/library/unit/CurrencyUnitTest.java @@ -33,6 +33,7 @@ import org.openhab.core.library.dimension.Currency; import org.openhab.core.library.dimension.EnergyPrice; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.util.UnitUtils; /** * The {@link CurrencyUnitTest} contains tests for the currency units @@ -97,6 +98,16 @@ public void testPriceCalculation() { assertThat(price.doubleValue(), closeTo(1.25, 1E-4)); } + @Test + public void testEnergyPriceConversion() { + QuantityType price = new QuantityType<>("0.25 EUR/kWh"); + QuantityType convertedPrice = price.toUnit("DKK/kWh"); + + assertThat(convertedPrice, is(notNullValue())); + assertThat(convertedPrice.getUnit(), is(UnitUtils.parseUnit("DKK/kWh"))); + assertThat(convertedPrice.doubleValue(), closeTo(1.8625, 1e-4)); + } + private static class TestCurrencyProvider implements CurrencyProvider { public static final Unit EUR = new CurrencyUnit("EUR", "€"); public static final Unit DKK = new CurrencyUnit("DKK", null);