Last active
June 16, 2016 14:01
-
-
Save ghusta/389559b51986521db0c3 to your computer and use it in GitHub Desktop.
Classe utilitaire pour la classe BigDecimal.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fr.gouv.finances.douane.intercom.outils.lang; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.math.MathContext; | |
import java.math.RoundingMode; | |
import java.util.ArrayList; | |
import java.util.List; | |
import org.apache.commons.lang.StringUtils; | |
import org.apache.commons.lang.math.NumberUtils; | |
import org.apache.log4j.Logger; | |
/** | |
* Classe utilitaire pour la classe {@link BigDecimal}. | |
* <p> | |
* Permet par exemple d'eviter l'emploi du constructeur {@link BigDecimal#BigDecimal(double)} pouvant entrainer des erreurs. | |
* <br/> | |
* Cf. regle PMD <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* </p> | |
*/ | |
public final class BigDecimalUtils | |
{ | |
private static final Logger LOGGER = Logger.getLogger(BigDecimalUtils.class); | |
/** | |
* BigDecimal : ZERO. | |
* | |
* @deprecated Utiliser {@link BigDecimal#ZERO} | |
*/ | |
@Deprecated | |
public static final BigDecimal BIG_DEC_ZERO = BigDecimal.ZERO; | |
/** | |
* BigDecimal : UN. | |
* | |
* @deprecated Utiliser {@link BigDecimal#ONE} | |
*/ | |
@Deprecated | |
public static final BigDecimal BIG_DEC_UN = BigDecimal.ONE; | |
/** | |
* BigDecimal : DIX. | |
* | |
* @deprecated Utiliser {@link BigDecimal#TEN} | |
*/ | |
@Deprecated | |
public static final BigDecimal BIG_DEC_DIX = BigDecimal.TEN; | |
/** | |
* BigDecimal : CENT. | |
*/ | |
public static final BigDecimal BIG_DEC_CENT = BigDecimal.valueOf(100); | |
/** | |
* BigDecimal : MILLE. | |
*/ | |
public static final BigDecimal BIG_DEC_MILLE = BigDecimal.valueOf(1000); | |
public static final MathContext MATH_CONTEXT_ARRONDI_MONETAIRE_EURO = new MathContext(2, RoundingMode.HALF_UP); | |
/** | |
* Constructeur prive. | |
*/ | |
private BigDecimalUtils() | |
{ | |
} | |
/** | |
* Creer un BigDecimal a partir d'un int sans passer par le constructeur {@link BigDecimal#BigDecimal(double)}. | |
* <br> | |
* Regle PMD explicative : <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* | |
* @param nb | |
* @return | |
* @since 1.4 | |
*/ | |
public static BigDecimal createBigDecimal(int nb) | |
{ | |
return BigDecimal.valueOf(nb); | |
} | |
/** | |
* Creer un BigDecimal a partir d'un long sans passer par le constructeur {@link BigDecimal#BigDecimal(double)}. | |
* <br> | |
* Regle PMD explicative : <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* | |
* @param nb | |
* @return | |
* @since 1.4 | |
*/ | |
public static BigDecimal createBigDecimal(long nb) | |
{ | |
return BigDecimal.valueOf(nb); | |
} | |
/** | |
* Creer un BigDecimal a partir d'un double sans passer par le constructeur {@link BigDecimal#BigDecimal(double)}. | |
* <br> | |
* Regle PMD explicative : <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* | |
* @param nb | |
* @return | |
* @since 1.4 | |
*/ | |
public static BigDecimal createBigDecimal(double nb) | |
{ | |
// Methode utilisee dans JDK 1.5 dans la methode : | |
// public static BigDecimal valueOf(double val) | |
return new BigDecimal(Double.toString(nb)); | |
} | |
/** | |
* Creer un BigDecimal a partir d'un double sans passer par le constructeur {@link BigDecimal#BigDecimal(double)}. | |
* <br> | |
* Regle PMD explicative : <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* | |
* @param nb | |
* @param newScale | |
* @param roundingMode | |
* @return | |
* @since 1.5 | |
*/ | |
public static BigDecimal createBigDecimalWithScale(double nb, int newScale, RoundingMode roundingMode) | |
{ | |
BigDecimal roundedBg = new BigDecimal(Double.toString(nb)); | |
roundedBg = roundedBg.setScale(newScale, roundingMode); | |
return roundedBg; | |
} | |
/** | |
* Creer un BigDecimal a partir d'un float sans passer par le constructeur {@link BigDecimal#BigDecimal(double)}. | |
* <br> | |
* Regle PMD explicative : <a href="http://pmd.sourceforge.net/pmd-4.3.0/rules/basic.html#AvoidDecimalLiteralsInBigDecimalConstructor">AvoidDecimalLiteralsInBigDecimalConstructor</a> | |
* | |
* @param nb | |
* @return | |
* @since 1.4 | |
*/ | |
public static BigDecimal createBigDecimal(float nb) | |
{ | |
return new BigDecimal(Float.toString(nb)); | |
} | |
/** | |
* Creer un BigDecimal a partir d'un String. | |
* | |
* @param str | |
* @return | |
* @see NumberUtils#createBigDecimal(String) | |
*/ | |
public static BigDecimal createBigDecimal(String str) | |
{ | |
return NumberUtils.createBigDecimal(str); | |
} | |
/** | |
* <p>Retourne soit le BigDecimal passe en parametre, | |
* ou s'il est {@code null}, retourne {@code BigDecimal.ZERO}.</p> | |
* <p/> | |
* <pre> | |
* BigDecimalUtils.defaultBigDecimal(null) == BigDecimal.ZERO | |
* BigDecimal bigDecimal1 <- new BigDecimal("12.34") | |
* BigDecimalUtils.defaultBigDecimal(bigDecimal1) == bigDecimal1 | |
* </pre> | |
* | |
* @param bigDec Peut etre null | |
* @return le BigDecimal passe en parametre, ou {@code BigDecimal.ZERO} | |
* s'il est {@code null} | |
* @see StringUtils#defaultString(String) | |
*/ | |
public static BigDecimal defaultBigDecimal(BigDecimal bigDec) | |
{ | |
return bigDec == null ? BigDecimal.ZERO : bigDec; | |
} | |
/** | |
* Addition d'une liste variable de {@link BigDecimal}. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de l'addition. | |
* @see BigDecimal#add(BigDecimal) | |
*/ | |
public static BigDecimal add(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.add(defaultBigDecimal(nb)); | |
} | |
return result; | |
} | |
public static BigDecimal add(Iterable<BigDecimal> numbers) | |
{ | |
BigDecimal result = BigDecimal.ZERO; | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.add(defaultBigDecimal(nb)); | |
} | |
return result; | |
} | |
/** | |
* Soustraction d'une liste variable de {@link BigDecimal}. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la soustraction. | |
* @see BigDecimal#subtract(BigDecimal) | |
*/ | |
public static BigDecimal subtract(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.subtract(defaultBigDecimal(nb)); | |
} | |
return result; | |
} | |
/** | |
* Multiplication d'une liste variable de {@link BigDecimal}. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la multiplication. | |
* @see BigDecimal#multiply(BigDecimal) | |
*/ | |
public static BigDecimal multiply(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.multiply(defaultBigDecimal(nb)); | |
} | |
return result; | |
} | |
/** | |
* Division d'une liste variable de {@link BigDecimal}. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la division. | |
* @throws ArithmeticException Si erreur | |
* @see BigDecimal#divide(BigDecimal) | |
* @deprecated Utiliser de preference {@link #divide(RoundingMode, BigDecimal, BigDecimal...)}. | |
*/ | |
@Deprecated | |
public static BigDecimal divide(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.divide(defaultBigDecimal(nb)); | |
} | |
return result; | |
} | |
/** | |
* Division d'une liste variable de {@link BigDecimal}, avec regle d'arrondi. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param roundingMode Regle d'arrondi | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la division. | |
* @throws ArithmeticException Si erreur | |
* @see BigDecimal#divide(BigDecimal, RoundingMode) | |
*/ | |
public static BigDecimal divide(RoundingMode roundingMode, BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.divide(defaultBigDecimal(nb), roundingMode); | |
} | |
return result; | |
} | |
/** | |
* Division d'une liste variable de {@link BigDecimal}, avec definition du nombre de decimales | |
* et regle d'arrondi. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param scale Nombre de decimales | |
* @param roundingMode Regle d'arrondi | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la division. | |
* @throws ArithmeticException Si erreur | |
* @see BigDecimal#divide(BigDecimal, RoundingMode) | |
*/ | |
public static BigDecimal divide(int scale, RoundingMode roundingMode, BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.divide(defaultBigDecimal(nb), scale, roundingMode); | |
} | |
return result; | |
} | |
/** | |
* Division d'une liste variable de {@link BigDecimal}, avec definition du nombre de decimales | |
* et regle d'arrondi. | |
* <br/> | |
* Methode null-safe : si un nombre est {@code null}, on prend par defaut zero. | |
* | |
* @param mc | |
* @param firstNumber | |
* @param numbers | |
* @return Resultat de la division. | |
* @throws ArithmeticException Si erreur | |
* @see BigDecimal#divide(BigDecimal, MathContext) | |
*/ | |
public static BigDecimal divide(MathContext mc, BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
BigDecimal result = defaultBigDecimal(firstNumber); | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.divide(defaultBigDecimal(nb), mc); | |
} | |
return result; | |
} | |
/** | |
* Retourne le max d'une liste de BigDecimal. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Valeur max. | |
* @see BigDecimal#max(BigDecimal) | |
*/ | |
public static BigDecimal max(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
if (firstNumber == null) | |
{ | |
throw new IllegalArgumentException("1er parametre null"); | |
} | |
BigDecimal result = firstNumber; | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.max(nb); | |
} | |
return result; | |
} | |
/** | |
* Retourne le max d'une liste de BigDecimal. | |
* | |
* @param numbers | |
* @return Valeur max. | |
* @see BigDecimal#max(BigDecimal) | |
*/ | |
public static BigDecimal max(Iterable<BigDecimal> numbers) | |
{ | |
BigDecimal result = null; | |
for (BigDecimal nb : numbers) | |
{ | |
if (result == null) | |
{ | |
// 1ere iteration | |
result = nb; | |
} | |
else | |
{ | |
result = result.max(nb); | |
} | |
} | |
return result; | |
} | |
/** | |
* Retourne le min d'une liste de BigDecimal. | |
* | |
* @param firstNumber | |
* @param numbers | |
* @return Valeur min. | |
* @see BigDecimal#min(BigDecimal) | |
*/ | |
public static BigDecimal min(BigDecimal firstNumber, BigDecimal... numbers) | |
{ | |
if (firstNumber == null) | |
{ | |
throw new IllegalArgumentException("1er parametre null"); | |
} | |
BigDecimal result = firstNumber; | |
for (BigDecimal nb : numbers) | |
{ | |
result = result.min(nb); | |
} | |
return result; | |
} | |
/** | |
* Retourne le min d'une liste de BigDecimal. | |
* | |
* @param numbers | |
* @return Valeur min. | |
* @see BigDecimal#min(BigDecimal) | |
*/ | |
public static BigDecimal min(Iterable<BigDecimal> numbers) | |
{ | |
BigDecimal result = null; | |
for (BigDecimal nb : numbers) | |
{ | |
if (result == null) | |
{ | |
// 1ere iteration | |
result = nb; | |
} | |
else | |
{ | |
result = result.min(nb); | |
} | |
} | |
return result; | |
} | |
/** | |
* Comparer deux instances de {@link BigDecimal}.<br/> | |
* La plupart du temps, il est preferable d'utiliser | |
* la methode <code>compareTo()</code> plutot que <code>equals()</code> dans le cas de la classe BigDecimal. | |
* | |
* @param bd1 | |
* @param bd2 | |
* @return Nombre entier, 0 si egaux. | |
* @see BigDecimal#compareTo(BigDecimal) | |
*/ | |
public static int compare(BigDecimal bd1, BigDecimal bd2) | |
{ | |
return bd1.compareTo(bd2); | |
} | |
/** | |
* Raccourci pour la methode {@link #compare(BigDecimal, BigDecimal)}. | |
* | |
* @param bd1 | |
* @param bd2 | |
* @return true si comparables. | |
* @see BigDecimalUtils#compare(BigDecimal, BigDecimal) | |
*/ | |
public static boolean isComparable(BigDecimal bd1, BigDecimal bd2) | |
{ | |
return compare(bd1, bd2) == 0; | |
} | |
/** | |
* Raccourci pour la methode {@link #compare(BigDecimal, BigDecimal)}. | |
* | |
* @param bd1 | |
* @param bd2 | |
* @return true si non comparables. | |
* @see BigDecimalUtils#compare(BigDecimal, BigDecimal) | |
*/ | |
public static boolean isNotComparable(BigDecimal bd1, BigDecimal bd2) | |
{ | |
return compare(bd1, bd2) != 0; | |
} | |
/** | |
* Raccourci pour la methode {@link BigDecimal#compareTo(BigDecimal)} différent de ZERO. | |
* | |
* @param bd1 | |
* @return true si non comparable a ZERO. | |
*/ | |
public static boolean isNotComparableToZero(BigDecimal bd1) | |
{ | |
return bd1.compareTo(BigDecimal.ZERO) != 0; | |
} | |
/** | |
* Raccourci pour la methode {@link BigDecimal#compareTo(BigDecimal)} avec ZERO. | |
* | |
* @param bd1 | |
* @return true si comparable a ZERO. | |
*/ | |
public static boolean isComparableToZero(BigDecimal bd1) | |
{ | |
return bd1.compareTo(BigDecimal.ZERO) == 0; | |
} | |
/** | |
* Raccourci pour la methode {@link BigDecimal#compareTo(BigDecimal)} avec ZERO. | |
* | |
* @param bd1 | |
* @return resultat entier de la comparaison avec ZERO (-1, 0 ou 1). | |
* @see BigDecimal#compareTo(BigDecimal) | |
*/ | |
public static int compareToZero(BigDecimal bd1) | |
{ | |
return bd1.compareTo(BigDecimal.ZERO); | |
} | |
/** | |
* Détermine si un BigDecimal est strictement positif. | |
* | |
* @param bd1 Nombre | |
* @return | |
* @see BigDecimal#signum() | |
*/ | |
public static boolean isPositive(BigDecimal bd1) | |
{ | |
return bd1.signum() == 1; | |
} | |
/** | |
* Détermine si un BigDecimal est positif ou nul. | |
* | |
* @param bd1 Nombre | |
* @return | |
* @see BigDecimal#signum() | |
*/ | |
public static boolean isPositiveOrZero(BigDecimal bd1) | |
{ | |
return bd1.signum() >= 0; | |
} | |
/** | |
* Détermine si un BigDecimal est strictement négatif. | |
* | |
* @param bd1 Nombre | |
* @return | |
* @see BigDecimal#signum() | |
*/ | |
public static boolean isNegative(BigDecimal bd1) | |
{ | |
return bd1.signum() == -1; | |
} | |
/** | |
* Détermine si un BigDecimal est négatif ou nul. | |
* | |
* @param bd1 Nombre | |
* @return | |
* @see BigDecimal#signum() | |
*/ | |
public static boolean isNegativeOrZero(BigDecimal bd1) | |
{ | |
return bd1.signum() <= 0; | |
} | |
/** | |
* Détermine si un BigDecimal est égal à 0 (conformément à {@link BigDecimal#signum()}). | |
* | |
* @param bd1 Nombre | |
* @return | |
* @see BigDecimal#signum() | |
*/ | |
public static boolean isZero(BigDecimal bd1) | |
{ | |
return bd1.signum() == 0; | |
} | |
/** | |
* Appel de {@link BigDecimal#setScale(int)} | |
* <br/> | |
* Methode null-safe. | |
* | |
* @param bd BigDecimal en entree | |
* @param newScale scale en entree | |
* @return Nouveau BigDecimal, ou {@code null} si BigDecimal null en entree | |
* @throws ArithmeticException if the specified scaling operation would require rounding. | |
* @see BigDecimal#setScale(int) | |
* @deprecated Utiliser de preference {@link #changeScale(BigDecimal, int, RoundingMode)}. | |
*/ | |
@Deprecated | |
public static BigDecimal changeScale(final BigDecimal bd, int newScale) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
return bd.setScale(newScale); | |
} | |
/** | |
* Appel de {@link BigDecimal#setScale(int, RoundingMode)} (avec regle d'arrondi). | |
* <br/> | |
* Methode null-safe. | |
* | |
* @param bd BigDecimal en entree | |
* @param newScale scale en entree | |
* @param roundingMode Regle d'arrondi | |
* @return Nouveau BigDecimal, ou {@code null} si BigDecimal null en entree | |
* @throws ArithmeticException if roundingMode==UNNECESSARY and the specified scaling operation would require rounding. | |
* @see BigDecimal#setScale(int) | |
*/ | |
public static BigDecimal changeScale(final BigDecimal bd, int newScale, RoundingMode roundingMode) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
return bd.setScale(newScale, roundingMode); | |
} | |
/** | |
* Appel de {@link BigDecimal#setScale(int, RoundingMode)} (avec regle d'arrondi). | |
* | |
* @param bd | |
* @param mathContext | |
* @return Nouveau BigDecimal, ou {@code null} si BigDecimal null en entree | |
* @throws ArithmeticException if roundingMode==UNNECESSARY and the specified scaling operation would require rounding. | |
*/ | |
public static BigDecimal changeScale(final BigDecimal bd, final MathContext mathContext) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
return bd.setScale(mathContext.getPrecision(), mathContext.getRoundingMode()); | |
} | |
/** | |
* Converts this {@code BigDecimal} to a {@code BigInteger}, | |
* checking for lost information. An exception is thrown if this | |
* {@code BigDecimal} has a nonzero fractional part. | |
* | |
* @param bd | |
* @return {@code BigDecimal} converted to a {@code BigInteger}, or {@code null}. | |
* @throws ArithmeticException if {@code this} has a nonzero fractional part. | |
* @see BigDecimal#toBigIntegerExact() | |
*/ | |
public static BigInteger toBigIntegerExact(final BigDecimal bd) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
return bd.toBigIntegerExact(); | |
} | |
/** | |
* Tronquer la partie decimale de {@code BigDecimal} non nécessaire. | |
* | |
* @param bd {@code BigDecimal} en entrée. | |
* @return Nouveau {@code BigDecimal}, ou {@code null}. | |
* @throws ArithmeticException Pb arrondi | |
* @see BigDecimal#toBigIntegerExact() | |
* @see BigDecimal#stripTrailingZeros() | |
*/ | |
public static BigDecimal toBigDecimalWithoutFractionalPart(final BigDecimal bd) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
return bd.setScale(0, BigDecimal.ROUND_UNNECESSARY); | |
} | |
/** | |
* Obtenir la partie décimale d'un {@code BigDecimal} uniquement. | |
* <pre> | |
* getFractionalPart(new BigDecimal( "-12.5689" )) => new BigInteger("5689") | |
* </pre> | |
* | |
* @param bd Nombre en entrée. | |
* @return BigInteger | |
*/ | |
public static BigInteger getFractionalPart(final BigDecimal bd) | |
{ | |
if (bd == null) | |
{ | |
return null; | |
} | |
// soit le reste de la division entiere par 1 (val absolue) | |
BigDecimal remainder = bd.remainder(BigDecimal.ONE).abs(); | |
return new BigInteger(remainder.toString().substring("0.".length())); | |
} | |
/** | |
* Applique la regle d'arrondi par defaut pour les montants monetaires. | |
* <br> | |
* Soit 2 decimales, et arrondi {@link RoundingMode#HALF_UP}. | |
* | |
* @param bd Montant en entree. | |
* @return Nouveau Montant | |
*/ | |
public static BigDecimal arrondiMonetaire(final BigDecimal bd) | |
{ | |
return changeScale(bd, MATH_CONTEXT_ARRONDI_MONETAIRE_EURO.getPrecision(), | |
MATH_CONTEXT_ARRONDI_MONETAIRE_EURO.getRoundingMode()); | |
} | |
/** | |
* Calcul de repartition d'une somme au <b>prorata</b>. | |
* <br> | |
* <a href="http://stackoverflow.com/questions/1925691/proportionately-distribute-prorate-a-value-across-a-set-of-values">lien stackoverflow</a> | |
* | |
* @param montantRepartir Montant a repartir. | |
* @param montantsReference Liste des montants. Chacun represente un poids. | |
* @return Liste ordonnée de montants. | |
*/ | |
public static List<BigDecimal> calculerProrata(final BigDecimal montantRepartir, final List<BigDecimal> montantsReference) | |
{ | |
return calculerProrata(montantRepartir, montantsReference, null); | |
} | |
/** | |
* Calcul de repartition d'une somme au <b>prorata</b>. | |
* | |
* @param montantRepartir Montant a repartir. | |
* @param montantsReference Liste des montants. Chacun represente un poids. | |
* @param mcArrondi Regle d'arrondi a appliquer | |
* @return Liste ordonnée de montants. | |
*/ | |
public static List<BigDecimal> calculerProrata(final BigDecimal montantRepartir, final List<BigDecimal> montantsReference, | |
final MathContext mcArrondi) | |
{ | |
MathContext mc = new MathContext(10, RoundingMode.HALF_UP); | |
// à priori pas besoin de ce ctrl ? | |
boolean interdireDepassementMontantRepartir = false; | |
if (montantRepartir == null || BigDecimalUtils.isNegative(montantRepartir)) | |
{ | |
throw new IllegalArgumentException("Parametre 'montantRepartir' invalide (" + montantRepartir + ")"); | |
} | |
if (montantRepartir.compareTo(BigDecimal.ZERO) == 0) | |
{ | |
// retourner liste init à 0 | |
List<BigDecimal> listeZeros = new ArrayList<BigDecimal>(montantsReference.size()); | |
for (int i = 0; i < montantsReference.size(); i++) | |
{ | |
listeZeros.add(BigDecimal.ZERO); | |
} | |
return listeZeros; | |
} | |
if (montantsReference == null || montantsReference.isEmpty()) | |
{ | |
throw new IllegalArgumentException("Parametre 'montantsReference' invalide"); | |
} | |
List<BigDecimal> resultats = new ArrayList<BigDecimal>(); | |
BigDecimal sommeMontantsImpactes = BigDecimalUtils.add(montantsReference); | |
// regle a definir si besoin ? | |
if (interdireDepassementMontantRepartir) | |
{ | |
if (montantRepartir.compareTo(sommeMontantsImpactes) > 0) | |
{ | |
throw new IllegalArgumentException("Le montant a repartir ne peut etre superieur a la somme des montants"); | |
} | |
} | |
// verifier que la regle d'arrondi s'applique au montant a repartir | |
BigDecimal montantRepartirArrondi = null; | |
if (mcArrondi != null) | |
{ | |
montantRepartirArrondi = montantRepartir.setScale(mcArrondi.getPrecision(), mcArrondi.getRoundingMode()); | |
// FIXME: a confirmer, mais on n'utilise pas le montant a repartir arrondi par la suite... | |
// d'ou la possibilité d'une difference et une ArithmeticException | |
} | |
// calcul : montant prorata = poids / somme poids * montant repartir | |
BigDecimal resteRepartir = montantRepartir; | |
int maxMontantRefIndex = -1; | |
for (int i = 0; i < montantsReference.size(); i++) | |
{ | |
BigDecimal montantRef = montantsReference.get(i); | |
BigDecimal quotePart; | |
// trouver l'indice max, en cas de resteRepartir > 0 | |
if (i == 0) | |
{ | |
maxMontantRefIndex = 0; | |
} | |
else | |
{ | |
if (montantRef.compareTo(montantsReference.get(i - 1)) > 0) | |
{ | |
maxMontantRefIndex = i; | |
} | |
} | |
// multiply avant divide | |
BigDecimal m = montantRef.multiply(montantRepartir); | |
quotePart = m.divide(sommeMontantsImpactes, mc); | |
if (mcArrondi != null) | |
{ | |
// appliquer arrondi | |
quotePart = quotePart.setScale(mcArrondi.getPrecision(), mcArrondi.getRoundingMode()); | |
} | |
// calcul reste | |
resteRepartir = resteRepartir.subtract(quotePart); | |
resultats.add(quotePart); | |
} | |
// si resteRepartir != 0 ? | |
if (resteRepartir.compareTo(BigDecimal.ZERO) != 0) | |
{ | |
BigDecimal nouveauMt = null; | |
if (mcArrondi != null) | |
{ | |
nouveauMt = | |
resultats.get(maxMontantRefIndex).add( | |
resteRepartir.setScale(mcArrondi.getPrecision(), mcArrondi.getRoundingMode())); | |
} | |
else | |
{ | |
nouveauMt = resultats.get(maxMontantRefIndex).add(resteRepartir); | |
} | |
resultats.set(maxMontantRefIndex, nouveauMt); | |
} | |
// Verification si repartition avec perte | |
BigDecimal sommeMontantsApresRepartition = BigDecimalUtils.add(resultats); | |
if (montantRepartir.compareTo(sommeMontantsApresRepartition) != 0) | |
{ | |
BigDecimal diffArrondiMontantRepartir = BigDecimal.ZERO; | |
if (mcArrondi != null) | |
{ | |
diffArrondiMontantRepartir = montantRepartir.subtract(montantRepartirArrondi); | |
} | |
LOGGER.debug("calculerProrata : diffArrondiMontantRepartir = " + diffArrondiMontantRepartir); | |
BigDecimal diff = montantRepartir.subtract(sommeMontantsApresRepartition); | |
throw new ArithmeticException( | |
"La repartition ne tombe pas juste ! (diff = " + diff + "). Données calcul : montantRepartir = [" | |
+ montantRepartir + "]; montantsReference = [" + StringUtils.join(montantsReference, ", ") + "]"); | |
} | |
return resultats; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment