001/*
002 * CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE
003 * CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT.
004 * PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY
005 * DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THE
006 * AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE"
007 * BUTTON AT THE BOTTOM OF THIS PAGE.
008 *
009 * Specification: JSR-354 Money and Currency API ("Specification")
010 *
011 * Copyright (c) 2012-2013, Credit Suisse All rights reserved.
012 */
013package org.javamoney.moneta.spi.base;
014
015import javax.money.CurrencyQuery;
016import javax.money.CurrencyQueryBuilder;
017import javax.money.CurrencyUnit;
018import javax.money.MonetaryException;
019import javax.money.UnknownCurrencyException;
020import javax.money.spi.MonetaryCurrenciesSingletonSpi;
021import java.util.Collection;
022import java.util.Locale;
023import java.util.Objects;
024import java.util.Set;
025
026/**
027 * Factory singleton backing interface for {@link javax.money.Monetary} that provides access to
028 * different registered {@link javax.money.spi.CurrencyProviderSpi} instances.
029 * <p>
030 * Implementations of this interface must be thread safe.
031 *
032 * @author Anatole Tresch
033 * @version 0.8
034 */
035public abstract class BaseMonetaryCurrenciesSingletonSpi implements MonetaryCurrenciesSingletonSpi{
036
037    /**
038     * Access a new instance based on the currency code. Currencies are
039     * available as provided by {@link javax.money.spi.CurrencyProviderSpi} instances registered
040     * with the {@link javax.money.spi.Bootstrap}.
041     *
042     * @param currencyCode the ISO currency code, not {@code null}.
043     * @param providers    the (optional) specification of providers to consider. If not set (empty) the providers
044     *                     as defined by #getDefaultRoundingProviderChain() should be used.
045     * @return the corresponding {@link javax.money.CurrencyUnit} instance.
046     * @throws javax.money.UnknownCurrencyException if no such currency exists.
047     */
048    public CurrencyUnit getCurrency(String currencyCode, String... providers) {
049        Objects.requireNonNull(currencyCode, "Currency Code may not be null");
050        Collection<CurrencyUnit> found =
051                getCurrencies(CurrencyQueryBuilder.of().setCurrencyCodes(currencyCode).setProviderNames(providers).build());
052        if (found.isEmpty()) {
053            throw new UnknownCurrencyException(currencyCode);
054        }
055        if (found.size() > 1) {
056            throw new MonetaryException("Ambiguous CurrencyUnit for code: " + currencyCode + ": " + found);
057        }
058        return found.iterator().next();
059    }
060
061    /**
062     * Access a new instance based on the currency code. Currencies are
063     * available as provided by {@link javax.money.spi.CurrencyProviderSpi} instances registered
064     * with the {@link javax.money.spi.Bootstrap}.
065     *
066     * @param country   the ISO currency's country, not {@code null}.
067     * @param providers the (optional) specification of providers to consider. If not set (empty) the providers
068     *                  as defined by #getDefaultRoundingProviderChain() should be used.
069     * @return the corresponding {@link javax.money.CurrencyUnit} instance.
070     * @throws javax.money.UnknownCurrencyException if no such currency exists.
071     */
072    public CurrencyUnit getCurrency(Locale country, String... providers) {
073        Collection<CurrencyUnit> found =
074                getCurrencies(CurrencyQueryBuilder.of().setCountries(country).setProviderNames(providers).build());
075        if (found.isEmpty()) {
076            throw new MonetaryException("No currency unit found for locale: " + country);
077        }
078        if (found.size() > 1) {
079            throw new MonetaryException("Ambiguous CurrencyUnit for locale: " + country + ": " + found);
080        }
081        return found.iterator().next();
082    }
083
084    /**
085     * Provide access to all currently known currencies.
086     *
087     * @param locale    the target {@link java.util.Locale}, typically representing an ISO country,
088     *                  not {@code null}.
089     * @param providers the (optional) specification of providers to consider. If not set (empty) the providers
090     *                  as defined by #getDefaultRoundingProviderChain() should be used.
091     * @return a collection of all known currencies, never null.
092     */
093    public Set<CurrencyUnit> getCurrencies(Locale locale, String... providers) {
094        return getCurrencies(CurrencyQueryBuilder.of().setCountries(locale).setProviderNames(providers).build());
095    }
096
097    /**
098     * Allows to check if a {@link javax.money.CurrencyUnit} instance is defined, i.e.
099     * accessible from {@link BaseMonetaryCurrenciesSingletonSpi#getCurrency(String, String...)}.
100     *
101     * @param code      the currency code, not {@code null}.
102     * @param providers the (optional) specification of providers to consider. If not set (empty) the providers
103     *                  as defined by #getDefaultRoundingProviderChain() should be used.
104     * @return {@code true} if {@link BaseMonetaryCurrenciesSingletonSpi#getCurrency(String, String...)}
105     * would return a result for the given code.
106     */
107    public boolean isCurrencyAvailable(String code, String... providers) {
108        return !getCurrencies(CurrencyQueryBuilder.of().setCurrencyCodes(code).setProviderNames(providers).build())
109                .isEmpty();
110    }
111
112    /**
113     * Allows to check if a {@link javax.money.CurrencyUnit} instance is
114     * defined, i.e. accessible from {@link #getCurrency(String, String...)}.
115     *
116     * @param locale    the target {@link java.util.Locale}, not {@code null}.
117     * @param providers the (optional) specification of providers to consider. If not set (empty) the providers
118     *                  as defined by #getDefaultRoundingProviderChain() should be used.
119     * @return {@code true} if {@link #getCurrencies(java.util.Locale, String...)} would return a
120     * non empty result for the given code.
121     */
122    public boolean isCurrencyAvailable(Locale locale, String... providers) {
123        return !getCurrencies(CurrencyQueryBuilder.of().setCountries(locale).setProviderNames(providers).build()).isEmpty();
124    }
125
126    /**
127     * Provide access to all currently known currencies.
128     *
129     * @param providers the (optional) specification of providers to consider. If not set (empty) the providers
130     *                  as defined by #getDefaultRoundingProviderChain() should be used.
131     * @return a collection of all known currencies, never null.
132     */
133    public Set<CurrencyUnit> getCurrencies(String... providers) {
134        return getCurrencies(CurrencyQueryBuilder.of().setProviderNames(providers).build());
135    }
136
137    /**
138     * Access a single currency by query.
139     *
140     * @param query The currency query, not null.
141     * @return the {@link javax.money.CurrencyUnit} found, never null.
142     * @throws javax.money.MonetaryException if multiple currencies match the query.
143     */
144    public CurrencyUnit getCurrency(CurrencyQuery query) {
145        Set<CurrencyUnit> currencies = getCurrencies(query);
146        if (currencies.isEmpty()) {
147            return null;
148        }
149        if (currencies.size() == 1) {
150            return currencies.iterator().next();
151        }
152        throw new MonetaryException("Ambiguous request for CurrencyUnit: " + query + ", found: " + currencies);
153    }
154}