All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.javamoney.tck.tests.conversion.TestExchangeRate Maven / Gradle / Ivy

/*
 * Copyright (c) 2012, 2013, Werner Keil, Credit Suisse (Anatole Tresch). Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License. Contributors: Anatole Tresch - initial version.
 */
package org.javamoney.tck.tests.conversion;

import javax.money.CurrencyUnit;
import javax.money.NumberValue;
import javax.money.convert.ConversionContext;
import javax.money.convert.ExchangeRate;
import javax.money.convert.RateType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * This class models an exchange rate, which defines the factor the numeric value of a base amount in some currency
 * 'A' must be multiplied
 * to get the corresponding amount in the terminating currency 'B'. Hereby
 * 
    *
  • an exchange rate always models one rate from a base (source) to a term * (target) {@link javax.money.CurrencyUnit}.
  • *
  • an exchange rate is always bound to a rate type, which typically matches * the data source of the conversion data, e.g. different credit card providers * may use different rates for the same conversion.
  • *
  • an exchange rate may restrict its validity. In most of the use cases a * rates' validity will be well defined, but it is also possible that the data * provider is not able to support the rate's validity, leaving it undefined-
  • *
  • an exchange rate has a provider, which is responsible for defining the * rate. A provider hereby may be, but must not be the same as the rate's data * source.
  • *
  • an exchange rate can be a direct rate, where its factor is * represented by a single conversion step. Or it can model a derived * rate, where multiple conversion steps are required to define the overall * base/term conversion. In case of derived rates the chained rates define the * overall factor, by multiplying the individual chain rate factors. Of course, * this also requires that each subsequent rate's base currency in the chain * does match the previous term currency (and vice versa):
  • *
  • Whereas the factor should be directly implied by the format rate chain * for derived rates, this is obviously not the case for the validity range, * since rates can have a undefined validity range. Nevertheless in many cases * also the validity range can (but must not) be derived from the rate chain.
  • *
  • Finally a conversion rate is always unidirectional. There might be cases * where the reciprocal value of {@link #factor} matches the correct reverse * rate. But in most use cases the reverse rate either has a different rate (not * equal to the reciprocal value), or might not be defined at all. Therefore for * reversing a ExchangeRate one must access an {@link javax.money.convert.ExchangeRateProvider} and * query for the reverse rate.
  • *
*

* The class also implements {@link Comparable} to allow sorting of multiple * exchange rates using the following sorting order; *

    *
  • Exchange rate type
  • *
  • Exchange rate provider
  • *
  • base currency
  • *
  • term currency
  • *
*

* Finally ExchangeRate is modeled as an immutable and thread safe type. Also * exchange rates are {@link java.io.Serializable}, hereby serializing in the following * form and order: *

    *
  • The base {@link javax.money.CurrencyUnit} *
  • The target {@link javax.money.CurrencyUnit} *
  • The factor (NumberValue) *
  • The {@link javax.money.convert.ConversionContext} *
  • The rate chain *
* * @author Werner Keil * @author Anatole Tresch * @see Wikipedia: * Exchange Rate (Quotations) */ @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject") public final class TestExchangeRate implements ExchangeRate, Serializable, Comparable { /** * serialVersionUID. */ private static final long serialVersionUID = 5077295306570465837L; /** * The base currency. */ private final CurrencyUnit base; /** * The terminating currency. */ private final CurrencyUnit term; /** * The conversion factor. */ private final NumberValue factor; /** * The {@link javax.money.convert.ConversionContext} */ private final ConversionContext conversionContext; /** * The full chain, at least one instance long. */ private List chain = new ArrayList<>(); /** * Creates a new instance with a custom chain of exchange rate type, e.g. or * creating derived rates. * * @param builder The Builder, never {@code null}. */ private TestExchangeRate(Builder builder) { Objects.requireNonNull(builder.base, "base may not be null."); Objects.requireNonNull(builder.term, "term may not be null."); Objects.requireNonNull(builder.factor, "factor may not be null."); Objects.requireNonNull(builder.conversionContext, "exchangeRateType may not be null."); this.base = builder.base; this.term = builder.term; this.factor = builder.factor; this.conversionContext = builder.conversionContext; setExchangeRateChain(builder.rateChain); } /** * Internal method to set the rate chain, which also ensure that the chain * passed, when not null, contains valid elements. * * @param chain the chain to set. */ private void setExchangeRateChain(List chain) { this.chain.clear(); if (chain == null || chain.isEmpty()) { this.chain.add(this); } else { for (ExchangeRate aChain : chain) { if (chain == null) { throw new IllegalArgumentException("Chain element can not be null."); } } this.chain.addAll(chain); } } /** * Access the {@link javax.money.convert.ConversionContext} of {@link javax.money.convert.ExchangeRate}. * * @return the conversion context, never null. */ public final ConversionContext getContext() { return this.conversionContext; } /** * Get the base (source) {@link javax.money.CurrencyUnit}. * * @return the base {@link javax.money.CurrencyUnit}. */ public final CurrencyUnit getBaseCurrency() { return this.base; } /** * Get the term (target) {@link javax.money.CurrencyUnit}. * * @return the term {@link javax.money.CurrencyUnit}. */ public final CurrencyUnit getCurrency() { return this.term; } /** * Access the rate's bid factor. * * @return the bid factor for this exchange rate, or {@code null}. */ public final NumberValue getFactor() { return this.factor; } /** * Access the chain of exchange rates. * * @return the chain of rates, in case of a derived rate, this may be * several instances. For a direct exchange rate, this equals to * new ExchangeRate[]{this}. */ public final List getExchangeRateChain() { return this.chain; } /** * Allows to evaluate if this exchange rate is a derived exchange rate. * Derived exchange rates are defined by an ordered list of subconversions * with intermediate steps, whereas a direct conversion is possible in one * steps. *

* This method always returns {@code true}, if the chain contains more than * one rate. Direct rates, have also a chain, but with exact one rate. * * @return true, if the exchange rate is derived. */ public final boolean isDerived() { return this.chain.size() > 1; } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(ExchangeRate o) { if (o == null) { return -1; } int compare = this.getBaseCurrency().getCurrencyCode().compareTo(o.getBaseCurrency().getCurrencyCode()); if (compare == 0) { compare = this.getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); } if (compare == 0) { compare = this.getContext().getProviderName().compareTo(o.getContext().getProviderName()); } return compare; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "ExchangeRate [base=" + base + ", factor=" + factor + ", conversionContext=" + conversionContext + "]"; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((base == null) ? 0 : base.hashCode()); result = prime * result + ((conversionContext == null) ? 0 : conversionContext.hashCode()); result = prime * result + ((factor == null) ? 0 : factor.hashCode()); result = prime * result + ((term == null) ? 0 : term.hashCode()); result = prime * result + ((chain == null) ? 0 : chain.hashCode()); return result; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TestExchangeRate other = (TestExchangeRate) obj; if (base == null) { if (other.base != null) { return false; } } else if (!base.equals(other.base)) { return false; } if (!chain.equals(other.getExchangeRateChain())) { return false; } if (conversionContext == null) { if (other.conversionContext != null) { return false; } } else if (!conversionContext.equals(other.conversionContext)) { return false; } if (factor == null) { if (other.factor != null) { return false; } } else if (!factor.equals(other.factor)) { return false; } if (term == null) { if (other.term != null) { return false; } } else if (!term.equals(other.term)) { return false; } return true; } /** * Builder for creating new instances of {@link javax.money.convert.ExchangeRate}. Note that * instances of this class are not thread-safe. * * @author Anatole Tresch * @author Werner Keil */ public static class Builder { /** * The {@link javax.money.convert.ConversionContext}. */ private ConversionContext conversionContext; /** * The base (source) currency. */ private CurrencyUnit base; /** * The term (target) currency. */ private CurrencyUnit term; /** * The conversion factor. */ private NumberValue factor; /** * The chain of invovled rates. */ private List rateChain = new ArrayList<>(); /** * Sets the exchange rate type * * @param rateType the {@link javax.money.convert.RateType} contained */ public Builder(String provider, RateType rateType) { this(ConversionContext.of(provider, rateType)); } /** * Sets the exchange rate type * * @param context the {@link javax.money.convert.ConversionContext} to be applied */ public Builder(ConversionContext context) { setContext(context); } /** * Sets the exchange rate type * * @param rate the {@link javax.money.convert.ExchangeRate} to be applied */ public Builder(ExchangeRate rate) { setContext(rate.getContext()); setFactor(rate.getFactor()); setTerm(rate.getCurrency()); setBase(rate.getBaseCurrency()); setRateChain(rate.getExchangeRateChain()); } /** * Sets the base {@link javax.money.CurrencyUnit} * * @param base to base (source) {@link javax.money.CurrencyUnit} to be applied * @return the builder instance */ public Builder setBase(CurrencyUnit base) { this.base = base; return this; } /** * Sets the terminating (target) {@link javax.money.CurrencyUnit} * * @param term to terminating {@link javax.money.CurrencyUnit} to be applied * @return the builder instance */ public Builder setTerm(CurrencyUnit term) { this.term = term; return this; } /** * Sets the {@link javax.money.convert.ExchangeRate} chain. * * @param exchangeRates the {@link javax.money.convert.ExchangeRate} chain to be applied * @return the builder instance */ public Builder setRateChain(ExchangeRate... exchangeRates) { this.rateChain.clear(); if (exchangeRates != null) { this.rateChain.addAll(Arrays.asList(exchangeRates.clone())); } return this; } /** * Sets the {@link javax.money.convert.ExchangeRate} chain. * * @param exchangeRates the {@link javax.money.convert.ExchangeRate} chain to be applied * @return the builder instance */ public Builder setRateChain(List exchangeRates) { this.rateChain.clear(); if (exchangeRates != null) { this.rateChain.addAll(exchangeRates); } return this; } /** * Sets the conversion factor, as the factor * {@code base * factor = target}. * * @param factor the factor. * @return The builder instance. */ public Builder setFactor(NumberValue factor) { this.factor = factor; return this; } /** * Sets the provider to be applied. * * @param conversionContext the {@link javax.money.convert.ConversionContext}, not null. * @return The builder. */ public Builder setContext(ConversionContext conversionContext) { Objects.requireNonNull(conversionContext); this.conversionContext = conversionContext; return this; } /** * Builds a new instance of {@link javax.money.convert.ExchangeRate}. * * @return a new instance of {@link javax.money.convert.ExchangeRate}. * @throws IllegalArgumentException if the rate could not be built. */ public TestExchangeRate build() { return new TestExchangeRate(this); } /** * Initialize the {@link Builder} with an {@link javax.money.convert.ExchangeRate}. This is * useful for creating a new rate, reusing some properties from an * existing one. * * @param rate the base rate * @return the Builder, for chaining. */ public Builder setRate(ExchangeRate rate) { this.base = rate.getBaseCurrency(); this.term = rate.getCurrency(); this.conversionContext = rate.getContext(); this.factor = rate.getFactor(); this.rateChain = rate.getExchangeRateChain(); this.term = rate.getCurrency(); return this; } } /** * Create a {@link Builder} based on the current rate instance. * * @return a new {@link Builder}, never {@code null}. */ public Builder toBuilder() { return new Builder(getContext()).setBase(getBaseCurrency()).setTerm(getCurrency()) .setFactor(getFactor()).setRateChain(getExchangeRateChain()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy