001/* 002 * CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU 003 * ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT. PLEASE READ THE TERMS AND CONDITIONS OF THIS 004 * AGREEMENT CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF 005 * THE AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE" BUTTON AT THE 006 * BOTTOM OF THIS PAGE. Specification: JSR-354 Money and Currency API ("Specification") Copyright 007 * (c) 2012-2015, Credit Suisse All rights reserved. 008 */ 009package org.javamoney.moneta.spi.base; 010 011import javax.money.CurrencyUnit; 012import javax.money.MonetaryException; 013import javax.money.convert.ConversionQuery; 014import javax.money.convert.ConversionQueryBuilder; 015import javax.money.convert.CurrencyConversion; 016import javax.money.convert.ExchangeRateProvider; 017import javax.money.spi.MonetaryConversionsSingletonSpi; 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.List; 022import java.util.Objects; 023 024/** 025 * SPI (conversion) that implements the functionality provided by the 026 * {@code MonetaryConversions} singleton accessor. It should be registered as a 027 * service using the JDK {@code ServiceLoader}. Hereby only one instance can be 028 * registered at a time. 029 * <p> 030 * This interface is designed to support also contextual behaviour, e.g. in Java 031 * EE containers each application may provide its own 032 * {@link javax.money.convert.ExchangeRateProvider} instances, e.g. by registering them as CDI 033 * beans. An EE container can register an according 034 * {@link BaseMonetaryConversionsSingletonSpi} that manages the different application 035 * contexts transparently. In a SE environment this class is expected to behave 036 * like an ordinary singleton, loading its SPIs from the {@link java.util.ServiceLoader}. 037 * <p> 038 * Instances of this class must be thread safe. It is not a requirement that 039 * they are serializable. 040 * <p> 041 * Only one instance can be registered using the {@link java.util.ServiceLoader}. When 042 * registering multiple instances the {@link javax.money.convert.MonetaryConversions} accessor will 043 * not work. 044 * 045 * @author Anatole Tresch 046 * @author Werner Keil 047 */ 048public abstract class BaseMonetaryConversionsSingletonSpi implements MonetaryConversionsSingletonSpi{ 049 050 /** 051 * Allows to quickly check, if a {@link javax.money.convert.ExchangeRateProvider} is accessible for the given 052 * {@link javax.money.convert.ConversionQuery}. 053 * 054 * @param conversionQuery the {@link javax.money.convert.ConversionQuery} determining the type of conversion 055 * required, not null. 056 * @return {@code true}, if such a conversion is supported, meaning an according 057 * {@link javax.money.convert.ExchangeRateProvider} can be 058 * accessed. 059 * @see #getExchangeRateProvider(javax.money.convert.ConversionQuery) 060 * @see #getExchangeRateProvider(String...)} 061 */ 062 public boolean isExchangeRateProviderAvailable(ConversionQuery conversionQuery) { 063 try { 064 return getExchangeRateProvider(conversionQuery) != null; 065 } catch (Exception e) { 066 return false; 067 } 068 } 069 070 /** 071 * Allows to quickly check, if a {@link javax.money.convert.CurrencyConversion} is accessible for the given 072 * {@link javax.money.convert.ConversionQuery}. 073 * 074 * @param conversionQuery the {@link javax.money.convert.ConversionQuery} determining the type of conversion 075 * required, not null. 076 * @return {@code true}, if such a conversion is supported, meaning an according 077 * {@link javax.money.convert.CurrencyConversion} can be 078 * accessed. 079 * @see #getConversion(javax.money.convert.ConversionQuery) 080 * @see #getConversion(javax.money.CurrencyUnit, String...)} 081 */ 082 public boolean isConversionAvailable(ConversionQuery conversionQuery) { 083 try { 084 return getConversion(conversionQuery) != null; 085 } catch (Exception e) { 086 return false; 087 } 088 } 089 090 /** 091 * Allows to quickly check, if a {@link javax.money.convert.CurrencyConversion} is accessible for the given 092 * {@link javax.money.convert.ConversionQuery}. 093 * 094 * @param termCurrency the terminating/target currency unit, not null. 095 * @param providers the provider names defines a corresponding 096 * provider chain that must be encapsulated by the resulting {@link javax 097 * .money.convert.CurrencyConversion}. By default the provider 098 * chain as defined by #getDefaultRoundingProviderChain will be used. 099 * @return {@code true}, if such a conversion is supported, meaning an according 100 * {@link javax.money.convert.CurrencyConversion} can be 101 * accessed. 102 * @see #getConversion(javax.money.convert.ConversionQuery) 103 * @see #getConversion(javax.money.CurrencyUnit, String...)} 104 */ 105 public boolean isConversionAvailable(CurrencyUnit termCurrency, String... providers) { 106 return isConversionAvailable( 107 ConversionQueryBuilder.of().setTermCurrency(termCurrency).setProviderNames(providers).build()); 108 } 109 110 /** 111 * Access the current registered {@link javax.money.convert.ExchangeRateProvider} instances. If no provider 112 * names are passed ALL current registered providers are returned in undefined order. 113 * 114 * @param providers the provider names of hte providers to be accessed 115 * @return the list of providers, in the same order as requested. 116 * @throws javax.money.MonetaryException if a provider could not be resolved. 117 */ 118 public List<ExchangeRateProvider> getExchangeRateProviders(String... providers) { 119 List<ExchangeRateProvider> provInstances = new ArrayList<>(); 120 Collection<String> providerNames = Arrays.asList(providers); 121 if (providerNames.isEmpty()) { 122 providerNames = getProviderNames(); 123 } 124 for (String provName : providerNames) { 125 ExchangeRateProvider provider = getExchangeRateProvider(provName); 126 if(provider==null){ 127 throw new MonetaryException("Unsupported conversion/rate provider: " + provName); 128 } 129 provInstances.add(provider); 130 } 131 return provInstances; 132 } 133 134 /** 135 * Access a compound instance of an {@link javax.money.convert.ExchangeRateProvider} based on the given provider chain. 136 * 137 * @param providers the {@link javax.money.convert.ConversionQuery} provider names defines a corresponding 138 * provider chain that must be 139 * encapsulated by the resulting {@link javax.money.convert.ExchangeRateProvider}. By default 140 * the default 141 * provider changes as defined in #getDefaultRoundingProviderChain will be used. 142 * @return an {@link javax.money.convert.ExchangeRateProvider} built up with the given sub 143 * providers, never {@code null}. 144 * @throws javax.money.MonetaryException if a provider listed could not be found. 145 * @see #getProviderNames() 146 * @see #isExchangeRateProviderAvailable(javax.money.convert.ConversionQuery) 147 */ 148 public ExchangeRateProvider getExchangeRateProvider(String... providers) { 149 return getExchangeRateProvider(ConversionQueryBuilder.of().setProviderNames(providers).build()); 150 } 151 152 /** 153 * Access an instance of {@link javax.money.convert.CurrencyConversion}. 154 * 155 * @param conversionQuery the {@link javax.money.convert.ConversionQuery} determining the type of conversion 156 * required, not null. 157 * @return the corresponding conversion, not null. 158 * @throws javax.money.MonetaryException if no matching conversion could be found. 159 * @see #isConversionAvailable(javax.money.convert.ConversionQuery) 160 */ 161 public CurrencyConversion getConversion(ConversionQuery conversionQuery) { 162 return getExchangeRateProvider(conversionQuery).getCurrencyConversion( 163 Objects.requireNonNull(conversionQuery.getCurrency(), "Terminating Currency is required.") 164 ); 165 } 166 167 /** 168 * Access an instance of {@link javax.money.convert.CurrencyConversion}. 169 * 170 * @param termCurrency the terminating/target currency unit, not null. 171 * @param providers the {@link javax.money.convert.ConversionQuery} provider names defines a corresponding 172 * provider chain that must be encapsulated by the resulting {@link javax 173 * .money.convert.CurrencyConversion}. By default the default 174 * provider chain as defined by #getDefaultRoundingProviderChain will be used. 175 * @return the corresponding conversion, not null. 176 * @throws javax.money.MonetaryException if no matching conversion could be found. 177 * @see #isConversionAvailable(javax.money.convert.ConversionQuery) 178 */ 179 public CurrencyConversion getConversion(CurrencyUnit termCurrency, String... providers) { 180 return getConversion(ConversionQueryBuilder.of().setTermCurrency(termCurrency).setProviderNames(providers).build()); 181 } 182 183}