001/** 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta.spi; 017 018import javax.money.CurrencyUnit; 019import javax.money.MonetaryAmount; 020import javax.money.MonetaryOperator; 021import javax.money.NumberValue; 022import javax.money.convert.ConversionContext; 023import javax.money.convert.CurrencyConversion; 024import javax.money.convert.CurrencyConversionException; 025import javax.money.convert.ExchangeRate; 026import java.math.MathContext; 027import java.math.RoundingMode; 028import java.util.Objects; 029 030/** 031 * Abstract base class used for implementing currency conversion. 032 * 033 * @author Anatole Tresch 034 * @author Werner Keil 035 */ 036public abstract class AbstractCurrencyConversion implements CurrencyConversion { 037 038 private final CurrencyUnit termCurrency; 039 private final ConversionContext conversionContext; 040 041 public AbstractCurrencyConversion(CurrencyUnit termCurrency, ConversionContext conversionContext) { 042 Objects.requireNonNull(termCurrency); 043 Objects.requireNonNull(conversionContext); 044 this.termCurrency = termCurrency; 045 this.conversionContext = conversionContext; 046 } 047 048 /** 049 * Access the terminating {@link CurrencyUnit} of this conversion instance. 050 * 051 * @return the terminating {@link CurrencyUnit} , never {@code null}. 052 */ 053 @Override 054 public CurrencyUnit getCurrency() { 055 return termCurrency; 056 } 057 058 /** 059 * Access the target {@link ConversionContext} of this conversion instance. 060 * 061 * @return the target {@link ConversionContext}. 062 */ 063 @Override 064 public ConversionContext getContext() { 065 return conversionContext; 066 } 067 068 /** 069 * Get the exchange rate type that this {@link MonetaryOperator} instance is 070 * using for conversion. 071 * 072 * @return the {@link ExchangeRate} to be used, or null, if this conversion 073 * is not supported (will lead to a 074 * {@link CurrencyConversionException}. 075 * @see #apply(MonetaryAmount) 076 */ 077 @Override 078 public abstract ExchangeRate getExchangeRate(MonetaryAmount sourceAmount); 079 080 /* 081 * (non-Javadoc) 082 * @see javax.money.convert.CurrencyConversion#with(javax.money.convert.ConversionContext) 083 */ 084 public abstract CurrencyConversion with(ConversionContext conversionContext); 085 086 /** 087 * Method that converts the source {@link MonetaryAmount} to an 088 * {@link MonetaryAmount} based on the {@link ExchangeRate} of this 089 * conversion. 090 * 091 * @param amount The source amount 092 * @return The converted amount, never null. 093 * @throws CurrencyConversionException if conversion failed, or the required data is not available. 094 * @see #getExchangeRate(MonetaryAmount) 095 */ 096 @Override 097 public MonetaryAmount apply(MonetaryAmount amount) { 098 if (termCurrency.equals(Objects.requireNonNull(amount).getCurrency())) { 099 return amount; 100 } 101 ExchangeRate rate = getExchangeRate(amount); 102 if (Objects.isNull(rate) || !amount.getCurrency().equals(rate.getBaseCurrency())) { 103 throw new CurrencyConversionException(amount.getCurrency(), 104 Objects.isNull(rate) ? null : rate.getCurrency(), null); 105 } 106 NumberValue factor = rate.getFactor(); 107 factor = roundFactor(amount, factor); 108 return amount.multiply(factor).getFactory().setCurrency(rate.getCurrency()).create(); 109 } 110 111 /** 112 * Optionally rounds the factor to be used. By default this method will only round 113 * as much as its is needed, so the factor can be handled by the target amount instance based on its 114 * numeric capabilities. 115 * 116 * @param amount the amount, not null. 117 * @param factor the factor 118 * @return the new NumberValue, never null. 119 */ 120 protected NumberValue roundFactor(MonetaryAmount amount, NumberValue factor) { 121 if (amount.getContext().getMaxScale() > 0) { 122 if (factor.getScale() > amount.getContext().getMaxScale()) { 123 return factor.round(new MathContext(amount.getContext().getMaxScale(), RoundingMode.HALF_EVEN)); 124 } 125 } 126 return factor; 127 } 128 129 130 /* 131 * (non-Javadoc) 132 * 133 * @see java.lang.Object#toString() 134 */ 135 @Override 136 public String toString() { 137 return getClass().getName() + " [MonetaryAmount -> MonetaryAmount" + ']'; 138 } 139 140}