
com.github.daytron.daytronmoney.conversion.CurrencyExchange Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of DaytronMoney Show documentation
Show all versions of DaytronMoney Show documentation
A Java library for dealing simple monetary operations and conversions.
The newest version!
/*
* The MIT License
*
* Copyright 2015 Ryan Gilera.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.daytron.daytronmoney.conversion;
import com.github.daytron.daytronmoney.currency.Money;
import com.github.daytron.daytronmoney.currency.MoneyFactory;
import com.github.daytron.daytronmoney.exception.MoneyConversionException;
import com.github.daytron.daytronmoney.exception.NegativeMoneyException;
import com.github.daytron.daytronmoney.exception.ZeroMoneyException;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Class to handle all currency conversions. Can only request a single instance
* of this class.
*
* @author Ryan Gilera
*/
public class CurrencyExchange {
private Map listOfRates;
private static final LocalDateTime MIN_DATE_ALLOWED = LocalDateTime.of(2000,
Month.JANUARY, 1, 0, 0);
/**
* Creates an instance private. Connects to API and extract latest currency
* rates from its JSOn file. Saves the currency rates into a
* Map
object. Purpose of list is for currency code
* verification in conversion process.
*/
private CurrencyExchange() {
JsonObject tempObject;
try {
tempObject = ConversionClient.getLatestRatesJsonObject();
} catch (MoneyConversionException ex) {
Logger.getLogger(CurrencyExchange.class.getName())
.log(Level.SEVERE, null, ex);
throw new InstantiationError("Cannot connect to the API.");
}
Set> rateList = tempObject.entrySet();
// Creates a copy
this.listOfRates = new ConcurrentHashMap<>();
for (Map.Entry rateItem : rateList) {
listOfRates.put(rateItem.getKey(), rateItem.getValue().getAsString());
}
}
/**
* Retrieves the one and only instance of this class.
*
* @return CurrencyExchange
object
*/
public static CurrencyExchange get() {
return MySingletonContainer.INSTANCE;
}
/**
* A static inner class that holds the instance of CurrencyExchange class
*/
private static final class MySingletonContainer {
private static final CurrencyExchange INSTANCE = new CurrencyExchange();
}
/**
* Converts a Money
object to another currency.
*
* @param fromMoney Money
object to be converted
* @param toCurrencyCode String
currency code to convert to
* @return Money
object
* @throws
* com.github.daytron.daytronmoney.exception.MoneyConversionException
*/
public Money convert(Money fromMoney, String toCurrencyCode)
throws MoneyConversionException {
return convert(fromMoney, toCurrencyCode, null);
}
/**
* Convert a Money
object to another currency on a specific
* point in time. Allowed historical currency conversion all the way back
* to year 2000. Some currency rates are not available on some dates, throws
* a MoneyConversionException if the rate or base is not available.
*
* @param fromMoney Money
object to be converted
* @param toCurrencyCode String
currency code to convert to
* @param date date at which point the rate is retrieved
* @return Money
object
* @throws MoneyConversionException For any error encountered
*/
public Money convert(Money fromMoney, String toCurrencyCode,
LocalDateTime date) throws MoneyConversionException {
boolean isHistoricalConversion = true;
if (date == null) {
isHistoricalConversion = false;
} else if (date.isBefore(MIN_DATE_ALLOWED)) {
throw new MoneyConversionException("Cannot retrieve "
+ "historical rate before year 2000.");
} else if (date.isAfter(LocalDateTime.now())) {
throw new MoneyConversionException("Cannot process future date.");
} else if (isDateGivenToday(date)) {
isHistoricalConversion = false;
}
validateMoney(fromMoney);
validateCurrencyCode(toCurrencyCode);
toCurrencyCode = toCurrencyCode.trim();
toCurrencyCode = toCurrencyCode.toUpperCase();
String rate;
if (isHistoricalConversion) {
rate = ConversionClient.getCurrencyRate(fromMoney.getCurrencyCode(),
toCurrencyCode, date);
} else {
rate = ConversionClient.getCurrencyRate(fromMoney.getCurrencyCode(),
toCurrencyCode);
}
if (fromMoney.getCurrencyCode().equalsIgnoreCase(toCurrencyCode)) {
return fromMoney;
}
final MoneyFactory mf = new MoneyFactory(toCurrencyCode);
// Change to new currency first to allow same currency operation
Money baseMoney = new Money.Builder()
.currencyCode(toCurrencyCode)
.sign(fromMoney.getSign())
.wholeUnit(fromMoney.getWholeUnit())
.decimalUnit(fromMoney.getDecimalUnit())
.leadingDecimalZeroes(fromMoney.getLeadingDecimalZeros())
.build();
return baseMoney.multiply(mf.valueOf(rate));
}
/**
* Verifies if given date is today.
*
* @param date To compare with today's date
* @return true if date is today, otherwise false
*/
private boolean isDateGivenToday(LocalDateTime date) {
LocalDateTime now = LocalDateTime.now();
return date.getYear() == now.getYear()
&& date.getMonthValue() == now.getMonthValue()
&& date.getDayOfMonth() == now.getDayOfMonth();
}
/**
* Get currency rate for a particular currency with a base currency. Returns
* null if MoneyConversionException occurred. Cause runtime exceptions if
* invalid arguments are presented.
*
* @param baseCurrency Base currency
* @param toCurrency currency of the rate return
* @return Currency rate in String value
*/
public String getCurrencyRate(String baseCurrency, String toCurrency) {
validateCurrencyCode(baseCurrency);
validateCurrencyCode(toCurrency);
try {
String rate = ConversionClient.getCurrencyRate(baseCurrency, toCurrency);
return rate;
} catch (MoneyConversionException ex) {
return null;
}
}
/**
* A helper method that validates the arguments for convert() method.
*
* @param fromMoney Money
to validate
* @param toCurrencyCode Money
object
*/
private void validateMoney(Money fromMoney) {
if (fromMoney == null) {
throw new NullPointerException("Null argument Money detected.");
}
if (fromMoney.isLessThanZero()) {
throw new NegativeMoneyException("Cannot convert negative money value.");
}
if (fromMoney.isZero()) {
throw new ZeroMoneyException("Cannot convert zero value.");
}
}
private void validateCurrencyCode(String currencyCode) {
if (currencyCode == null) {
throw new NullPointerException("Currency null value.");
}
currencyCode = currencyCode.trim();
if (currencyCode.isEmpty()) {
throw new IllegalArgumentException("Empty currency value.");
}
currencyCode = currencyCode.toUpperCase();
if (!listOfRates.containsKey(currencyCode)) {
throw new IllegalArgumentException("Currency code not recognized.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy