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