/*
* Copyright 2014 Mikhail Vorontsov
*
* 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 info.javaperformance.money;
import java.math.BigDecimal;
import java.math.MathContext;
/**
* Safe but slow Money implementation. Uses BigDecimal as a storage.
*/
class MoneyBigDecimal extends AbstractMoney {
private final BigDecimal m_value;
public MoneyBigDecimal( final BigDecimal value ) {
m_value = value;
}
public MoneyBigDecimal( final double value )
{
m_value = new BigDecimal( value, MathContext.DECIMAL64 ).stripTrailingZeros(); //decimal64 to match double
}
public MoneyBigDecimal( final String value )
{
//important - do not use DECIMAL64 context here - you will lose precision for huge values.
//at the same time using it is required for BigDecimal(double) constructor - it matches "double" range.
m_value = new BigDecimal( value );
}
public double toDouble() {
return m_value.doubleValue();
}
/**
* Convert this value into a BigDecimal. This method is also used for arithmetic calculations when necessary.
*
* @return This object as BigDecimal
*/
public BigDecimal toBigDecimal() {
return m_value;
}
/**
* Return this value with an opposite sign.
*
* @return A new object with the same value with a different sign
*/
public Money negate() {
return new MoneyBigDecimal( m_value.negate() );
}
/**
* Convert into a String in a plain notation with a decimal dot.
* @return a String in a plain notation with a decimal dot.
*/
@Override
public String toString() {
return m_value.toPlainString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MoneyBigDecimal that = (MoneyBigDecimal) o;
return m_value.equals(that.m_value);
}
@Override
public int hashCode() {
return m_value.hashCode();
}
protected Money add( final MoneyLong other )
{
return other.add( this ); //implemented in MoneyLong
}
@Override
protected int compareTo(MoneyLong other) {
return -(other.compareTo(this)); // flips the response with unary
}
/**
* Multiply the current object by the <code>long</code> value.
*
* @param multiplier Multiplier
* @return A new Money object normalized to the efficient representation if possible
*/
public Money multiply( final long multiplier ) {
final BigDecimal res = m_value.multiply(BigDecimal.valueOf(multiplier));
return MoneyFactory.fromBigDecimal( res );
}
/**
* Multiply the current object by the <code>double</code> value.
*
* @param multiplier Multiplier
* @return A new Money object normalized to the efficient representation if possible
*/
public Money multiply(double multiplier) {
return MoneyFactory.fromBigDecimal(
m_value.multiply(new BigDecimal(multiplier, MathContext.DECIMAL64), MathContext.DECIMAL64) );
}
/**
* Divide the current object by the given <code>long</code> divider.
*
* @param divider Divider
* @param precision Maximal precision to keep. We will round the next digit.
* @return A new Money object normalized to the efficient representation if possible
*/
public Money divide( final long divider, final int precision )
{
final BigDecimal res = m_value.divide( BigDecimal.valueOf( divider ), MathContext.DECIMAL64 ).stripTrailingZeros();
return truncate( res, precision );
}
/**
* Divide the current object by the given <code>long</code> divider.
*
* @param divider Divider
* @param precision Maximal precision to keep. We will round the next digit.
* @return A new Money object normalized to the efficient representation if possible
*/
public Money divide( final double divider, final int precision )
{
final BigDecimal res = m_value.divide( BigDecimal.valueOf( divider ), MathContext.DECIMAL64 ).stripTrailingZeros();
return truncate( res, precision );
}
/**
* Truncate the current value leaving no more than {@code maximalPrecision} signs after decimal point.
* The number will be rounded towards closest digit (0-4 -> 0; 5-9 -> 1)
*
* @param maximalPrecision Required precision
* @return A new Money object normalized to the efficient representation if possible
*/
private static Money truncate( final BigDecimal val, final int maximalPrecision ) {
MoneyFactory.checkPrecision( maximalPrecision );
final BigDecimal res = val.setScale( maximalPrecision, BigDecimal.ROUND_HALF_UP );
return MoneyFactory.fromBigDecimal( res );
}
/**
* Truncate the current value leaving no more than {@code maximalPrecision} signs after decimal point.
* The number will be rounded towards closest digit (0-4 -> 0; 5-9 -> 1)
*
* @param maximalPrecision Required precision
* @return A new Money object normalized to the efficient representation if possible
*/
public Money truncate( final int maximalPrecision ) {
if ( m_value.scale() <= maximalPrecision )
return this;
return truncate(m_value, maximalPrecision);
}
}