/**
* Copyright (c) 2012, Lindsay Bradford and other Contributors.
* All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the BSD 3-Clause licence which accompanies
* this distribution, and is available at
* http://opensource.org/licenses/BSD-3-Clause
*/
package blacksmyth.personalfinancier.model;
import java.math.BigDecimal;
import java.math.MathContext;
public final class CashFlowFrequencyUtility {
// TODO: make this an observer of the Preferences so we react correctly to context changes.
private static final MathContext MATH_CONTEXT = ModelPreferences.getInstance().getPreferredMathContext();
private static final BigDecimal MONTHS_PER_QUARTER = BigDecimalFactory.create(3);
private static final BigDecimal MONTHS_PER_YEAR = BigDecimalFactory.create(12);
private static final BigDecimal DAYS_PER_YEAR = BigDecimalFactory.create(365.25);
private static final BigDecimal DAYS_PER_MONTH = DAYS_PER_YEAR.divide(MONTHS_PER_YEAR, MATH_CONTEXT);
private static final BigDecimal DAYS_PER_FORTNIGHT = BigDecimalFactory.create(14);
private static final BigDecimal DAYS_PER_WEEK = BigDecimalFactory.create(7);
/**
* Converts <tt>initialAmount</tt> from a figure that recurs at <tt>initialFrequency</tt> to the equivalent amount
* that recurs at <tt>newFrequency</tt> and returns this converted amount.
* @param initialAmount
* @param initialFrequency
* @param newFrequency
* @return the initial amount converted to the equivalent at a different cash flow frequency.
*/
public static BigDecimal convertFrequencyAmount(BigDecimal initialAmount, CashFlowFrequency initialFrequency,
CashFlowFrequency newFrequency) {
if (initialAmount == null) return null;
if (initialFrequency == newFrequency) return initialAmount;
// Convert initial amount to a monthly amount, then convert the monthly amount to
// the amount at newFrequency.
// It doesn't really matter which frequency I choose to standardise on. I've picked
// Monthly as many of the lower frequency periods are v-easy to calculate from using it.
return convertMonthlyAmountToNewFrequency(
convertFrequencyAmountToMonthly(
initialAmount,
initialFrequency
),
newFrequency
);
}
/**
* Converts an <tt>initialAmount</tt> with a cash-flow frequency of <tt>initialFreqnency</tt> to the equivalent
* amount at a monthly frequency.
* @param initialAmount
* @param initialFrequency
* @return the amount at a cash-flow frequency of monthly.
*/
private static BigDecimal convertFrequencyAmountToMonthly(BigDecimal initialAmount, CashFlowFrequency initialFrequency) {
switch (initialFrequency) {
case Yearly:
return initialAmount.divide(MONTHS_PER_YEAR, MATH_CONTEXT);
case Quarterly:
return initialAmount.divide(MONTHS_PER_QUARTER, MATH_CONTEXT); // 3 months in a quarter
case Monthly:
return initialAmount; // no conversion necessary.
case Fortnightly:
return initialAmount.multiply(
DAYS_PER_MONTH.divide(DAYS_PER_FORTNIGHT, MATH_CONTEXT),
MATH_CONTEXT
);
case Weekly:
return initialAmount.multiply(
DAYS_PER_MONTH.divide(DAYS_PER_WEEK, MATH_CONTEXT),
MATH_CONTEXT
);
case Daily:
return initialAmount.multiply(DAYS_PER_MONTH, MATH_CONTEXT);
}
return null;
}
/**
* Converts an amount at a cash-flow frequency of monthly to a new cash-flow frequency of
* <tt>newFrequency</tt>
* @param monthlyAmount
* @param newFrequency
* @return The amount converted to the specified cash-flow frequency.
*/
private static BigDecimal convertMonthlyAmountToNewFrequency(BigDecimal monthlyAmount,
CashFlowFrequency newFrequency) {
switch(newFrequency) {
case Yearly:
return monthlyAmount.multiply(MONTHS_PER_YEAR, MATH_CONTEXT);
case Quarterly:
return monthlyAmount.multiply(MONTHS_PER_QUARTER, MATH_CONTEXT);
case Monthly:
return monthlyAmount;
case Fortnightly:
return monthlyAmount.divide(
DAYS_PER_MONTH.divide(DAYS_PER_FORTNIGHT, MATH_CONTEXT),
MATH_CONTEXT
);
case Weekly:
return monthlyAmount.divide(
DAYS_PER_MONTH.divide(DAYS_PER_WEEK, MATH_CONTEXT),
MATH_CONTEXT
);
case Daily:
return monthlyAmount.divide(DAYS_PER_MONTH, MATH_CONTEXT);
}
return null;
}
}