001/**
002 * Copyright (c) 2012, 2015, 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.util.Comparator;
019import java.util.List;
020import java.util.Map;
021import java.util.Objects;
022import java.util.function.BinaryOperator;
023import java.util.function.Predicate;
024import java.util.function.Supplier;
025import java.util.stream.Collector;
026import java.util.stream.Collectors;
027
028import javax.money.CurrencyUnit;
029import javax.money.MonetaryAmount;
030import javax.money.MonetaryException;
031import javax.money.convert.CurrencyConversion;
032import javax.money.convert.ExchangeRate;
033import javax.money.convert.ExchangeRateProvider;
034
035import org.javamoney.moneta.spi.MoneyUtils;
036
037/**
038 * This singleton class provides access to the predefined monetary functions.
039 *
040 * @author otaviojava
041 * @author anatole
042 * @author keilw
043 */
044public final class MonetaryFunctions {
045
046    /**
047     * Private constructor for static accessor class.
048     */
049    private MonetaryFunctions(){}
050    /**
051     * Collector to group by CurrencyUnit
052     * @return the Collector to of Map<CurrencyUnit, List<MonetaryAmount>>
053     */
054    public static Collector<MonetaryAmount,?,Map<CurrencyUnit,List<MonetaryAmount>>> groupByCurrencyUnit(){
055        return Collectors.groupingBy(MonetaryAmount::getCurrency);
056    }
057
058    /**
059     * Creates a the summary of MonetaryAmounts.
060     * @param currencyUnit the target {@link javax.money.CurrencyUnit}
061     * @return the MonetarySummaryStatistics
062     */
063        public static Collector<MonetaryAmount, MonetarySummaryStatistics, MonetarySummaryStatistics> summarizingMonetary(
064            CurrencyUnit currencyUnit){
065                Supplier<MonetarySummaryStatistics> supplier = () -> new DefaultMonetarySummaryStatistics(currencyUnit);
066                return Collector.of(supplier, MonetarySummaryStatistics::accept, MonetarySummaryStatistics::combine);
067    }
068
069        /**
070         * reates a the summary of MonetaryAmounts.
071         * @param currencyUnit the target {@link javax.money.CurrencyUnit}
072         * @param provider the rate provider
073     * @return the MonetarySummaryStatistics
074         * @deprecated Use #summarizingMonetary(CurrencyUnit) instead of.
075     */
076        @Deprecated
077        public static Collector<MonetaryAmount, MonetarySummaryStatistics, MonetarySummaryStatistics> summarizingMonetary(
078                        CurrencyUnit currencyUnit,javax.money.convert.ExchangeRateProvider provider){
079                // TODO implement method here
080                return summarizingMonetary(currencyUnit);
081        }
082
083    /**
084     * of MonetaryAmount group by MonetarySummary
085     * @return the MonetarySummaryStatistics
086     */
087    public static Collector<MonetaryAmount,GroupMonetarySummaryStatistics,GroupMonetarySummaryStatistics>
088    groupBySummarizingMonetary(){
089        return Collector.of(GroupMonetarySummaryStatistics::new, GroupMonetarySummaryStatistics::accept,
090                            GroupMonetarySummaryStatistics::combine);
091    }
092
093    /**
094     * Get a comparator for sorting CurrencyUnits ascending.
095     *
096     * @return the Comparator to sort by CurrencyUnit in ascending order, not null.
097     */
098    public static Comparator<MonetaryAmount> sortCurrencyUnit(){
099        return Comparator.comparing(MonetaryAmount::getCurrency);
100    }
101
102        /**
103         * comparator to sort the {@link MonetaryAmount} considering the
104         * {@link ExchangeRate}
105         * @param provider the rate provider to be used, not null.
106         * @return the sort of {@link MonetaryAmount} using {@link ExchangeRate}
107         */
108        public static Comparator<? super MonetaryAmount> sortValuable(
109                        ExchangeRateProvider provider) {
110
111                return (m1, m2) -> {
112                        CurrencyConversion conversion = provider.getCurrencyConversion(m1
113                                        .getCurrency());
114                        return m1.compareTo(conversion.apply(m2));
115                };
116        }
117
118        /**
119         * comparator to sort the {@link MonetaryAmount} considering the
120         * {@link ExchangeRate}
121         * @param provider the rate provider to be used.
122         * @return the sort of {@link MonetaryAmount} using {@link ExchangeRate}
123         * @deprecated call #sortValuable instead of.
124         */
125        @Deprecated
126        public static Comparator<? super MonetaryAmount> sortValiable(final ExchangeRateProvider provider) {
127                return sortValuable(provider);
128        }
129
130        /**
131         * Descending order of
132         * {@link MonetaryFunctions#sortValuable(ExchangeRateProvider)}
133         * @param provider the rate provider to be used.
134         * @return the Descending order of
135         *         {@link MonetaryFunctions#sortValuable(ExchangeRateProvider)}
136         * @deprecated Use #sortValiableDesc instead of.
137         */
138        @Deprecated
139        public static Comparator<? super MonetaryAmount> sortValiableDesc(
140                        final ExchangeRateProvider provider) {
141                return sortValuableDesc(provider);
142        }
143
144        /**
145         * Descending order of
146         * {@link MonetaryFunctions#sortValuable(ExchangeRateProvider)}
147         * @param provider the rate provider to be used, not null.
148         * @return the Descending order of
149         *         {@link MonetaryFunctions#sortValuable(ExchangeRateProvider)}
150         */
151        public static Comparator<? super MonetaryAmount> sortValuableDesc(
152                        ExchangeRateProvider provider) {
153                return sortValuable(provider).reversed();
154        }
155
156    /**
157     * Get a comparator for sorting CurrencyUnits descending.
158     * @return the Comparator to sort by CurrencyUnit in descending order, not null.
159     */
160    public static Comparator<MonetaryAmount> sortCurrencyUnitDesc(){
161        return sortCurrencyUnit().reversed();
162    }
163
164    /**
165     * Get a comparator for sorting amount by number value ascending.
166     * @return the Comparator to sort by number in ascending way, not null.
167     */
168    public static Comparator<MonetaryAmount> sortNumber(){
169        return Comparator.comparing(MonetaryAmount::getNumber);
170    }
171
172    /**
173     * Get a comparator for sorting amount by number value descending.
174     * @return the Comparator to sort by number in descending way, not null.
175     */
176    public static Comparator<MonetaryAmount> sortNumberDesc(){
177        return sortNumber().reversed();
178    }
179
180    /**
181         * Create predicate that filters by CurrencyUnit.
182         * @param currencies
183         *            the target {@link javax.money.CurrencyUnit}
184         * @return the predicate from CurrencyUnit
185         */
186        public static Predicate<MonetaryAmount> isCurrency(
187                        CurrencyUnit... currencies) {
188
189                if (Objects.isNull(currencies) || currencies.length == 0) {
190                        return m -> true;
191                }
192                Predicate<MonetaryAmount> predicate = null;
193
194                for (CurrencyUnit currencyUnit : currencies) {
195                        if (Objects.isNull(predicate)) {
196                                predicate = m -> m.getCurrency().equals(currencyUnit);
197                        } else {
198                                predicate = predicate.or(m -> m.getCurrency().equals(
199                                                currencyUnit));
200                        }
201                }
202                return predicate;
203    }
204
205    /**
206     * Create predicate that filters by CurrencyUnit.
207     * @param currencies the target {@link javax.money.CurrencyUnit} instances
208     * @return the predicate from CurrencyUnit
209     */
210        public static Predicate<MonetaryAmount> filterByExcludingCurrency(
211            CurrencyUnit... currencies) {
212
213                if (Objects.isNull(currencies) || currencies.length == 0) {
214                        return m -> true;
215                }
216                return isCurrency(currencies).negate();
217    }
218
219    /**
220     * Creates filter using isGreaterThan in MonetaryAmount.
221     * @param amount the amount to be compared to.
222     * @return the filter with isGreaterThan conditions
223     */
224    public static Predicate<MonetaryAmount> isGreaterThan(MonetaryAmount amount){
225        return m -> m.isGreaterThan(amount);
226    }
227
228    /**
229     * Creates filter using isGreaterThanOrEqualTo in MonetaryAmount
230     * @param amount the amount to be compared to.
231     * @return the filter with isGreaterThanOrEqualTo conditions
232     */
233    public static Predicate<MonetaryAmount> isGreaterThanOrEqualTo(MonetaryAmount amount){
234        return m -> m.isGreaterThanOrEqualTo(amount);
235    }
236
237    /**
238     * Creates filter using isLessThan in MonetaryAmount
239     * @param amount the amount to be compared to.
240     * @return the filter with isLessThan conditions
241     */
242    public static Predicate<MonetaryAmount> isLessThan(MonetaryAmount amount){
243        return m -> m.isLessThan(amount);
244    }
245
246    /**
247     * Creates filter using isLessThanOrEqualTo in MonetaryAmount
248     * @param amount the amount to be compared to.
249     * @return the filter with isLessThanOrEqualTo conditions
250     */
251    public static Predicate<MonetaryAmount> isLessThanOrEqualTo(MonetaryAmount amount){
252        return m -> m.isLessThanOrEqualTo(amount);
253    }
254
255    /**
256     * Creates a filter using the isBetween predicate.
257     * @param min min value inclusive, not null.
258     * @param max max value inclusive, not null.
259     * @return the Predicate between min and max.
260     */
261    public static Predicate<MonetaryAmount> isBetween(MonetaryAmount min, MonetaryAmount max){
262        return isLessThanOrEqualTo(max).and(isGreaterThanOrEqualTo(min));
263    }
264
265    /**
266     * Adds two monetary together
267     * @param a the first operand
268     * @param b the second operand
269     * @return the sum of {@code a} and {@code b}
270     * @throws NullPointerException if a o b be null
271     * @throws MonetaryException    if a and b have different currency
272     */
273    public static MonetaryAmount sum(MonetaryAmount a, MonetaryAmount b){
274        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
275        return a.add(b);
276    }
277
278    /**
279     * Returns the smaller of two {@code MonetaryAmount} values. If the arguments
280     * have the same value, the result is that same value.
281     * @param a an argument.
282     * @param b another argument.
283     * @return the smaller of {@code a} and {@code b}.
284     */
285        static MonetaryAmount min(MonetaryAmount a, MonetaryAmount b) {
286        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
287        return a.isLessThan(b) ? a : b;
288    }
289
290    /**
291     * Returns the greater of two {@code MonetaryAmount} values. If the
292     * arguments have the same value, the result is that same value.
293     * @param a an argument.
294     * @param b another argument.
295     * @return the larger of {@code a} and {@code b}.
296     */
297        static MonetaryAmount max(MonetaryAmount a, MonetaryAmount b) {
298        MoneyUtils.checkAmountParameter(Objects.requireNonNull(a), Objects.requireNonNull(b.getCurrency()));
299        return a.isGreaterThan(b) ? a : b;
300    }
301
302    /**
303     * Creates a BinaryOperator to sum.
304     * @return the sum BinaryOperator, not null.
305     */
306    public static BinaryOperator<MonetaryAmount> sum(){
307        return MonetaryFunctions::sum;
308    }
309
310        /**
311         * return the sum and convert all values to specific currency using the
312         * provider, if necessary
313         * @param provider the rate provider to be used, not null.
314         * @param currency
315         *            currency
316         * @return the list convert to specific currency unit
317         */
318        public static BinaryOperator<MonetaryAmount> sum(
319                        ExchangeRateProvider provider, CurrencyUnit currency) {
320                CurrencyConversion currencyConversion = provider
321                                .getCurrencyConversion(currency);
322
323                return (m1, m2) -> currencyConversion.apply(m1).add(
324                                currencyConversion.apply(m2));
325        }
326
327    /**
328         * Creates a BinaryOperator to calculate the minimum amount
329         * @return the minimum BinaryOperator, not null.
330         */
331    public static BinaryOperator<MonetaryAmount> min(){
332        return MonetaryFunctions::min;
333    }
334
335        /**
336         * return the minimum value, if the monetary amounts have different
337         * currencies, will converter first using the given ExchangeRateProvider
338         * @param provider
339         *            the ExchangeRateProvider to convert the currencies
340         * @return the minimum value
341         */
342        public static BinaryOperator<MonetaryAmount> min(
343                        ExchangeRateProvider provider) {
344
345                return (m1, m2) -> {
346                        CurrencyConversion conversion = provider.getCurrencyConversion(m1
347                                        .getCurrency());
348
349                        if (m1.isGreaterThan(conversion.apply(m2))) {
350                                return m2;
351                        }
352                        return m1;
353                };
354        }
355
356    /**
357         * Creates a BinaryOperator to calculate the maximum amount.
358         * @return the max BinaryOperator, not null.
359         */
360    public static BinaryOperator<MonetaryAmount> max(){
361        return MonetaryFunctions::max;
362    }
363
364        /**
365         * return the maximum value, if the monetary amounts have different
366         * currencies, will converter first using the given ExchangeRateProvider
367         * @param provider
368         *            the ExchangeRateProvider to convert the currencies
369         * @return the maximum value
370         */
371        public static BinaryOperator<MonetaryAmount> max(
372                        ExchangeRateProvider provider) {
373
374                return (m1, m2) -> {
375                        CurrencyConversion conversion = provider
376                                        .getCurrencyConversion(m1.getCurrency());
377
378                        if (m1.isGreaterThan(conversion.apply(m2))) {
379                                return m1;
380                        }
381                        return m2;
382                };
383        }
384
385
386}