Skip to content

Commit

Permalink
Add currency as unit
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <github@klug.nrw>
  • Loading branch information
J-N-K committed May 13, 2023
1 parent c2ce05a commit 0036306
Show file tree
Hide file tree
Showing 12 changed files with 799 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@

import static org.openhab.core.library.unit.MetricPrefix.HECTO;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.DateTimeException;
import java.time.ZoneId;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Function;

import javax.measure.Quantity;
import javax.measure.Unit;
Expand Down Expand Up @@ -68,13 +72,17 @@
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.library.dimension.ArealDensity;
import org.openhab.core.library.dimension.Currency;
import org.openhab.core.library.dimension.DataAmount;
import org.openhab.core.library.dimension.DataTransferRate;
import org.openhab.core.library.dimension.Density;
import org.openhab.core.library.dimension.ElectricConductivity;
import org.openhab.core.library.dimension.Intensity;
import org.openhab.core.library.dimension.VolumetricFlowRate;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.unit.CurrencyProvider;
import org.openhab.core.library.unit.CurrencyUnit;
import org.openhab.core.library.unit.CurrencyUnits;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
Expand Down Expand Up @@ -114,19 +122,21 @@
"service.config.category=system", //
"service.config.description.uri=system:i18n" })
@NonNullByDefault
public class I18nProviderImpl
implements TranslationProvider, LocaleProvider, LocationProvider, TimeZoneProvider, UnitProvider {
public class I18nProviderImpl implements TranslationProvider, LocaleProvider, LocationProvider, TimeZoneProvider,
UnitProvider, CurrencyProvider {

private final Logger logger = LoggerFactory.getLogger(I18nProviderImpl.class);

public static final String CONFIGURATION_PID = "org.openhab.i18n";

// LocaleProvider
public static final String LANGUAGE = "language";
public static final String BASE_CURRENCY = "baseCurrency";
public static final String SCRIPT = "script";
public static final String REGION = "region";
public static final String VARIANT = "variant";
private @Nullable Locale locale;
private @Nullable String currencyCode;

// TranslationProvider
private final ResourceBundleTracker resourceBundleTracker;
Expand Down Expand Up @@ -162,6 +172,7 @@ protected void deactivate() {
@Modified
protected synchronized void modified(Map<String, Object> config) {
final String language = toStringOrNull(config.get(LANGUAGE));
this.currencyCode = toStringOrNull(config.get(BASE_CURRENCY));
final String script = toStringOrNull(config.get(SCRIPT));
final String region = toStringOrNull(config.get(REGION));
final String variant = toStringOrNull(config.get(VARIANT));
Expand Down Expand Up @@ -387,6 +398,7 @@ public static Map<Class<? extends Quantity<?>>, Map<SystemOfUnits, Unit<? extend
addDefaultUnit(dimensionMap, Area.class, SIUnits.SQUARE_METRE, ImperialUnits.SQUARE_FOOT);
addDefaultUnit(dimensionMap, ArealDensity.class, Units.DOBSON_UNIT);
addDefaultUnit(dimensionMap, CatalyticActivity.class, Units.KATAL);
addDefaultUnit(dimensionMap, Currency.class, CurrencyUnits.SYSTEM_CURRENCY);
addDefaultUnit(dimensionMap, DataAmount.class, Units.BYTE);
addDefaultUnit(dimensionMap, DataTransferRate.class, Units.MEGABIT_PER_SECOND);
addDefaultUnit(dimensionMap, Density.class, Units.KILOGRAM_PER_CUBICMETRE);
Expand Down Expand Up @@ -436,4 +448,29 @@ private static <T extends Quantity<T>> void addDefaultUnit(
Class<T> dimension, Unit<T> unit) {
dimensionMap.put(dimension, Map.of(SIUnits.getInstance(), unit, ImperialUnits.getInstance(), unit));
}

@Override
public Unit<Currency> getBaseCurrency() {
String currencyCode = this.currencyCode;
if (currencyCode == null && locale != null) {
currencyCode = java.util.Currency.getInstance(locale).getCurrencyCode();
}
if (currencyCode != null) {
// either the currency was set or determined from the locale
String symbol = java.util.Currency.getInstance(currencyCode).getSymbol();
return new CurrencyUnit(currencyCode, symbol);
} else {
return new CurrencyUnit("DEF", null);
}
}

@Override
public Collection<Unit<Currency>> getCurrencies() {
return Set.of();
}

@Override
public Function<Unit<Currency>, @Nullable BigDecimal> getExchangeRateFunction() {
return unit -> null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.internal.library.unit;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Objects;

import javax.measure.UnitConverter;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import tech.units.indriya.function.AbstractConverter;

/**
* The {@link CurrencyConverter} is a
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class CurrencyConverter extends AbstractConverter {

private final BigDecimal factor;

public CurrencyConverter(BigDecimal factor) {
this.factor = factor;
}

@Override
public boolean equals(@Nullable Object cvtr) {
return cvtr instanceof CurrencyConverter currencyConverter && factor.equals(currencyConverter.factor);
}

@Override
public int hashCode() {
return Objects.hashCode(factor);
}

@Override
protected @Nullable String transformationLiteral() {
return null;
}

@Override
protected AbstractConverter inverseWhenNotIdentity() {
return new CurrencyConverter(BigDecimal.ONE.divide(factor, MathContext.DECIMAL128));
}

@Override
protected boolean canReduceWith(@Nullable AbstractConverter that) {
return false;
}

@Override
protected Number convertWhenNotIdentity(@NonNullByDefault({}) Number value) {
return new BigDecimal(value.toString()).multiply(factor, MathContext.DECIMAL128);
}

@Override
public int compareTo(@Nullable UnitConverter o) {
return o instanceof CurrencyConverter currencyConverter ? factor.compareTo(currencyConverter.factor) : -1;
}

@Override
public boolean isIdentity() {
return false;
}

@Override
public boolean isLinear() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.internal.library.unit;

import static org.openhab.core.library.unit.CurrencyUnits.SYSTEM_CURRENCY;

import java.math.BigDecimal;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Function;

import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.dimension.Currency;
import org.openhab.core.library.unit.CurrencyProvider;
import org.openhab.core.library.unit.CurrencyUnit;
import org.openhab.core.library.unit.CurrencyUnits;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;

import tech.units.indriya.format.SimpleUnitFormat;

/**
* The {@link CurrencyService} is a
*
* @author Jan N. Klug - Initial contribution
*/
@Component
@NonNullByDefault
public class CurrencyService {

public static Function<Unit<Currency>, @Nullable BigDecimal> FACTOR_FCN = unit -> null;

private final Set<CurrencyProvider> currencyProviders = new CopyOnWriteArraySet<>();

@Activate
public CurrencyService(
@Reference(target = "(" + Constants.SERVICE_PID + "=org.openhab.i18n)") CurrencyProvider currencyProvider) {
currencyProviders.add(currencyProvider);
enableProvider(currencyProvider);
}

@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
public void addCurrencyProvider(CurrencyProvider currencyProvider) {
currencyProviders.add(currencyProvider);
}

public void removeCurrencyProvider(CurrencyProvider currencyProvider) {
currencyProviders.remove(currencyProvider);
}

private synchronized void enableProvider(CurrencyProvider currencyProvider) {
FACTOR_FCN = currencyProvider.getExchangeRateFunction();
((CurrencyUnit) SYSTEM_CURRENCY).setSymbol(currencyProvider.getBaseCurrency().getSymbol());
((CurrencyUnit) SYSTEM_CURRENCY).setName(currencyProvider.getBaseCurrency().getName());
SimpleUnitFormat.getInstance().label(SYSTEM_CURRENCY, currencyProvider.getBaseCurrency().getSymbol());
currencyProvider.getCurrencies().forEach(CurrencyUnits::addUnit);
}

/**
* Get the exchange rate for a given currency to the system's base unit
*
* @param currency the currency
* @return the exchange rate
*/
public static @Nullable BigDecimal getConversionFactor(Unit<Currency> currency) {
return FACTOR_FCN.apply(currency);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.core.internal.library.unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.CurrencyUnits;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
Expand All @@ -29,6 +30,7 @@ public class UnitInitializer {
Units.getInstance();
SIUnits.getInstance();
ImperialUnits.getInstance();
CurrencyUnits.getInstance();
}

public static void init() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.library.dimension;

import javax.measure.Quantity;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link Currency} is a
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public interface Currency extends Quantity<Currency> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.library.unit;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.function.Function;

import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.dimension.Currency;

/**
* The {@link CurrencyProvider} can be implemented by services that supply currencies and their
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public interface CurrencyProvider {

/**
* Get the name of this {@link CurrencyProvider}
*
* @return the name, defaults to the class name
*/
default String getName() {
return getClass().getName();
}

/**
* Get the base currency from this provider
* <p />
* This currency is used as base for calculating exchange rates.
*
* @return the base currency of this provider
*/
Unit<Currency> getBaseCurrency();

/**
* Get all units that are supported by this provider
*
* @return a {@link Collection} of {@link Unit<Currency>}s
*/
Collection<Unit<Currency>> getCurrencies();

/**
* Get a {@link Function} that supplies exchanges rates for currencies supported by this provider
* <p />
* This needs to be dynamic because in most cases exchange rates are not constant over time.
*
* @return the function
*/
Function<Unit<Currency>, @Nullable BigDecimal> getExchangeRateFunction();
}
Loading

0 comments on commit 0036306

Please sign in to comment.