001package org.javamoney.moneta.function;
002
003import java.util.Comparator;
004import java.util.List;
005import java.util.Map;
006import java.util.Objects;
007import java.util.function.BinaryOperator;
008import java.util.function.Predicate;
009import java.util.function.Supplier;
010import java.util.stream.Collector;
011import java.util.stream.Collectors;
012
013import javax.money.CurrencyUnit;
014import javax.money.MonetaryAmount;
015import javax.money.MonetaryException;
016
017import org.javamoney.moneta.spi.MoneyUtils;
018
019/**
020 * This singleton class provides access to the predefined monetary functions.
021 *
022 * @author otaviojava
023 * @author anatole
024 */
025public final class MonetaryFunctions {
026
027
028    /**
029     * Collector to group by CurrencyUnit
030     *
031     * @return the Collector to of Map<CurrencyUnit, List<MonetaryAmount>>
032     */
033    public static Collector<MonetaryAmount,?,Map<CurrencyUnit,List<MonetaryAmount>>> groupByCurrencyUnit(){
034        return Collectors.groupingBy(MonetaryAmount::getCurrency);
035    }
036
037    /**
038     * of the summary of the MonetaryAmount
039     *
040     * @param currencyUnit the target {@link javax.money.CurrencyUnit}
041     * @return the MonetarySummaryStatistics
042     */
043    public static Collector<MonetaryAmount,MonetarySummaryStatistics,MonetarySummaryStatistics> summarizingMonetary(
044            CurrencyUnit currencyUnit){
045        Supplier<MonetarySummaryStatistics> supplier = () -> new MonetarySummaryStatistics(currencyUnit);
046        return Collector.of(supplier, MonetarySummaryStatistics::accept, MonetarySummaryStatistics::combine);
047    }
048
049    /**
050     * of MonetaryAmount group by MonetarySummary
051     *
052     * @return the MonetarySummaryStatistics
053     */
054    public static Collector<MonetaryAmount,GroupMonetarySummaryStatistics,GroupMonetarySummaryStatistics>
055    groupBySummarizingMonetary(){
056        return Collector.of(GroupMonetarySummaryStatistics::new, GroupMonetarySummaryStatistics::accept,
057                            GroupMonetarySummaryStatistics::combine);
058    }
059
060    /**
061     * Get a comparator for sorting CurrencyUnits ascending.
062     *
063     * @return the Comparator to sort by CurrencyUnit in ascending order, not null.
064     */
065    public static Comparator<MonetaryAmount> sortCurrencyUnit(){
066        return Comparator.comparing(MonetaryAmount::getCurrency);
067    }
068
069    /**
070     * Get a comparator for sorting CurrencyUnits descending.
071     *
072     * @return the Comparator to sort by CurrencyUnit in descending order, not null.
073     */
074    public static Comparator<MonetaryAmount> sortCurrencyUnitDesc(){
075        return sortCurrencyUnit().reversed();
076    }
077
078    /**
079     * Get a comparator for sorting amount by number value ascending.
080     *
081     * @return the Comparator to sort by number in ascending way, not null.
082     */
083    public static Comparator<MonetaryAmount> sortNumber(){
084        return Comparator.comparing(MonetaryAmount::getNumber);
085    }
086
087    /**
088     * Get a comparator for sorting amount by number value descending.
089     *
090     * @return the Comparator to sort by number in descending way, not null.
091     */
092    public static Comparator<MonetaryAmount> sortNumberDesc(){
093        return sortNumber().reversed();
094    }
095
096    /**
097         * Create predicate that filters by CurrencyUnit.
098         * @param currencies
099         *            the target {@link javax.money.CurrencyUnit}
100         * @return the predicate from CurrencyUnit
101         */
102        public static Predicate<MonetaryAmount> isCurrency(
103                        CurrencyUnit... currencies) {
104
105                if (Objects.isNull(currencies) || currencies.length == 0) {
106                        return m -> true;
107                }
108                Predicate<MonetaryAmount> predicate = null;
109
110                for (CurrencyUnit currencyUnit : currencies) {
111                        if (Objects.isNull(predicate)) {
112                                predicate = m -> m.getCurrency().equals(currencyUnit);
113                        } else {
114                                predicate = predicate.or(m -> m.getCurrency().equals(
115                                                currencyUnit));
116                        }
117                }
118                return predicate;
119    }
120
121    /**
122     * Create predicate that filters by CurrencyUnit.
123     *
124     * @param currencies the target {@link javax.money.CurrencyUnit} instances to be filtered.
125     * @return the predicate from CurrencyUnit
126     */
127        public static Predicate<MonetaryAmount> fiterByExcludingCurrency(
128                        CurrencyUnit... currencies) {
129
130                if (Objects.isNull(currencies) || currencies.length == 0) {
131                        return m -> true;
132                }
133                return isCurrency(currencies).negate();
134    }
135
136    /**
137     * Creates filter using isGreaterThan in MonetaryAmount.
138     *
139     * @param amount the amount to be compared, not null.
140     * @return the filter with isGreaterThan conditions
141     */
142    public static Predicate<MonetaryAmount> isGreaterThan(MonetaryAmount amount){
143        return m -> m.isGreaterThan(amount);
144    }
145
146    /**
147     * Creates filter using isGreaterThanOrEqualTo in MonetaryAmount
148     *
149     * @param amount the amount to be compared, not null.
150     * @return the filter with isGreaterThanOrEqualTo conditions
151     */
152    public static Predicate<MonetaryAmount> isGreaterThanOrEqualTo(MonetaryAmount amount){
153        return m -> m.isGreaterThanOrEqualTo(amount);
154    }
155
156    /**
157     * Creates filter using isLessThan in MonetaryAmount
158     *
159     * @param amount the amount to be compared, not null.
160     * @return the filter with isLessThan conditions
161     */
162    public static Predicate<MonetaryAmount> isLessThan(MonetaryAmount amount){
163        return m -> m.isLessThan(amount);
164    }
165
166    /**
167     * Creates filter using isLessThanOrEqualTo in MonetaryAmount
168     *
169     * @param amount the amount to be compared, not null.
170     * @return the filter with isLessThanOrEqualTo conditions
171     */
172    public static Predicate<MonetaryAmount> isLessThanOrEqualTo(MonetaryAmount amount){
173        return m -> m.isLessThanOrEqualTo(amount);
174    }
175
176    /**
177     * Creates a filter using the isBetween predicate.
178     *
179     * @param min min value inclusive, not null.
180     * @param max max value inclusive, not null.
181     * @return the Predicate between min and max.
182     */
183    public static Predicate<MonetaryAmount> isBetween(MonetaryAmount min, MonetaryAmount max){
184        return isLessThanOrEqualTo(max).and(isGreaterThanOrEqualTo(min));
185    }
186
187    /**
188     * Adds two monetary together
189     *
190     * @param a the first operand
191     * @param b the second operand
192     * @return the sum of {@code a} and {@code b}
193     * @throws NullPointerException if a o b be null
194     * @throws MonetaryException    if a and b have different currency
195     */
196    public static MonetaryAmount sum(MonetaryAmount a, MonetaryAmount b){
197        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
198        return a.add(b);
199    }
200
201    /**
202     * Returns the smaller of two {@code MonetaryAmount} values. If the arguments
203     * have the same value, the result is that same value.
204     *
205     * @param a an argument.
206     * @param b another argument.
207     * @return the smaller of {@code a} and {@code b}.
208     */
209    public static MonetaryAmount min(MonetaryAmount a, MonetaryAmount b){
210        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
211        return a.isLessThan(b) ? a : b;
212    }
213
214    /**
215     * Returns the greater of two {@code MonetaryAmount} values. If the
216     * arguments have the same value, the result is that same value.
217     *
218     * @param a an argument.
219     * @param b another argument.
220     * @return the larger of {@code a} and {@code b}.
221     */
222    public static MonetaryAmount max(MonetaryAmount a, MonetaryAmount b){
223        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
224        return a.isGreaterThan(b) ? a : b;
225    }
226
227    /**
228     * Creates a BinaryOperator to sum.
229     *
230     * @return the sum BinaryOperator, not null.
231     */
232    public static BinaryOperator<MonetaryAmount> sum(){
233        return MonetaryFunctions::sum;
234    }
235
236    /**
237         * Creates a BinaryOperator to calculate the minimum amount
238         *
239         * @return the min BinaryOperator, not null.
240         */
241    public static BinaryOperator<MonetaryAmount> min(){
242        return MonetaryFunctions::min;
243    }
244
245    /**
246         * Creates a BinaryOperator to calculate the maximum amount.
247         *
248         * @return the max BinaryOperator, not null.
249         */
250    public static BinaryOperator<MonetaryAmount> max(){
251        return MonetaryFunctions::max;
252    }
253}