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 static java.util.Objects.requireNonNull;
019
020import java.math.BigDecimal;
021import java.math.MathContext;
022import java.math.RoundingMode;
023import java.util.Objects;
024
025import javax.money.MonetaryAmount;
026import javax.money.MonetaryOperator;
027
028/**
029 * This implementation uses a scale and {@link RoundingMode} and precision to does the rounding operations. The implementation will use both the <b>scale</b> and <b>precision</b>, in other words, the number of digits to the right of the decimal point and the number of digits.
030 * The derived class will implements the {@link org.javamoney.moneta.RoundedMoney} with this rounding monetary operator
031 *  <pre>
032 *   {@code
033 *     int scale = 3;
034 *     int precision = 5;
035 *     MathContext mathContext = new MathContext(precision, RoundingMode.HALF_EVEN);
036 *     MonetaryOperator monetaryOperator = PrecisionScaleRoundedOperator.of(scale, mathContext);
037 *     CurrencyUnit real = Monetary.getCurrency("BRL");
038 *     MonetaryAmount money = Money.of(BigDecimal.valueOf(35.34567), real);
039 *     MonetaryAmount result = monetaryOperator.apply(money); // BRL 35.346
040 *
041 *    }
042* </pre>
043 * Case the parameter in {@link MonetaryOperator#apply(MonetaryAmount)} be null, the apply will return a {@link NullPointerException}
044 * @author Otavio Santana
045 * @see org.javamoney.moneta.RoundedMoney
046 * @see MonetaryOperator
047 * @see BigDecimal#scale()
048 * @see MathContext
049 * @see BigDecimal#precision()
050 */
051public final class PrecisionScaleRoundedOperator implements MonetaryOperator {
052
053        private final PrecisionContextRoundedOperator mathContextOperator;
054
055        private final ScaleRoundedOperator scaleRoundedOperator;
056
057        private final int scale;
058
059        private final MathContext mathContext;
060
061        private PrecisionScaleRoundedOperator(int scale, MathContext mathContext) {
062                this.scale = scale;
063                this.mathContext = mathContext;
064                this.mathContextOperator = PrecisionContextRoundedOperator.of(mathContext);
065                this.scaleRoundedOperator = ScaleRoundedOperator.of(scale, mathContext.getRoundingMode());
066        }
067
068        /**
069         * Creates the rounded Operator from scale and roundingMode
070         * @param mathContext the math context, not null.
071         * @return the {@link MonetaryOperator} using the scale and {@link RoundingMode} used in parameter
072         * @throws NullPointerException when the {@link MathContext} is null
073         * @throws IllegalArgumentException if {@link MathContext#getPrecision()} is lesser than zero
074         * @throws IllegalArgumentException if {@link MathContext#getRoundingMode()} is {@link RoundingMode#UNNECESSARY}
075         * @see RoundingMode
076         */
077        public static PrecisionScaleRoundedOperator of(int scale, MathContext mathContext) {
078
079                Objects.requireNonNull(mathContext);
080
081                if(RoundingMode.UNNECESSARY.equals(mathContext.getRoundingMode())) {
082                   throw new IllegalArgumentException("To create the ScaleRoundedOperator you cannot use the RoundingMode.UNNECESSARY on MathContext");
083                }
084
085                if(mathContext.getPrecision() <= 0) {
086                        throw new IllegalArgumentException("To create the ScaleRoundedOperator you cannot use the zero precision on MathContext");
087                }
088                return new PrecisionScaleRoundedOperator(scale, mathContext);
089        }
090
091        @Override
092        public MonetaryAmount apply(MonetaryAmount amount) {
093                return scaleRoundedOperator.apply(requireNonNull(amount)).with(mathContextOperator);
094        }
095
096        public int getScale() {
097                return scale;
098        }
099
100        public MathContext getMathContext() {
101                return mathContext;
102        }
103
104        @Override
105        public String toString() {
106                return PrecisionScaleRoundedOperator.class.getName() + '{' +
107                "scale:" + Integer.toString(scale) + ',' +
108                "mathContext:" + mathContext + '}';
109        }
110
111}