1 | |
|
2 | |
|
3 | |
|
4 | |
package org.homeunix.thecave.buddi.model.impl; |
5 | |
|
6 | |
import java.util.Date; |
7 | |
import java.util.HashMap; |
8 | |
import java.util.LinkedList; |
9 | |
import java.util.List; |
10 | |
import java.util.Map; |
11 | |
import java.util.logging.Logger; |
12 | |
|
13 | |
import org.homeunix.thecave.buddi.model.BudgetCategory; |
14 | |
import org.homeunix.thecave.buddi.model.BudgetCategoryType; |
15 | |
import org.homeunix.thecave.buddi.model.Document; |
16 | |
import org.homeunix.thecave.buddi.model.ModelObject; |
17 | |
import org.homeunix.thecave.buddi.plugin.api.exception.DataModelProblemException; |
18 | |
import org.homeunix.thecave.buddi.plugin.api.exception.InvalidValueException; |
19 | |
|
20 | |
import ca.digitalcave.moss.collections.SortedArrayList; |
21 | |
import ca.digitalcave.moss.common.DateUtil; |
22 | |
|
23 | |
|
24 | |
|
25 | |
|
26 | |
|
27 | |
|
28 | |
|
29 | |
|
30 | 489930 | public class BudgetCategoryImpl extends SourceImpl implements BudgetCategory { |
31 | |
private boolean income; |
32 | |
private boolean expanded; |
33 | |
private BudgetCategoryType periodType; |
34 | |
private BudgetCategory parent; |
35 | |
private Map<String, Long> amounts; |
36 | |
private List<BudgetCategory> children; |
37 | |
private List<BudgetCategory> allChildren; |
38 | |
|
39 | |
public Map<String, Long> getAmounts() { |
40 | 132670 | if (amounts == null) |
41 | 30250 | amounts = new HashMap<String, Long>(); |
42 | 132670 | return amounts; |
43 | |
} |
44 | |
public void setAmounts(Map<String, Long> amounts) { |
45 | 0 | this.amounts = amounts; |
46 | 0 | } |
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
public long getAmount(Date periodDate){ |
54 | 132670 | Long l = getAmounts().get(getPeriodKey(periodDate)); |
55 | 132670 | if (l == null) |
56 | 132670 | return 0; |
57 | 0 | return l; |
58 | |
} |
59 | |
|
60 | |
@Override |
61 | |
public void setDeleted(boolean deleted) throws InvalidValueException { |
62 | 0 | if (getDocument() != null) |
63 | 0 | getDocument().startBatchChange(); |
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | 0 | if (getDocument() != null){ |
71 | 0 | if (deleted){ |
72 | |
|
73 | 0 | for (BudgetCategory bc : getChildren()) { |
74 | 0 | bc.setDeleted(deleted); |
75 | |
} |
76 | |
} |
77 | |
else { |
78 | |
|
79 | 0 | if (getParent() != null) |
80 | 0 | getParent().setDeleted(deleted); |
81 | |
} |
82 | |
} |
83 | |
|
84 | 0 | setChanged(); |
85 | |
|
86 | 0 | super.setDeleted(deleted); |
87 | |
|
88 | 0 | if (getDocument() != null) |
89 | 0 | getDocument().finishBatchChange(); |
90 | 0 | } |
91 | |
|
92 | |
public List<BudgetCategory> getChildren() { |
93 | 53872 | if (children == null) |
94 | 30242 | children = new FilteredLists.BudgetCategoryListFilteredByDeleted(getDocument(), new FilteredLists.BudgetCategoryListFilteredByParent(getDocument(), getDocument().getBudgetCategories(), this)); |
95 | 53872 | return children; |
96 | |
} |
97 | |
|
98 | |
public List<BudgetCategory> getAllChildren() { |
99 | 0 | if (allChildren == null) |
100 | 0 | allChildren = new FilteredLists.BudgetCategoryListFilteredByParent(getDocument(), getDocument().getBudgetCategories(), this); |
101 | 0 | return allChildren; |
102 | |
} |
103 | |
|
104 | |
public long getAmount(Date startDate, Date endDate){ |
105 | 0 | if (startDate.after(endDate)) |
106 | 0 | throw new RuntimeException("Start date cannot be before End Date!"); |
107 | |
|
108 | 0 | Logger.getLogger(this.getClass().getName()).info("Starting to calculate the budgeted amount for " + getFullName() + " between " + startDate + " and " + endDate + "."); |
109 | |
|
110 | |
|
111 | 0 | if (getBudgetPeriodType().getStartOfBudgetPeriod(startDate).equals( |
112 | |
getBudgetPeriodType().getStartOfBudgetPeriod(endDate))){ |
113 | |
|
114 | 0 | long amount = getAmount(startDate); |
115 | |
|
116 | 0 | long daysInPeriod = getBudgetPeriodType().getDaysInPeriod(startDate); |
117 | |
|
118 | 0 | long daysBetween = DateUtil.getDaysBetween(startDate, endDate, true); |
119 | |
|
120 | |
|
121 | |
|
122 | |
|
123 | 0 | return (long) (((double) amount / (double) daysInPeriod) * daysBetween); |
124 | |
} |
125 | |
|
126 | |
|
127 | 0 | if (getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1).equals( |
128 | |
getBudgetPeriodType().getStartOfBudgetPeriod(endDate)) |
129 | |
|| getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1).before( |
130 | |
getBudgetPeriodType().getStartOfBudgetPeriod(endDate))){ |
131 | |
|
132 | 0 | long amountStartPeriod = getAmount(startDate); |
133 | |
|
134 | 0 | long daysInStartPeriod = getBudgetPeriodType().getDaysInPeriod(startDate); |
135 | |
|
136 | 0 | long daysAfterStartDateInStartPeriod = DateUtil.getDaysBetween(startDate, getBudgetPeriodType().getEndOfBudgetPeriod(startDate), true); |
137 | |
|
138 | 0 | double totalStartPeriod = (((double) amountStartPeriod / (double) daysInStartPeriod) * daysAfterStartDateInStartPeriod); |
139 | |
|
140 | |
|
141 | 0 | double totalInMiddle = 0; |
142 | 0 | for (String periodKey : getBudgetPeriods( |
143 | |
getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1), |
144 | |
getBudgetPeriodType().getBudgetPeriodOffset(endDate, -1))) { |
145 | 0 | totalInMiddle += getAmount(getPeriodDate(periodKey)); |
146 | 0 | Logger.getLogger(this.getClass().getName()).info("Added " + getAmount(getPeriodDate(periodKey)) + " to total for one period in between; current value is " + totalInMiddle); |
147 | |
} |
148 | |
|
149 | |
|
150 | 0 | long amountEndPeriod = getAmount(endDate); |
151 | |
|
152 | 0 | long daysInEndPeriod = getBudgetPeriodType().getDaysInPeriod(endDate); |
153 | |
|
154 | 0 | long daysBeforeEndDateInEndPeriod = DateUtil.getDaysBetween(getBudgetPeriodType().getStartOfBudgetPeriod(endDate), endDate, true); |
155 | |
|
156 | 0 | double totalEndPeriod = (long) (((double) amountEndPeriod / (double) daysInEndPeriod) * daysBeforeEndDateInEndPeriod); |
157 | |
|
158 | |
|
159 | |
|
160 | |
|
161 | 0 | return (long) (totalStartPeriod + totalInMiddle + totalEndPeriod); |
162 | |
} |
163 | |
|
164 | 0 | throw new RuntimeException("You should not be here. We have returned all legitimate numbers from getAmount(Date, Date) in BudgetCategoryImpl. Please contact Wyatt Olson with details on how you got here (what steps did you perform in Buddi to get this error message)."); |
165 | |
} |
166 | |
|
167 | |
|
168 | |
|
169 | |
|
170 | |
|
171 | |
|
172 | |
|
173 | |
|
174 | |
public List<String> getBudgetPeriods(Date startDate, Date endDate){ |
175 | 0 | List<String> budgetPeriodKeys = new LinkedList<String>(); |
176 | |
|
177 | 0 | Date temp = getBudgetPeriodType().getStartOfBudgetPeriod(startDate); |
178 | |
|
179 | 0 | while (temp.before(getBudgetPeriodType().getEndOfBudgetPeriod(endDate))){ |
180 | 0 | budgetPeriodKeys.add(getPeriodKey(temp)); |
181 | 0 | temp = getBudgetPeriodType().getBudgetPeriodOffset(temp, 1); |
182 | |
} |
183 | |
|
184 | 0 | return budgetPeriodKeys; |
185 | |
} |
186 | |
|
187 | |
|
188 | |
|
189 | |
|
190 | |
|
191 | |
|
192 | |
public void setAmount(Date periodDate, long amount){ |
193 | 0 | if (getAmount(periodDate) != amount) |
194 | 0 | setChanged(); |
195 | 0 | getAmounts().put(getPeriodKey(periodDate), amount); |
196 | 0 | } |
197 | |
public BudgetCategoryType getPeriodType() { |
198 | 1943840 | return periodType; |
199 | |
} |
200 | |
public void setPeriodType(BudgetCategoryType periodType) { |
201 | 30340 | this.periodType = periodType; |
202 | 30340 | setChanged(); |
203 | 30340 | } |
204 | |
public boolean isIncome() { |
205 | 1194192 | return income; |
206 | |
} |
207 | |
public void setIncome(boolean income) { |
208 | 30340 | this.income = income; |
209 | 30340 | setChanged(); |
210 | 30340 | } |
211 | |
@Override |
212 | |
public void setName(String name) throws InvalidValueException { |
213 | |
|
214 | |
|
215 | |
|
216 | |
|
217 | |
|
218 | |
|
219 | |
|
220 | |
|
221 | |
|
222 | 30340 | super.setName(name); |
223 | 30340 | } |
224 | |
public BudgetCategory getParent() { |
225 | 1722167 | return parent; |
226 | |
} |
227 | |
public void setParent(BudgetCategory parent) { |
228 | 0 | this.parent = parent; |
229 | 0 | setChanged(); |
230 | 0 | } |
231 | |
public boolean isExpanded() { |
232 | 123100 | return expanded; |
233 | |
} |
234 | |
public void setExpanded(boolean expanded) { |
235 | 0 | this.expanded = expanded; |
236 | 0 | } |
237 | |
|
238 | |
|
239 | |
|
240 | |
|
241 | |
|
242 | |
|
243 | |
|
244 | |
|
245 | |
|
246 | |
private String getPeriodKey(Date periodDate){ |
247 | 132670 | Date d = getBudgetPeriodType().getStartOfBudgetPeriod(periodDate); |
248 | 132670 | return getBudgetPeriodType().getName() + ":" + DateUtil.getYear(d) + ":" + DateUtil.getMonth(d) + ":" + DateUtil.getDay(d); |
249 | |
} |
250 | |
|
251 | |
|
252 | |
|
253 | |
|
254 | |
|
255 | |
private Date getPeriodDate(String periodKey){ |
256 | 0 | String[] splitKey = periodKey.split(":"); |
257 | 0 | if (splitKey.length == 4){ |
258 | 0 | int year = Integer.parseInt(splitKey[1]); |
259 | 0 | int month = Integer.parseInt(splitKey[2]); |
260 | 0 | int day = Integer.parseInt(splitKey[3]); |
261 | 0 | return getBudgetPeriodType().getStartOfBudgetPeriod(DateUtil.getDate(year, month, day)); |
262 | |
} |
263 | |
|
264 | 0 | throw new DataModelProblemException("Cannot parse date from key " + periodKey); |
265 | |
} |
266 | |
public String getFullName(){ |
267 | 809799 | if (getDocument() != null && getParent() != null && !getParent().equals(this)){ |
268 | 0 | for (BudgetCategory bc : getDocument().getBudgetCategories()) { |
269 | 0 | if (bc.getName().equals(this.getName()) |
270 | |
&& !bc.equals(this)) |
271 | 0 | return this.getName() + " (" + this.getParent().getName() + ")"; |
272 | |
} |
273 | |
} |
274 | 809799 | return this.getName(); |
275 | |
} |
276 | |
|
277 | |
|
278 | |
|
279 | |
|
280 | |
public BudgetCategoryType getBudgetPeriodType() { |
281 | 971920 | if (getPeriodType() == null) |
282 | 0 | setPeriodType(new BudgetCategoryTypeMonthly()); |
283 | 971920 | return getPeriodType(); |
284 | |
} |
285 | |
@Override |
286 | |
public int compareTo(ModelObject arg0) { |
287 | 459590 | if (arg0 instanceof BudgetCategoryImpl){ |
288 | 459590 | BudgetCategoryImpl c = (BudgetCategoryImpl) arg0; |
289 | 459590 | if (this.isIncome() != c.isIncome()) |
290 | 61516 | return -1 * Boolean.valueOf(this.isIncome()).compareTo(Boolean.valueOf(c.isIncome())); |
291 | 398074 | return this.getFullName().compareTo(c.getFullName()); |
292 | |
} |
293 | 0 | return super.compareTo(arg0); |
294 | |
} |
295 | |
|
296 | |
public List<Date> getBudgetedDates() { |
297 | 0 | List<Date> budgetedDates = new SortedArrayList<Date>(); |
298 | |
|
299 | 0 | Map<String, Long> amounts = getAmounts(); |
300 | 0 | for (String key : amounts.keySet()){ |
301 | 0 | if (amounts.get(key) != null && amounts.get(key) != 0) |
302 | 0 | budgetedDates.add(getPeriodDate(key)); |
303 | |
} |
304 | |
|
305 | 0 | return budgetedDates; |
306 | |
} |
307 | |
|
308 | |
BudgetCategory clone(Map<ModelObject, ModelObject> originalToCloneMap) throws CloneNotSupportedException { |
309 | |
|
310 | 0 | if (originalToCloneMap.get(this) != null) |
311 | 0 | return (BudgetCategory) originalToCloneMap.get(this); |
312 | |
|
313 | 0 | BudgetCategoryImpl b = new BudgetCategoryImpl(); |
314 | 0 | originalToCloneMap.put(this, b); |
315 | |
|
316 | 0 | b.document = (Document) originalToCloneMap.get(document); |
317 | 0 | b.expanded = expanded; |
318 | 0 | b.income = income; |
319 | 0 | b.periodType = periodType; |
320 | 0 | b.deleted = isDeleted(); |
321 | 0 | b.modifiedTime = new Time(modifiedTime); |
322 | 0 | b.name = name; |
323 | 0 | b.notes = notes; |
324 | 0 | if (parent != null) |
325 | 0 | b.parent = (BudgetCategory) ((BudgetCategoryImpl) parent).clone(originalToCloneMap); |
326 | 0 | b.amounts = new HashMap<String, Long>(); |
327 | 0 | if (amounts != null){ |
328 | 0 | for (String s : amounts.keySet()) { |
329 | 0 | b.amounts.put(s, amounts.get(s).longValue()); |
330 | |
} |
331 | |
} |
332 | |
|
333 | 0 | return b; |
334 | |
} |
335 | |
} |