Skip to content
Browse files

add ExchangeRate and (Big)Money conversion mechanisms (reverse-merged…

… from commit e3d81c8)

Undo this commit which should have been on a branch, not master
  • Loading branch information...
1 parent 7c61b61 commit 1ec3d9a1eb44f6e69a705e06c1d3e48507ad4242 @jodastephen jodastephen committed Jan 3, 2013
View
196 src/main/java/org/joda/money/DefaultExchangeRateOperations.java
@@ -1,196 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A default implementation of {@link ExchangeRateOperations} operating on a externally provided {@link ExchangeRate}.
- *
- * @author tpasierb
- */
-class DefaultExchangeRateOperations implements ExchangeRateOperations {
-
- private ExchangeRate exchangeRate;
- private final int scale;
- private final RoundingMode roundingMode;
-
- DefaultExchangeRateOperations(ExchangeRate exchangeRate, int scale, RoundingMode roundingMode) {
- Utils.notNull(roundingMode, "Exchange rate must not be null");
- Utils.isTrue(scale >= 0, "Scale must be greater or equal to 0");
- this.exchangeRate = exchangeRate;
- this.scale = scale;
- this.roundingMode = roundingMode;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.joda.money.ExchangeRateOperations#invert()
- */
- public ExchangeRateOperations invert() {
- this.exchangeRate = ExchangeRate.of(BigDecimal.ONE.divide(exchangeRate.getRate(), scale, roundingMode), exchangeRate.getTarget(),
- exchangeRate.getSource());
- return this;
- }
-
- /**
- * Converts the given {@link BigMoney} to an equivalent in the other currency.
- *
- * This object's rate is used directly if the given Money's currency is equal to this exchange rate's target
- * currency. Otherwise this exchange rate is inverted before the conversion is made.
- *
- * The following formula is used for calculation:<br>
- *
- * <p>
- * 1 major unit of <strong><code>target currency</code></strong> = <strong><code>rate</code></strong> major units of
- * <strong><code>source currency</code></strong>.
- * </p>
- *
- * For the following example exchange rate: <code>1 USD = 2.3428 PLN</code><br/>
- * passing {@link Money 100 USD} as an argument the method will return the equivalent in PLN. On the other hand when
- * the method receives {@link Money 20 PLN} it will return an equivalent in USD.
- *
- * @see ExchangeRateOperations
- * @param toExchange the value in currency to be exchanged
- * @return the equivalent in other currency
- * @throws NullPointerException if the given parameter is <code>null</code>
- * @throws NotExchangeableException if this exchange rate cannot be used for conversion of the given {@link Money}
- */
- public BigMoney exchange(BigMoney toExchange) {
- Utils.notNull(toExchange, "Money to exchange cannot be null");
-
- BigDecimal rate = null;
- CurrencyUnit resultingCurrency = null;
-
- if (toExchange.getCurrencyUnit().equals(exchangeRate.getTarget())) {
- resultingCurrency = exchangeRate.getSource();
- rate = exchangeRate.getRate();
- } else if (toExchange.getCurrencyUnit().equals(exchangeRate.getSource())) {
- resultingCurrency = exchangeRate.getTarget();
- rate = invert().getExchangeRate().getRate();
- } else {
- throw new NotExchangeableException(toExchange, exchangeRate);
- }
-
- return BigMoney.of(resultingCurrency, toExchange.getAmount().multiply(rate));
- }
-
- /**
- * Uses this ExchangeRate's rounding mode for creating the resulting {@link Money} instance.
- *
- * @param toExchange the value in currency to be exchanged
- * @return the equivalent in other currency
- * @see ExchangeRate#exchange(BigMoney)
- */
- public Money exchange(Money toExchange) {
- return Money.of(exchange(BigMoney.of(toExchange)), this.roundingMode);
- }
-
- /**
- * Combines this object with the given one. This {@link ExchangeRate} and the given one have to have a common
- * currency no matter the position "source" or "target".
- *
- * This object's non common currency will become the target currency and the other object's non common currency will
- * become the source currency of the returned object. The common currency will "disappear".
- *
- * <br>
- * Example:
- *
- * <pre>
- * this rate: 1 USD = 3.50 PLN
- * other rate: 1 EUR = 4.00 PLN
- *
- * this.combine(other) results in 1 USD = 0.8750 EUR
- * other.combine(this) results in 1 EUR = 1.1429 USD (rounded to 4 decimal places *)
- * </pre>
- *
- * A special case is when exchange rates for the same sets of currencies are combined no matter the position. In
- * this case they may or may not differ on the rate field. Combining two such exchange rate will result in
- * "identity" rate for <code>this</code> rate's target currency.
- *
- * <br>
- * Example:
- *
- * <pre>
- * this rate: 1 EUR = 3.22 PLN
- * other rate: 1 EUR = 3.19 PLN
- *
- * this.combine(other) results in 1 EUR = 1 EUR.
- * </pre>
- *
- * The resulting ExchangeRate will have the scale and roundingMode of this instance.
- *
- * <pre>
- * * rounding for this example only, internally scale may be greater
- * </pre>
- *
- * @param other the exchange rate to be combine with this instance
- * @return the combined exchange rate
- * @throws NullPointerException if the other object is null
- * @throws NoCommonCurrencyException if objects this and other have no common currency which means that it is
- * impossible to create a combination of the two exchange rates
- */
- public ExchangeRateOperations combine(ExchangeRate other) {
- Utils.notNull(other, "Exchange rate to be combined must not be null");
-
- CurrencyUnit commonCurrency = null;
-
- Set<CurrencyUnit> currencies = new HashSet<CurrencyUnit>();
-
- currencies.add(other.getSource());
- currencies.add(other.getTarget());
- if (!currencies.add(exchangeRate.getTarget())) {
- commonCurrency = exchangeRate.getTarget();
- }
- if (!currencies.add(exchangeRate.getSource())) {
- commonCurrency = exchangeRate.getSource();
- }
-
- if (commonCurrency == null) {
- throw new NoCommonCurrencyException(exchangeRate, other);
- }
-
- ExchangeRate a = exchangeRate;
- ExchangeRate b = other;
-
- if (!a.getSource().equals(commonCurrency)) {
- a = a.operations(scale, roundingMode).invert().getExchangeRate();
- }
- if (!b.getSource().equals(commonCurrency)) {
- b = b.operations(scale, roundingMode).invert().getExchangeRate();
- }
-
- BigDecimal newRate = null;
-
- if (b.getTarget() == a.getTarget()) {
- newRate = BigDecimal.ONE;
- } else {
- newRate = a.getRate().divide(b.getRate(), scale, roundingMode).stripTrailingZeros();
- }
-
- this.exchangeRate = ExchangeRate.of(newRate, b.getTarget(), a.getTarget());
- return this;
- }
-
- public ExchangeRate getExchangeRate() {
- return exchangeRate;
- }
-
-}
View
260 src/main/java/org/joda/money/ExchangeRate.java
@@ -1,260 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Represents an exchange rate that allows {@link BigMoneyProvider} instances to be converted between amounts in
- * different {@link CurrencyUnit}s. Exchange rate is an object that holds data about two {@link CurrencyUnit}s and a
- * conversion rate, and basically mirrors the following equation:
- *
- * <p>
- * 1 major unit of <strong><code>target currency</code></strong> = <strong><code>rate</code></strong> major units of
- * <strong><code>source currency</code></strong>.
- * </p>
- *
- * Exchange rates provide a few simple operations that can be performed with them through {@link ExchangeRateOperations}
- * . A default implementation can be acquired using {@link #operations(int, RoundingMode)}.
- *
- * <p>
- * Instances of this class are immutable.
- * </p>
- *
- * <p>
- * Two instances of this class are considered equal when both target and source currencies are the same and the rates
- * compare equal according to {@link BigDecimal#compareTo(BigDecimal)}.
- * </p>
- *
- * Please be aware that creating an exchange rate less or equal to 0 is considered an error.
- *
- * @author Tom Pasierb
- */
-public final class ExchangeRate implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private static final Pattern PARSE_REGEX = Pattern.compile("^1\\s+([A-Z]{3})\\s+=\\s([-+]?\\d+(.\\d+)?)\\s+([A-Z]{3})$");
-
- /**
- * Conversion rate between source and target currency.
- */
- private final BigDecimal rate;
-
- /**
- * The source currency.
- */
- private final CurrencyUnit source;
-
- /**
- * The target currency.
- */
- private final CurrencyUnit target;
-
- public static ExchangeRate parse(String exchangeRate) {
- Utils.notNull(exchangeRate, "Exchange rate cannot be null");
-
- String exr = exchangeRate.trim();
- Matcher matcher = PARSE_REGEX.matcher(exr);
- if (!matcher.matches()) {
- throw new IllegalArgumentException(String.format("Exchange rate '%s' cannot be parsed", exchangeRate));
- }
-
- String source, target, rate;
- target = matcher.group(1);
- rate = matcher.group(2);
- source = matcher.groupCount() == 4 ? matcher.group(4) : matcher.group(3);
- return ExchangeRate.of(new BigDecimal(rate), CurrencyUnit.getInstance(source), CurrencyUnit.getInstance(target));
- }
-
- /**
- * Creates an identity rate for the given {@link CurrencyUnit}. The rate will be equal to 1.00 and both source and
- * target currency units will be the same as the given one.
- *
- * @param currency to be set for source and target currency units.
- * @throws NullPointerException if the given parameter is <code>null</code>
- * @return identity exchange rate having rate 1 and the same source and target currencies
- */
- public static ExchangeRate identity(CurrencyUnit currency) {
- return new ExchangeRate(BigDecimal.ONE, currency, currency);
- }
-
- /**
- * Creates an Exchange rate with the given parameters.
- *
- * @param rate the conversion rate
- * @param source source {@link CurrencyUnit}
- * @param target target {@link CurrencyUnit}
- * @throws NullPointerException if any of the given parameters is <code>null</code>
- * @throws IllegalArgumentException if the given rate is less or equal to 0 or when source and currency units are
- * equal and rate is not equal to 1
- * @return an instance of exchange rate parameterized as instructed
- */
- public static ExchangeRate of(BigDecimal rate, CurrencyUnit source, CurrencyUnit target) {
- return new ExchangeRate(rate, source, target);
- }
-
- /**
- * Constructs an instance of ExchangeRate with the given parameters.
- *
- * @param rate the conversion rate
- * @param source source {@link CurrencyUnit}
- * @param target target {@link CurrencyUnit}
- * @throws NullPointerException if any of the given parameters is <code>null</code>
- * @throws IllegalArgumentException if the given rate is less or equal to 0 or when source and currency units are
- * equal and rate is not equal to 1
- * @return an instance of exchange rate parameterized as instructed
- */
- ExchangeRate(BigDecimal rate, CurrencyUnit source, CurrencyUnit target) {
- Utils.notNull(rate, "Rate must not be null");
- Utils.notNull(source, "Source currency must not be null");
- Utils.notNull(target, "Target currency must not be null");
- Utils.isTrue(rate.compareTo(BigDecimal.ZERO) > 0, "Rate must be greater than 0");
- if (source == target) {
- Utils.isTrue(rate.compareTo(BigDecimal.ONE) == 0,
- "Rate must be 1 if source and target currencies are the same, got rate=%f, source=%s, target=%s", rate, source, target);
- }
- this.rate = rate;
- this.source = source;
- this.target = target;
- }
-
- /**
- * Creates an ExchangeRate like this one with the given rate.
- *
- * @param rate the rate that the new ExchangeRate should have
- * @return a copy of this with the given rate and all other settings as this one
- * @see #of(BigDecimal, CurrencyUnit, CurrencyUnit)
- */
- public ExchangeRate withRate(BigDecimal rate) {
- return new ExchangeRate(rate, source, target);
- }
-
- /**
- * Returns the conversion rate.
- *
- * @return the conversion rate between this exchange rate's source and target currencies
- */
- public BigDecimal getRate() {
- return rate;
- }
-
- /**
- * Returns the source currency.
- *
- * @return the source currency
- */
- public CurrencyUnit getSource() {
- return source;
- }
-
- /**
- * Returns the target currency.
- *
- * @return the target currency
- */
- public CurrencyUnit getTarget() {
- return target;
- }
-
- /**
- * Creates an instance of an object that knows how to perform operations on {@link ExchangeRate}s.
- *
- * @param scale scale that will be used for calculations
- * @param roundingMode rounding mode to use if rounding is necessary
- * @return an instance of {@link ExchangeRateOperations}
- */
- public ExchangeRateOperations operations(int scale, RoundingMode roundingMode) {
- return new DefaultExchangeRateOperations(this, scale, roundingMode);
- }
-
- /**
- * Creates an instance of an object that knows how to perform operations on {@link ExchangeRate}s with default
- * values for <code>scale</code> and <code>roundingMode</code> that will be used for calculations. The values used
- * as defaults are:
- * <ul>
- * <li>scale = 16</li>
- * <li>roundingMode = {@link RoundingMode#HALF_EVEN}</li>
- * </ul>
- *
- * @return an instance of {@link ExchangeRateOperations}
- */
- public ExchangeRateOperations operations() {
- return new DefaultExchangeRateOperations(this, 16, RoundingMode.HALF_EVEN);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((rate == null) ? 0 : rate.hashCode());
- result = prime * result + ((source == null) ? 0 : source.hashCode());
- result = prime * result + ((target == null) ? 0 : target.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof ExchangeRate)) {
- return false;
- }
- ExchangeRate other = (ExchangeRate) obj;
- boolean equal = true;
- equal &= rate == null ? other.rate == null : rate.compareTo(other.rate) == 0;
- if (!equal) {
- return equal;
- }
- equal &= source == null ? other.source == null : source.equals(other.source);
- if (!equal) {
- return equal;
- }
- equal &= target == null ? other.target == null : target.equals(other.target);
- return equal;
- }
-
- @Override
- public String toString() {
- return "ExchangeRate[1 " + target + " = " + rate + " " + source + "]";
- }
-
- /**
- * Block malicious data streams.
- *
- * @param ois the input stream, not null
- * @throws InvalidObjectException
- */
- private void readObject(ObjectInputStream ois) throws InvalidObjectException {
- throw new InvalidObjectException("Serialization delegate required");
- }
-
- /**
- * Uses a serialization delegate.
- *
- * @return the replacing object, never null
- */
- private Object writeReplace() {
- return new Ser(Ser.EXCHANGE_RATE, this);
- }
-}
View
111 src/main/java/org/joda/money/ExchangeRateOperations.java
@@ -1,111 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-/**
- * Defines operations performed on {@link ExchangeRate}s. Object implementing this interface are contextual objects
- * operating on "current" ExchangeRate. Some operations modify the underlying ExchageRate and others just perform
- * operations using the underlying ExchangeRate. The reasoning behind having this can of object is that one can create
- * such an object with the given settings and then perform serveral operations with the same settings.
- *
- * @author tpasierb
- */
-public interface ExchangeRateOperations {
-
- /**
- * Converts the given {@link BigMoney value in currency} to value in the other currency using the underlying
- * ExchangeRate. The conversion is possible if either source or target currency of the exchange rate represented by
- * this object matches the currency of the given {@link BigMoney}.
- *
- * @param toExchange the value in currency to be converted
- * @return the equivalent in other currency
- * @throws NullPointerException if the given parameter is <code>null</code>
- * @throws NotExchangeableException if exchange rate represented by this object cannot be used for conversion of the
- * given {@link Money}
- */
- BigMoney exchange(BigMoney toExchange);
-
- /**
- * Converts the given {@link Money value in currency} to value in the other currency returning an instance of
- * {@link Money}.
- *
- * @param toExchange the value in currency to be exchanged
- * @return the equivalent in other currency
- * @see ExchangeRateOperations#exchange(BigMoney)
- */
- Money exchange(Money toExchange);
-
- /**
- * Inverts the underlying ExchangeRate. The operation involves swapping currencies and inverting the rate. The
- * inverted rate can be retrieved using {@link #getExchangeRate()}.
- *
- * @return ExchangeRateOperations this contextual operations object allowing fluent calls
- */
- ExchangeRateOperations invert();
-
- /**
- * Combines {@link ExchangeRate} represented by this object with the given one. This {@link ExchangeRate} and the
- * given one have to have a common currency no matter the position "source" or "target".
- *
- * This object's non common currency will become the target currency and the other object's non common currency will
- * become the source currency of the returned object. The common currency will "disappear".
- *
- * <br>
- * Example:
- *
- * <pre>
- * this rate: 1 USD = 3.50 PLN
- * other rate: 1 EUR = 4.00 PLN
- *
- * this.combine(other) results in 1 USD = 0.8750 EUR
- * other.combine(this) results in 1 EUR = 1.1429 USD (rounded to 4 decimal places *)
- * </pre>
- *
- * A special case is when exchange rates for the same sets of currencies are combined no matter the position. In
- * this case they may or may not differ on the rate field. Combining two such exchange rate will result in
- * "identity" rate for <code>this</code> rate's target currency.
- *
- * <br>
- * Example:
- *
- * <pre>
- * this rate: 1 EUR = 3.22 PLN
- * other rate: 1 EUR = 3.19 PLN
- *
- * this.combine(other) results in 1 EUR = 1 EUR.
- * </pre>
- *
- * The resulting ExchangeRate will have the scale and roundingMode of this instance.
- *
- * <pre>
- * * rounding for this example only, internally scale may be greater
- * </pre>
- *
- * @param other the exchange rate to be combine with this instance
- * @return this contextual operations object allowing fluent calls
- * @throws NullPointerException if the other object is null
- * @throws NoCommonCurrencyException if objects this and other have no common currency which means that it is
- * impossible to create a combination of the two exchange rates
- */
- ExchangeRateOperations combine(ExchangeRate other);
-
- /**
- * Returns the current ExchangeRate.
- *
- * @return the current ExchangeRate
- */
- ExchangeRate getExchangeRate();
-}
View
44 src/main/java/org/joda/money/NoCommonCurrencyException.java
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-/**
- * Indicates that operatation requiring a common currency cannot be performed.
- *
- * @author tpasierb
- */
-public class NoCommonCurrencyException extends IllegalArgumentException {
-
- private static final long serialVersionUID = 1L;
-
- private final ExchangeRate first;
- private final ExchangeRate second;
-
- public NoCommonCurrencyException(ExchangeRate first, ExchangeRate second) {
- super(String.format("Exchange rates have no common currency: %s vs %s", first, second));
- this.first = first;
- this.second = second;
- }
-
- public ExchangeRate getFirst() {
- return first;
- }
-
- public ExchangeRate getSecond() {
- return second;
- }
-
-}
View
46 src/main/java/org/joda/money/NotExchangeableException.java
@@ -1,46 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-/**
- * Indicates that conversion cannot be performed.
- *
- * @author tpasierb
- */
-public class NotExchangeableException extends IllegalArgumentException {
-
- private static final long serialVersionUID = 1L;
-
- private final BigMoneyProvider money;
-
- private final ExchangeRate exchangeRate;
-
- public NotExchangeableException(BigMoneyProvider money, ExchangeRate exchangeRate) {
- super(String.format("%s is not exchangeable using %s", money != null ? money : "Money <null>", exchangeRate != null ? exchangeRate
- : "ExchangeRate <null>"));
- this.money = money;
- this.exchangeRate = exchangeRate;
- }
-
- public BigMoneyProvider getMoney() {
- return money;
- }
-
- public ExchangeRate getExchangeRate() {
- return exchangeRate;
- }
-
-}
View
40 src/main/java/org/joda/money/Ser.java
@@ -37,9 +37,7 @@
/** Type for Money. */
static final byte MONEY = 'M';
/** Type for CurrencyUnit. */
- static final byte CURRENCY_UNIT = 'C'; // not in use yet
- /** Type for ExchangeRate. */
- static final byte EXCHANGE_RATE = 'E';
+ static final byte CURRENCY_UNIT = 'C'; // not in use yet
/** The type. */
private byte type;
@@ -89,11 +87,6 @@ public void writeExternal(ObjectOutput out) throws IOException {
writeCurrency(out, obj);
return;
}
- case EXCHANGE_RATE: {
- ExchangeRate obj = (ExchangeRate) object;
- writeExchangeRate(out, obj);
- return;
- }
}
throw new InvalidClassException("Joda-Money bug: Serialization broken");
}
@@ -112,18 +105,8 @@ private void writeCurrency(ObjectOutput out, CurrencyUnit obj) throws IOExceptio
out.writeShort(obj.getDefaultFractionDigits());
}
- private void writeExchangeRate(ObjectOutput out, ExchangeRate obj) throws IOException {
- writeCurrency(out, obj.getSource());
- writeCurrency(out, obj.getTarget());
- // write BigDecimal representing the conversion rate
- byte[] bytes = obj.getRate().unscaledValue().toByteArray();
- out.writeInt(bytes.length);
- out.write(bytes);
- out.writeInt(obj.getRate().scale());
- }
-
/**
- * Reads the data in from the stream.
+ * Outputs the data.
*
* @param in the input stream
* @throws IOException if an error occurs
@@ -143,10 +126,6 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
object = readCurrency(in);
return;
}
- case EXCHANGE_RATE: {
- object = readExchangeRate(in);
- return;
- }
}
throw new StreamCorruptedException("Serialization input has invalid type");
}
@@ -172,21 +151,6 @@ private CurrencyUnit readCurrency(ObjectInput in) throws IOException {
return singletonCurrency;
}
- private ExchangeRate readExchangeRate(ObjectInput in) throws IOException {
- CurrencyUnit source = readCurrency(in);
- CurrencyUnit target = readCurrency(in);
- byte[] bytes = new byte[in.readInt()];
- in.readFully(bytes);
- BigDecimal rate = new BigDecimal(new BigInteger(bytes), in.readInt());
- try {
- return new ExchangeRate(rate, source, target);
- } catch (RuntimeException e) {
- throw new InvalidObjectException(
- "Deserialization was not possible, the serialized form of the object may have been tampered with, cause: "
- + e.getMessage());
- }
- }
-
/**
* Returns the object that will replace this one.
*
View
75 src/main/java/org/joda/money/Utils.java
@@ -1,75 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-/**
- * Common utility methods for use in the current package.
- *
- * @author tpasierb
- */
-class Utils {
-
- /**
- * <p>
- * Validate that the specified argument is not {@code null}; otherwise throwing an exception with the specified
- * message.
- *
- * <pre>
- * Validate.notNull(myObject, &quot;The object must not be null&quot;);
- * </pre>
- *
- * @param <T> the object type
- * @param object the object to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message
- * @return the validated object (never {@code null} for method chaining)
- * @throws NullPointerException if the object is {@code null}
- *
- * // copied from commons-lang3
- */
- public static <T> T notNull(T object, String message, Object... values) {
- if (object == null) {
- throw new NullPointerException(String.format(message, values));
- }
- return object;
- }
-
- /**
- * <p>
- * Validate that the argument condition is {@code true}; otherwise throwing an exception with the specified message.
- * This method is useful when validating according to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.
- * </p>
- *
- * <pre>
- * Validate.isTrue(i &gt;= min &amp;&amp; i &lt;= max, &quot;The value must be between %d and %d&quot;, min, max);
- * Validate.isTrue(myObject.isOk(), &quot;The object is not okay&quot;);
- * </pre>
- *
- * @param expression the boolean expression to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if expression is {@code false}
- *
- * // copied from commons-lang3
- */
- public static void isTrue(boolean expression, String message, Object... values) {
- if (expression == false) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
-}
View
354 src/test/java/org/joda/money/TestExchangeRate.java
@@ -1,354 +0,0 @@
-/*
- * Copyright 2009-2011 Stephen Colebourne
- *
- * 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.
- */
-package org.joda.money;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-
-import org.testng.annotations.Test;
-
-@Test
-public class TestExchangeRate {
-
- private static CurrencyUnit PLN = CurrencyUnit.of("PLN");
- private static CurrencyUnit EUR = CurrencyUnit.EUR;
-
- private ExchangeRate PLN_TO_EUR_RATE = ExchangeRate.of(new BigDecimal("3.3710"), PLN, CurrencyUnit.EUR);
- private ExchangeRate USD_RATE = ExchangeRate.of(new BigDecimal("2.3428"), PLN, CurrencyUnit.USD);
- private ExchangeRate JPY_RATE = ExchangeRate.of(new BigDecimal("0.022336"), PLN, CurrencyUnit.JPY);
- private ExchangeRate GBP_RATE = ExchangeRate.of(new BigDecimal("4.1921"), PLN, CurrencyUnit.GBP);
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_factory_of_identity_nullCurrency() {
- ExchangeRate.identity(null);
- }
-
- @Test
- public void test_factory_of_identity_notNullCurrency() {
- ExchangeRate identity = ExchangeRate.identity(CurrencyUnit.EUR);
- assertTrue(BigDecimal.ONE.compareTo(identity.getRate()) == 0);
- assertEquals(identity.getSource(), CurrencyUnit.EUR);
- assertEquals(identity.getTarget(), CurrencyUnit.EUR);
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_factory_of_null_rate() {
- ExchangeRate.of(null, EUR, EUR);
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_factory_of_null_source() {
- ExchangeRate.of(new BigDecimal("1.2"), null, EUR);
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_factory_of_null_target() {
- ExchangeRate.of(new BigDecimal("1.2"), EUR, null);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_factory_of_negative_scale() {
- ExchangeRate.of(new BigDecimal("1.2"), EUR, CurrencyUnit.USD).operations(-1, RoundingMode.HALF_UP);
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_factory_of_null_rounding_mode() {
- ExchangeRate.of(new BigDecimal("1.2"), EUR, CurrencyUnit.USD).operations(2, null);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_factory_of_same_currencies_rate_not_one() {
- ExchangeRate.of(new BigDecimal("1.2"), EUR, EUR);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_factory_of_rate_zero() {
- ExchangeRate.of(new BigDecimal("0"), EUR, CurrencyUnit.USD);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_factory_of_rate_less_than_zero() {
- ExchangeRate.of(new BigDecimal("-0.01"), EUR, CurrencyUnit.USD);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_with_rate_zero() {
- USD_RATE.withRate(new BigDecimal("0"));
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_with_rate_less_than_zero() {
- USD_RATE.withRate(new BigDecimal("-1"));
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_exchange_nullOtherExchangeRate() {
- Money toExchange = null;
- USD_RATE.operations().exchange(toExchange);
- }
-
- @Test(expectedExceptions = NotExchangeableException.class)
- public void test_money_not_exchangeable_by_exchangeRate() {
- Money amountInUSD = Money.of(CurrencyUnit.USD, new BigDecimal("23.56"));
- PLN_TO_EUR_RATE.operations().exchange(amountInUSD);
- }
-
- @Test
- public void testPlnToUsd() {
- Money a1 = Money.of(PLN, new BigDecimal("24.555"), RoundingMode.HALF_UP);
-
- Money result = USD_RATE.operations().exchange(a1);
-
- assertEquals(USD_RATE.getTarget(), result.getCurrencyUnit());
- assertEquals(new BigDecimal("10.48"), result.getAmount());
- }
-
- @Test
- public void testUsdToPln() {
-
- Money a1 = Money.of(CurrencyUnit.USD, new BigDecimal("24.56"));
-
- Money result = USD_RATE.operations().exchange(a1);
-
- assertEquals(USD_RATE.getSource(), result.getCurrencyUnit());
- assertEquals(new BigDecimal("57.54"), result.getAmount());
- }
-
- @Test
- public void testPlnToJpy() {
-
- Money a1 = Money.of(PLN, new BigDecimal("49.89"));
-
- Money result = JPY_RATE.operations().exchange(a1);
-
- assertEquals(0, result.getAmount().scale());
- assertEquals(JPY_RATE.getTarget(), result.getCurrencyUnit());
- assertEquals(new BigDecimal("2234"), result.getAmount());
- }
-
- @Test
- public void testJpyToPln() {
-
- Money a1 = Money.of(CurrencyUnit.JPY, new BigDecimal("9811"));
-
- Money result = JPY_RATE.operations().exchange(a1);
-
- assertEquals(2, result.getAmount().scale());
- assertEquals(JPY_RATE.getSource(), result.getCurrencyUnit());
- assertEquals(new BigDecimal("219.14"), result.getAmount());
- }
-
- @Test
- public void test_exchange_using_identity_rate() {
- ExchangeRate identityRate = ExchangeRate.of(new BigDecimal("1"), PLN, PLN);
- Money a = Money.of(PLN, new BigDecimal("23.11"));
- Money exchanged = identityRate.operations().exchange(a);
- assertTrue(exchanged.getAmount().compareTo(a.getAmount()) == 0);
- }
-
- @Test
- public void testPrecisionSmallerThanGivenRate() {
- ExchangeRate rate = ExchangeRate.of(new BigDecimal("3.3741"), PLN, CurrencyUnit.USD);
- assertTrue(new BigDecimal("0.3").compareTo(rate.operations(2, RoundingMode.HALF_EVEN).invert().getExchangeRate().getRate()) == 0);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void testSameCurrencyRateDifferentThanOne() {
- ExchangeRate.of(new BigDecimal("3"), PLN, PLN);
- fail("construction of an exchange rate between the same source and target currencies and rate different than one should not be possible!");
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void combineNull() {
- ExchangeRate reversed = USD_RATE.operations().invert().getExchangeRate();
- reversed.operations().combine(null);
- }
-
- @Test(expectedExceptions = NoCommonCurrencyException.class)
- public void combineInvalid() {
- ExchangeRate reversed = USD_RATE.operations().invert().getExchangeRate();
- ExchangeRate other = ExchangeRate.of(new BigDecimal(2.11), CurrencyUnit.JPY, CurrencyUnit.getInstance("NOK"));
-
- reversed.operations().combine(other);
- }
-
- @Test
- public void combineValid() {
- ExchangeRate combined = PLN_TO_EUR_RATE.operations().combine(GBP_RATE).getExchangeRate();
- assertEquals(CurrencyUnit.EUR, combined.getTarget());
- assertEquals(CurrencyUnit.GBP, combined.getSource());
- BigDecimal expectedRate = PLN_TO_EUR_RATE.getRate().divide(GBP_RATE.getRate(), 16, RoundingMode.HALF_EVEN);
- assertTrue(expectedRate.compareTo(combined.getRate()) == 0);
-
- Money m = Money.of(CurrencyUnit.EUR, new BigDecimal("30"));
- Money o = combined.operations().exchange(m);
-
- assertEquals(new BigDecimal("24.12"), o.getAmount());
- assertEquals(CurrencyUnit.GBP, o.getCurrencyUnit());
- }
-
- @Test
- public void combineCommentExample() {
- ExchangeRate USD = ExchangeRate.of(new BigDecimal("3.50"), PLN, CurrencyUnit.USD);
- ExchangeRate EUR = ExchangeRate.of(new BigDecimal("4.00"), PLN, CurrencyUnit.EUR);
- ExchangeRate combined = USD.operations().combine(EUR).getExchangeRate();
-
- assertNotNull(combined);
- assertTrue(new BigDecimal("0.875").compareTo(combined.getRate()) == 0);
- assertEquals(CurrencyUnit.EUR, combined.getSource());
- assertEquals(CurrencyUnit.USD, combined.getTarget());
- }
-
- @Test
- public void combineExchangeRatesOnlyRateDifferent() {
- ExchangeRate EUR1 = ExchangeRate.of(new BigDecimal("3.22"), PLN, CurrencyUnit.EUR);
- ExchangeRate EUR2 = ExchangeRate.of(new BigDecimal("3.19"), PLN, CurrencyUnit.EUR);
- ExchangeRate combined = EUR1.operations().combine(EUR2).getExchangeRate();
-
- // EUR1's target should become the combined rate's target currency
- assertNotNull(combined);
- assertTrue(new BigDecimal("1").compareTo(combined.getRate()) == 0);
- assertEquals(CurrencyUnit.EUR, combined.getSource());
- assertEquals(CurrencyUnit.EUR, combined.getTarget());
-
- EUR1 = ExchangeRate.of(new BigDecimal("3.22"), PLN, CurrencyUnit.EUR);
- EUR2 = ExchangeRate.of(new BigDecimal("0.3135"), CurrencyUnit.EUR, PLN);
- combined = EUR1.operations().combine(EUR2).getExchangeRate();
-
- // EUR1's target should become the combined rate's target currency ... no matter the other's target and source
- // positions
- assertNotNull(combined);
- assertTrue(new BigDecimal("1").compareTo(combined.getRate()) == 0);
- assertEquals(CurrencyUnit.EUR, combined.getSource());
- assertEquals(CurrencyUnit.EUR, combined.getTarget());
- }
-
- @Test
- public void test_serialization() throws IOException, ClassNotFoundException {
- ExchangeRate rateOut = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(rateOut);
-
- byte[] arr = baos.toByteArray();
- ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(arr));
- ExchangeRate rateIn = (ExchangeRate) ois.readObject();
-
- assertNotNull(rateIn);
- assertEquals(rateOut, rateIn);
- }
-
- @Test(expectedExceptions = InvalidObjectException.class)
- public void test_deserialize_malicious_stream() throws IOException, ClassNotFoundException {
- // serialized form of ExchangeRate.of(new BigDecimal("-0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- byte[] in = new byte[] { (byte) 0xac, (byte) 0xed, 0x00, 0x05, 0x73, 0x72, 0x00, 0x12, 0x6f, 0x72, 0x67, 0x2e, 0x6a, 0x6f, 0x64,
- 0x61, 0x2e, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x2e, 0x53, 0x65, 0x72, 0x73, (byte) 0x8e, 0x13, (byte) 0xd0, (byte) 0xe9, 0x6b,
- (byte) 0xa0, (byte) 0xe2, 0x0c, 0x00, 0x00, 0x78, 0x70, 0x77, 0x1c, 0x45, 0x00, 0x03, 0x55, 0x53, 0x44, 0x03, 0x48, 0x00,
- 0x02, 0x00, 0x03, 0x45, 0x55, 0x52, 0x03, (byte) 0xd2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, (byte) 0xa5, 0x00, 0x00, 0x00,
- 0x02, 0x78 };
- ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(in));
- ois.readObject();
- }
-
- @Test
- public void test_hashcode_should_be_identical_for_equal_instances() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- ExchangeRate two = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
-
- assertEquals(one, two);
- assertTrue(one.hashCode() == two.hashCode());
- }
-
- @Test
- public void test_same_instances_should_be_equal() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- assertEquals(one, one);
- }
-
- @Test
- public void test_equals_object_of_other_type() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- assertFalse(one.equals(new BigDecimal("0")));
- }
-
- @Test
- public void test_equals_when_rate_differs() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- ExchangeRate two = ExchangeRate.of(new BigDecimal("0.92"), CurrencyUnit.USD, CurrencyUnit.EUR);
- assertFalse(one.equals(two));
- }
-
- @Test
- public void test_equals_when_source_currency_differs() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- ExchangeRate two = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.CHF, CurrencyUnit.EUR);
- assertFalse(one.equals(two));
- }
-
- @Test
- public void test_equals_when_target_currency_differs() {
- ExchangeRate one = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.EUR);
- ExchangeRate two = ExchangeRate.of(new BigDecimal("0.91"), CurrencyUnit.USD, CurrencyUnit.CHF);
- assertFalse(one.equals(two));
- }
-
- @Test(expectedExceptions = NullPointerException.class)
- public void test_parse_null_input() {
- ExchangeRate.parse(null);
- }
-
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void test_parse_illegal_input() {
- ExchangeRate.parse("ILLEGAL_INPUT");
- }
-
- @Test
- public void test_parse_legal_input_needs_trimming() {
- ExchangeRate rate = ExchangeRate.parse("\t1 EUR = 3.24 PLN \t ");
- assertEquals(rate.getTarget(), CurrencyUnit.EUR);
- assertEquals(rate.getSource(), PLN);
- assertEquals(rate.getRate(), new BigDecimal("3.24"));
- }
-
- @Test
- public void test_parse_legal_input_regular_case() {
- ExchangeRate rate = ExchangeRate.parse("1 GBP = 1.5485 USD");
- assertEquals(rate.getTarget(), CurrencyUnit.GBP);
- assertEquals(rate.getSource(), CurrencyUnit.USD);
- assertEquals(rate.getRate(), new BigDecimal("1.5485"));
- }
-
- @Test
- public void test_parse_legal_input_no_fractional_part() {
- ExchangeRate rate = ExchangeRate.parse("1 USD = 6 DKK");
- assertEquals(rate.getTarget(), CurrencyUnit.USD);
- assertEquals(rate.getSource(), CurrencyUnit.getInstance("DKK"));
- assertEquals(rate.getRate(), new BigDecimal("6"));
- }
-}

0 comments on commit 1ec3d9a

Please sign in to comment.
Something went wrong with that request. Please try again.