/** * 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.inflation; import java.math.BigDecimal; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Observable; import blacksmyth.general.ReflectionUtilities; import blacksmyth.general.SortedArrayList; import blacksmyth.general.file.IFileHandlerModel; import blacksmyth.personalfinancier.control.FinancierUndoManager; import blacksmyth.personalfinancier.control.inflation.IInflationController; import blacksmyth.personalfinancier.model.Money; import blacksmyth.personalfinancier.model.MoneyFactory; import blacksmyth.personalfinancier.model.ModelPreferences; public class InflationModel extends Observable implements InflationProvider, IInflationController, IFileHandlerModel<InflationFileContent> { private static GregorianCalendar TODAY; private static final String CONTROLLER_ASSERT_MSG = "Caller does not implement IInflationController."; private static final String VIEWER_ASSERT_MSG = "Caller does not implement IInflationObserver."; //Note: SortedSet of CPIEntries are to be sorted earliest to latest in date order. private static SortedArrayList<InflationEntry> inflationList = new SortedArrayList<InflationEntry>(); private final FinancierUndoManager undoManager = new FinancierUndoManager(); public InflationModel() { TODAY = new GregorianCalendar(); TODAY.setTime(new Date()); } public SortedArrayList<InflationEntry> getInflationList() { return InflationModel.inflationList; } public void setInflationList(SortedArrayList<InflationEntry> list) { InflationModel.inflationList = list; this.changeAndNotifyObservers(); } public FinancierUndoManager getUndoManager() { return undoManager; } @Override public Money computeComparisonValue(Money originalValue, Calendar originalDate, Calendar comparisonDate) { Money returnValue = MoneyFactory.copy(originalValue); BigDecimal originalCPIForDate = new BigDecimal(getCPIFigureForDate(originalDate)); BigDecimal comparsionCPIForDate = new BigDecimal(getCPIFigureForDate(comparisonDate)); if (getCPIFigureForDate(comparisonDate) == 0) { returnValue.setTotal( new BigDecimal(0) ); } else { returnValue.setTotal( originalValue.getTotal().multiply( comparsionCPIForDate.divide( originalCPIForDate, ModelPreferences.getInstance().getPreferredPrecision(), ModelPreferences.getInstance().getPreferredRoundingMode() ) ) ); } return returnValue; } @Override public double getCPIFigureForDate(Calendar date) { if (date.before(getEarliestDate())) { return getCPIFigureForDate(getEarliestDate()); } if (date.after(getLatestDate())) { return getCPIFigureForDate(getLatestDate()); } return getCPIFigureFromList(date); } public double getCPIFigureFromList(Calendar date) { if (inflationList.size() == 0) { return 0; } double cpiFigure = inflationList.last().getCPIValue(); for(int i = inflationList.size() - 1; i >= 0; i--) { Calendar applicableFromDate = inflationList.get(i).getDate(); cpiFigure = inflationList.get(i).getCPIValue(); if (applicableFromDate.before(date) || applicableFromDate.equals(date)) { break; } } return cpiFigure; } @Override public double getInflationForDateRange(Calendar earlierDate, Calendar laterDate) { if (inflationList.size() == 0) { return 0; } if (laterDate.before(earlierDate)) { return getInflationForDateRange(laterDate, earlierDate); } return getCPIFigureForDate(laterDate) / getCPIFigureForDate(earlierDate) - 1; } @Override public double getInflationPerAnnum(Calendar firstDate, Calendar secondDate) { if (inflationList.size() == 0) { return 0; } if (secondDate.before(firstDate)) { return getInflationPerAnnum(secondDate, firstDate); } double inflationOverDateRange = getInflationForDateRange(firstDate, secondDate); if (inflationOverDateRange != 0) { return inflationOverDateRange / getTimeDiffInYears(firstDate, secondDate); } return 0; } private double getTimeDiffInYears(Calendar earlierDate, Calendar laterDate) { final long MILLISECONDS_PER_DAY = 86400000; final double DAYS_PER_YEAR = MILLISECONDS_PER_DAY * 365.25; return (laterDate.getTimeInMillis() - earlierDate.getTimeInMillis()) / DAYS_PER_YEAR; } @Override public Calendar getEarliestDate() { if (inflationList.size() == 0) { return TODAY; } return inflationList.first().getDate(); } @Override public Calendar getLatestDate() { if (inflationList.size() == 0) { return TODAY; } return inflationList.last().getDate(); } public InflationEntry addEntry() { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; InflationEntry newItem = InflationEntryFactory.createEntry(inflationList); this.addEntry(newItem); return newItem; } public void addEntry(InflationEntry entry) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; inflationList.add(entry); this.changeAndNotifyObservers(); } public void addEntry(int index, InflationEntry item) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; inflationList.add(index, item); this.changeAndNotifyObservers(); } public InflationEntry removeEntry(int index) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; InflationEntry removedEntry = inflationList.remove(index); this.changeAndNotifyObservers(); return removedEntry; } public void removeEntry(InflationEntry entry) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; inflationList.remove(entry); this.changeAndNotifyObservers(); } public void setInflationEntryValue(int index, double value) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; assert (index >= 0 && index < inflationList.size()); inflationList.get(index).setCPIValue(value); this.changeAndNotifyObservers(); } public void setInflationEntryNotes(int index, String notes) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; assert (index >= 0 && index < inflationList.size()); inflationList.get(index).setNotes(notes); this.changeAndNotifyObservers(); } public void setInflationEntryDate(int index, Calendar date) { assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; assert (index >= 0 && index < inflationList.size()); inflationList.get(index).setDate(date); this.changeAndNotifyObservers(); } public void changeAndNotifyObservers() { this.setChanged(); this.notifyObservers(); } @Override public void fromSerializable(InflationFileContent content) { //assert ReflectionUtilities.callerImplements(IInflationController.class) : CONTROLLER_ASSERT_MSG; inflationList = content.inflationList; this.changeAndNotifyObservers(); } @Override public InflationFileContent toSerializable() { InflationFileContent content = new InflationFileContent(); content.inflationList = inflationList; return content; } }