Skip to content

Instantly share code, notes, and snippets.

@nurrony
Last active January 4, 2023 08:42
Show Gist options
  • Save nurrony/2bf17cb961ce3592dfccded6c4cf57b3 to your computer and use it in GitHub Desktop.
Save nurrony/2bf17cb961ce3592dfccded6c4cf57b3 to your computer and use it in GitHub Desktop.
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.function.BinaryOperator;
/**
* A simple utility class to calculate Money related calculation. It provides
* three private properties along with setters and these following default
* values
* <ul>
* <li><code>toPrecision: 2</code></li>
* <li><code>locale: java.utils.Locale.US</code></li>
* <li><code>roundingMode: java.math.RoundingMode.HALF_EVEN</code></li>
* </ul>
*
* @author Nur Rony
* @since v1.0.0
*/
final public class MoneyUtil {
private static int toPrecision = 2;
private static Locale locale = Locale.US;
private static RoundingMode roundingMode = RoundingMode.HALF_EVEN;
public static final BigDecimal ONE_HUNDRED = new BigDecimal(100);
private MoneyUtil() {
throw new IllegalStateException("MoneyUtil Utility Class");
}
/**
* Converts array of {@link String} to array of {@link BigDecimal}
*
* @param numbers array of {@link String}
* @return {@link BigDecimal[]}
* @throws NumberFormatException
*/
private static BigDecimal[] toBigDecimalArray(String... numbers) throws NumberFormatException {
return Arrays.stream(numbers).sequential().map(BigDecimal::new).toArray(BigDecimal[]::new);
}
/**
* Set precision for calculation
*
* @param toPrecision precision to set for decimal part
*/
public static void setPrecision(int toPrecision) {
MoneyUtil.toPrecision = toPrecision;
}
/**
* Set rounding for calculation; defaults: {@link RoundingMode.HALF_EVEN}
*
* @param roundingMode precision to set for decimal part
*/
public static void setRoundingMode(RoundingMode roundingMode) {
MoneyUtil.roundingMode = roundingMode;
}
/**
* Format a {@link BigDecimal} number for given locale
*
* @param number number to format
* @param locale locale of the formatted number
* @return {@link String}
*/
public static String formatToLocale(BigDecimal number, Locale locale) {
return NumberFormat.getNumberInstance(locale == null ? MoneyUtil.locale : locale).format(number);
}
/**
* Format a {@link BigDecimal} number for given locale with currency symbol
*
* @param number number to format
* @param locale locale of the formatted number
* @return {@link String}
*/
public static String formatToCurrency(BigDecimal number, Locale locale) {
return NumberFormat.getCurrencyInstance(locale == null ? MoneyUtil.locale : locale).format(number);
}
/**
* Add numbers given as {@link String}
*
* @param numbers array of {@link String}
* @return {@link BigDecimal}
*
* @see {@link #add(BigDecimal...)}
* @see {@link #toBigDecimalArray(String...)}
*/
public static BigDecimal add(String... numbers) throws NumberFormatException {
return add(toBigDecimalArray(numbers));
}
/**
* Add numbers given as {@link BigDecimal}
*
* @param numbers array of {@link BigDecimal}
* @return {@link BigDecimal}
*/
public static BigDecimal add(BigDecimal... numbers) {
return Arrays.stream(numbers).sequential().reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(toPrecision, roundingMode);
}
/**
* Subtracts numbers given as {@link String}
*
* @param numbers array of {@link String}
* @return {@link BigDecimal}
* @see {@link #subtract(BigDecimal...)}
*/
public static BigDecimal subtract(String... numbers) throws NumberFormatException {
return subtract(toBigDecimalArray(numbers));
}
/**
* Subtracts numbers given as {@link BigDecimal}
*
* @param numbers arrays of {@link BigDecimal}
* @return {@link BigDecimal}
*/
public static BigDecimal subtract(BigDecimal... numbers) {
BigDecimal[] shiftedArray = Arrays.copyOfRange(numbers, 1, numbers.length);
return Arrays.stream(shiftedArray).sequential().reduce(numbers[0], BigDecimal::subtract)
.setScale(toPrecision, roundingMode);
}
/**
* Multiplies numbers given as {@link String}
*
* @param numbers arrays of {@link String}
* @return {@link BigDecimal}
* @see {@link #multiply(BigDecimal...)}
* @see {@link #toBigDecimalArray(String...)}
*/
public static BigDecimal multiply(String... numbers) throws NumberFormatException {
return multiply(toBigDecimalArray(numbers));
}
/**
* Multiplies numbers given as {@link BigDecimal}
*
* @param numbers arrays of {@link BigDecimal}
* @return {@link BigDecimal}
*/
public static BigDecimal multiply(BigDecimal... numbers) {
return Arrays.stream(numbers).sequential().reduce(BigDecimal.ONE, BigDecimal::multiply)
.setScale(toPrecision, roundingMode);
}
/**
* Divides numbers given as {@link String}
*
* @param numbers arrays of {@link String}
* @return {@link BigDecimal}
*
* @see {@link #divide(BigDecimal...)}
* @see {@link #toBigDecimalArray(String...)}
*/
public static BigDecimal divide(String... numbers) throws NumberFormatException {
return divide(toBigDecimalArray(numbers));
}
/**
* Divides numbers given as {@link BigDecimal}
*
* @param numbers arrays of {@link BigDecimal}
* @return {@link BigDecimal}
*/
public static BigDecimal divide(BigDecimal... numbers) {
BigDecimal[] shiftedArray = Arrays.copyOfRange(numbers, 1, numbers.length);
BinaryOperator<BigDecimal> accumulator = (result, number) -> result.divide(number, 2, roundingMode);
return Arrays.stream(shiftedArray).sequential().reduce(numbers[0], accumulator).setScale(toPrecision, roundingMode);
}
/**
* Calculates percentage for given base number
*
* @param base number to calculate percentage on
* @param percentage amount of percentage
* @return {@link BigDecimal}
*
* @see {@link #percentage(BigDecimal, BigDecimal)}
*/
public static BigDecimal percentage(String base, String percentage) {
return percentage(new BigDecimal(base), new BigDecimal(percentage));
}
/**
* Calculates percentage for given base number
*
* @param base number to calculate percentage on
* @param percentage amount of percentage
* @return {@link BigDecimal}
*/
public static BigDecimal percentage(BigDecimal base, BigDecimal percentage) {
return (base.multiply(percentage).divide(ONE_HUNDRED)).setScale(toPrecision, roundingMode);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment