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 static org.javamoney.moneta.spi.AbstractCurrencyConversion.KEY_SCALE; 019 020import java.math.BigDecimal; 021import java.math.MathContext; 022import java.time.LocalDate; 023import java.time.LocalDateTime; 024import java.util.Objects; 025import java.util.Optional; 026import java.util.logging.Logger; 027 028import javax.money.NumberValue; 029import javax.money.convert.ConversionContext; 030import javax.money.convert.ConversionQuery; 031import javax.money.convert.CurrencyConversion; 032import javax.money.convert.ExchangeRate; 033import javax.money.convert.ExchangeRateProvider; 034import javax.money.convert.ProviderContext; 035import javax.money.convert.RateType; 036 037/** 038 * Abstract base class for {@link ExchangeRateProvider} implementations. 039 * 040 * @author Anatole Tresch 041 * @author Werner Keil 042 */ 043public abstract class AbstractRateProvider implements ExchangeRateProvider { 044 045 /** 046 * The {@link ConversionContext} of this provider. 047 */ 048 private final ProviderContext context; 049 050 @Deprecated 051 protected final Logger log = Logger.getLogger(getClass().getName()); 052 053 /** 054 * Constructor. 055 * 056 * @param providerContext the {@link ProviderContext}, not null. 057 */ 058 public AbstractRateProvider(ProviderContext providerContext) { 059 Objects.requireNonNull(providerContext); 060 this.context = providerContext; 061 } 062 063 /* 064 * (non-Javadoc) 065 * 066 * @see javax.money.convert.spi.ExchangeRateProviderSpi#getExchangeRateType 067 * () 068 */ 069 @Override 070 public ProviderContext getContext() { 071 return context; 072 } 073 074 @Override 075 public abstract ExchangeRate getExchangeRate(ConversionQuery conversionQuery); 076 077 @Override 078 public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) { 079 if (getContext().getRateTypes().size() == 1) { 080 return new LazyBoundCurrencyConversion(conversionQuery, this, ConversionContext 081 .of(getContext().getProviderName(), getContext().getRateTypes().iterator().next())); 082 } 083 return new LazyBoundCurrencyConversion(conversionQuery, this, 084 ConversionContext.of(getContext().getProviderName(), RateType.ANY)); 085 } 086 087 088 /** 089 * A protected helper method to multiply 2 {@link NumberValue} types.<br> 090 * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown. 091 * 092 * @param multiplicand the first value to be multiplied 093 * @param multiplier the second value to be multiplied 094 * @return the result of the multiplication as {@link NumberValue} 095 */ 096 protected static NumberValue multiply(NumberValue multiplicand, NumberValue multiplier) { 097 if (Objects.isNull(multiplicand)) { 098 throw new ArithmeticException("The multiplicand cannot be null"); 099 } 100 if (Objects.isNull(multiplier)) { 101 throw new ArithmeticException("The multiplier cannot be null"); 102 } 103 return new DefaultNumberValue( 104 multiplicand.numberValueExact(BigDecimal.class).multiply(multiplier.numberValue(BigDecimal.class))); 105 } 106 107 /** 108 * A protected helper method to divide 2 {@link NumberValue} types.<br> 109 * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown. 110 * 111 * @param dividend the first value to be divided 112 * @param divisor the value to be divided by 113 * @return the result of the division as {@link NumberValue} 114 */ 115 protected static NumberValue divide(NumberValue dividend, NumberValue divisor) { 116 if (Objects.isNull(dividend)) { 117 throw new ArithmeticException("The dividend cannot be null"); 118 } 119 if (Objects.isNull(divisor)) { 120 throw new ArithmeticException("The divisor cannot be null"); 121 } 122 return new DefaultNumberValue( 123 dividend.numberValueExact(BigDecimal.class).divide(divisor.numberValue(BigDecimal.class), 124 MathContext.DECIMAL64)); 125 } 126 127 /** 128 * A protected helper method to divide 2 {@link NumberValue} types.<br> 129 * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown. 130 * 131 * @param dividend the first value to be divided 132 * @param divisor the value to be divided by 133 * @param context the {@link MathContext} to use 134 * @return the result of the division as {@link NumberValue} 135 */ 136 protected static NumberValue divide(NumberValue dividend, NumberValue divisor, MathContext context) { 137 if (Objects.isNull(dividend)) { 138 throw new ArithmeticException("The dividend cannot be null"); 139 } 140 if (Objects.isNull(divisor)) { 141 throw new ArithmeticException("The divisor cannot be null"); 142 } 143 return new DefaultNumberValue( 144 dividend.numberValueExact(BigDecimal.class).divide(divisor.numberValue(BigDecimal.class), context)); 145 } 146 147 protected int getScale(String key) { 148 String string = MonetaryConfig.getConfig().getOrDefault( 149 key, "-1"); 150 if (string.isEmpty()) { 151 return -1; 152 } else { 153 try { 154 return Integer.valueOf(string); 155 } catch (NumberFormatException e) { 156 return -1; 157 } 158 } 159 } 160 161 protected ConversionContext getExchangeContext(String key) { 162 int scale = getScale(key); 163 if(scale < 0) { 164 return ConversionContext.of(this.context.getProviderName(), RateType.HISTORIC); 165 } else { 166 return ConversionContext.of(this.context.getProviderName(), RateType.HISTORIC).toBuilder().set(KEY_SCALE, scale).build(); 167 } 168 } 169 170 protected LocalDate[] getQueryDates(ConversionQuery query) { 171 172 if (Objects.nonNull(query.get(LocalDate.class)) || Objects.nonNull(query.get(LocalDateTime.class))) { 173 LocalDate localDate = Optional.ofNullable(query.get(LocalDate.class)).orElseGet(() -> query.get(LocalDateTime.class).toLocalDate()); 174 return new LocalDate[]{localDate}; 175 } else if(Objects.nonNull(query.get(LocalDate[].class))) { 176 return query.get(LocalDate[].class); 177 } 178 return null; 179 } 180}