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