001/**
002 * Copyright (c) 2012, 2015, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta.function;
017
018import java.math.BigDecimal;
019import java.math.MathContext;
020import java.math.RoundingMode;
021import java.util.Objects;
022
023import javax.money.MonetaryAmount;
024import javax.money.MonetaryOperator;
025
026import org.javamoney.moneta.RoundedMoney;
027
028/**
029 * <p>This implementation uses a {@link MathContext} to does the rounding operations. The implementation will use the <b>precision</b>, in other words, the total number of digits in a number</p>
030 * <p>The derived class will implements the {@link RoundedMoney} with this rounding monetary operator</p>
031 *  <pre>
032 *   {@code
033 *
034 *     MathContext mathContext = new MathContext(4, RoundingMode.HALF_EVEN);
035 *     MonetaryOperator monetaryOperator = PrecisionContextRoundedOperator.of(mathContext);
036 *     CurrencyUnit real = Monetary.getCurrency("BRL");
037 *     MonetaryAmount money = Money.of(BigDecimal.valueOf(35.34567), real);
038 *     MonetaryAmount result = monetaryOperator.apply(money); // BRL 35.35
039 *
040 *    }
041* </pre>
042* <p>Case the parameter in {@link MonetaryOperator#apply(MonetaryAmount)} be null, the apply will return a {@link NullPointerException}</p>
043 * @author Otavio Santana
044 * @see {@link PrecisionContextRoundedOperator#of(MathContext)}
045 * @see {@link RoundedMoney}
046 * @see {@link MonetaryOperator}
047 * @see {@link BigDecimal#precision()}
048 * @since 1.0.1
049 */
050public final class PrecisionContextRoundedOperator implements MonetaryOperator {
051
052        private final MathContext mathContext;
053
054        private PrecisionContextRoundedOperator(MathContext mathContext) {
055                this.mathContext = mathContext;
056        }
057
058        /**
059         * Creates the rounded Operator from mathContext
060         * @param mathContext
061         * @return the {@link MonetaryOperator} using the {@link MathContext} used in parameter
062         * @throws NullPointerException when the {@link MathContext} is null
063         * @throws IllegalArgumentException when the {@link MathContext#getPrecision()} is lesser than zero
064         * @throws IllegalArgumentException when the mathContext is {@link MathContext#getRoundingMode()} is {@link RoundingMode#UNNECESSARY}
065         * @see {@linkplain MathContext}
066         */
067        public static PrecisionContextRoundedOperator of(MathContext mathContext) {
068
069                Objects.requireNonNull(mathContext);
070
071                if(RoundingMode.UNNECESSARY.equals(mathContext.getRoundingMode())) {
072                           throw new IllegalArgumentException("To create the MathContextRoundedOperator you cannot use the RoundingMode.UNNECESSARY on MathContext");
073                }
074
075                if(mathContext.getPrecision() <= 0) {
076                                throw new IllegalArgumentException("To create the MathContextRoundedOperator you cannot use the zero precision on MathContext");
077                }
078
079                return new PrecisionContextRoundedOperator(mathContext);
080        }
081
082        @Override
083        public MonetaryAmount apply(MonetaryAmount amount) {
084                RoundedMoney roundedMoney = RoundedMoney.from(Objects.requireNonNull(amount));
085                BigDecimal numberValue = roundedMoney.getNumber().numberValue(BigDecimal.class);
086                BigDecimal numberRounded = numberValue.round(mathContext);
087                return RoundedMoney.of(numberRounded, roundedMoney.getCurrency(), this);
088        }
089
090        public MathContext getMathContext() {
091                return mathContext;
092        }
093
094        @Override
095        public String toString() {
096                StringBuilder sb = new StringBuilder();
097                sb.append(PrecisionContextRoundedOperator.class.getName()).append('{')
098                .append("mathContext:").append(mathContext).append('}');
099                return sb.toString();
100        }
101
102}