001/*
002  Copyright (c) 2012, 2014, 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 * 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
030 * The derived class will implements the {@link RoundedMoney} with this rounding monetary operator
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* Case the parameter in {@link MonetaryOperator#apply(MonetaryAmount)} be null, the apply will return a {@link NullPointerException}
043 * @author Otavio Santana
044 * @see PrecisionContextRoundedOperator#of(MathContext)
045 * @see RoundedMoney
046 * @see MonetaryOperator
047 * @see BigDecimal#precision()
048 */
049public final class PrecisionContextRoundedOperator implements MonetaryOperator {
050
051        private final MathContext mathContext;
052
053        private PrecisionContextRoundedOperator(MathContext mathContext) {
054                this.mathContext = mathContext;
055        }
056
057        /**
058         * Creates the rounded Operator from mathContext
059         * @param mathContext the math context, not null.
060         * @return the {@link MonetaryOperator} using the {@link MathContext} used in parameter
061         * @throws NullPointerException when the {@link MathContext} is null
062         * @throws IllegalArgumentException when the {@link MathContext#getPrecision()} is lesser than zero
063         * @throws IllegalArgumentException when the mathContext is {@link MathContext#getRoundingMode()} is {@link RoundingMode#UNNECESSARY}
064         * @see MathContext
065         */
066        public static PrecisionContextRoundedOperator of(MathContext mathContext) {
067
068                Objects.requireNonNull(mathContext);
069
070                if(RoundingMode.UNNECESSARY.equals(mathContext.getRoundingMode())) {
071                           throw new IllegalArgumentException("To create the MathContextRoundedOperator you cannot use the RoundingMode.UNNECESSARY on MathContext");
072                }
073
074                if(mathContext.getPrecision() <= 0) {
075                                throw new IllegalArgumentException("To create the MathContextRoundedOperator you cannot use the zero precision on MathContext");
076                }
077
078                return new PrecisionContextRoundedOperator(mathContext);
079        }
080
081        @Override
082        public MonetaryAmount apply(MonetaryAmount amount) {
083                RoundedMoney roundedMoney = RoundedMoney.from(Objects.requireNonNull(amount));
084                BigDecimal numberValue = roundedMoney.getNumber().numberValue(BigDecimal.class);
085                BigDecimal numberRounded = numberValue.round(mathContext);
086                return RoundedMoney.of(numberRounded, roundedMoney.getCurrency(), this);
087        }
088
089        public MathContext getMathContext() {
090                return mathContext;
091        }
092
093        @Override
094        public String toString() {
095                return PrecisionContextRoundedOperator.class.getName() + '{' +
096                "mathContext:" + mathContext + '}';
097        }
098
099}