001: /*
002: *
003: * JMoney - A Personal Finance Manager
004: * Copyright (c) 2002 Johann Gyger <johann.gyger@switzerland.org>
005: * Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
006: *
007: *
008: * This program is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU General Public License as published by
010: * the Free Software Foundation; either version 2 of the License, or
011: * (at your option) any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: package net.sf.jmoney.model2;
025:
026: import java.util.Calendar;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.Comparator;
030: import java.util.Date;
031: import java.util.LinkedList;
032: import java.util.List;
033: import java.util.Vector;
034:
035: import net.sf.jmoney.JMoneyPlugin;
036:
037: /**
038: * The data model for an account.
039: */
040: public abstract class CapitalAccount extends Account {
041:
042: protected IListManager<CapitalAccount> subAccounts;
043:
044: protected String abbreviation = null;
045:
046: protected String comment = null;
047:
048: /**
049: * The full constructor for a CapitalAccount object. This constructor is called
050: * only be the datastore when loading data from the datastore. The properties
051: * passed to this constructor must be valid because datastores should only pass back
052: * values that were previously saved from a CapitalAccount object. So, for example,
053: * we can be sure that a non-null name and currency are passed to this constructor.
054: *
055: * @param name the name of the account
056: */
057: public CapitalAccount(IObjectKey objectKey, ListKey parent,
058: String name, IListManager<CapitalAccount> subAccounts,
059: String abbreviation, String comment, IValues extensionValues) {
060: super (objectKey, parent, name, extensionValues);
061:
062: this .subAccounts = subAccounts;
063: this .abbreviation = abbreviation;
064: this .comment = comment;
065: }
066:
067: /**
068: * The default constructor for a CapitalAccount object. This constructor is called
069: * when a new CapitalAccount object is created. The properties are set to default
070: * values. The list properties are set to empty lists. The parameter list for this
071: * constructor is the same as the full constructor except that there are no parameters
072: * for the scalar properties.
073: */
074: public CapitalAccount(IObjectKey objectKey, ListKey parent) {
075: super (objectKey, parent);
076:
077: this .name = JMoneyPlugin
078: .getResourceString("Account.newAccount");
079:
080: this .subAccounts = objectKey
081: .constructListManager(CapitalAccountInfo
082: .getSubAccountAccessor());
083: this .abbreviation = null;
084: this .comment = null;
085: }
086:
087: @Override
088: protected String getExtendablePropertySetId() {
089: return "net.sf.jmoney.capitalAccount";
090: }
091:
092: /**
093: * @return the abbrevation of this account.
094: */
095: public String getAbbreviation() {
096: return abbreviation;
097: }
098:
099: /**
100: * @return the comment of this account.
101: */
102: public String getComment() {
103: return comment;
104: }
105:
106: @Override
107: public ObjectCollection<CapitalAccount> getSubAccountCollection() {
108: return new ObjectCollection<CapitalAccount>(subAccounts, this ,
109: CapitalAccountInfo.getSubAccountAccessor());
110: }
111:
112: /**
113: * Get the entries in this account sorted according to the given
114: * sort specification. If the datastore plug-in has implemented
115: * the IEntryQueries interface then pass the request on to the
116: * datastore through the method of the same name in the IEntryQueries
117: * interface. If the IEntryQueries interface has not been implemented
118: * by the datastore then evaluate ourselves.
119: * <P>
120: * @return A collection containing the entries of this account.
121: * The entries are sorted using the given property and
122: * given sort order. The collection is a read-only
123: * collection.
124: */
125: public Collection<Entry> getSortedEntries(
126: final ScalarPropertyAccessor<?> sortProperty,
127: boolean descending) {
128: IEntryQueries queries = (IEntryQueries) getSession()
129: .getAdapter(IEntryQueries.class);
130: if (queries != null) {
131: return queries.getSortedEntries(this , sortProperty,
132: descending);
133: } else {
134: // IEntryQueries has not been implemented in the datastore.
135: // We must therefore provide our own implementation.
136:
137: List<Entry> sortedEntries = new LinkedList<Entry>(
138: getEntries());
139:
140: Comparator<Entry> entryComparator;
141: if (sortProperty.getPropertySet() == EntryInfo
142: .getPropertySet()) {
143: entryComparator = new Comparator<Entry>() {
144: public int compare(Entry entry1, Entry entry2) {
145: return sortProperty.getComparator().compare(
146: entry1, entry2);
147: }
148: };
149: } else if (sortProperty.getPropertySet() == TransactionInfo
150: .getPropertySet()) {
151: entryComparator = new Comparator<Entry>() {
152: public int compare(Entry entry1, Entry entry2) {
153: return sortProperty.getComparator().compare(
154: entry1.getTransaction(),
155: entry2.getTransaction());
156: }
157: };
158: } else if (sortProperty.getPropertySet() == AccountInfo
159: .getPropertySet()) {
160: entryComparator = new Comparator<Entry>() {
161: public int compare(Entry entry1, Entry entry2) {
162: return sortProperty.getComparator().compare(
163: entry1.getAccount(),
164: entry2.getAccount());
165: }
166: };
167: } else {
168: throw new RuntimeException(
169: "given property cannot be used for entry sorting");
170: }
171:
172: if (descending) {
173: final Comparator<Entry> ascendingComparator = entryComparator;
174: entryComparator = new Comparator<Entry>() {
175: public int compare(Entry entry1, Entry entry2) {
176: return ascendingComparator.compare(entry2,
177: entry1);
178: }
179: };
180: }
181:
182: Collections.sort(sortedEntries, entryComparator);
183:
184: return sortedEntries;
185: }
186: }
187:
188: /**
189: * @param anAbbrevation the abbrevation of this account.
190: */
191:
192: public void setAbbreviation(String anAbbreviation) {
193: String oldAbbreviation = this .abbreviation;
194: this .abbreviation = anAbbreviation;
195:
196: // Notify the change manager.
197: processPropertyChange(CapitalAccountInfo
198: .getAbbreviationAccessor(), oldAbbreviation,
199: anAbbreviation);
200: }
201:
202: /**
203: * @param aComment the comment of this account.
204: */
205:
206: public void setComment(String aComment) {
207: String oldComment = this .comment;
208: this .comment = aComment;
209:
210: // Notify the change manager.
211: processPropertyChange(CapitalAccountInfo.getCommentAccessor(),
212: oldComment, aComment);
213: }
214:
215: @Override
216: public String toString() {
217: return name;
218: }
219:
220: @Override
221: public String getFullAccountName() {
222: if (getParent() == null) {
223: return name;
224: } else {
225: return getParent().getFullAccountName() + "." + this .name;
226: }
227: }
228:
229: /**
230: * Create a sub-account of this account. This method is
231: * identical to calling
232: * <code>getSubAccountCollection().createNewElement(propertySet)</code>.
233: *
234: * @param propertySet a property set derived (directly or
235: * indirectly) from the CapitalAccount property set.
236: * This property set must not be derivable and is
237: * the property set for the type of capital account
238: * to be created.
239: */
240: public CapitalAccount createSubAccount(
241: ExtendablePropertySet<? extends CapitalAccount> propertySet) {
242: return getSubAccountCollection().createNewElement(propertySet);
243: }
244:
245: /**
246: * Delete a sub-account of this account. This method is
247: * identical to calling
248: * <code>getSubAccountCollection().remove(subAccount)</code>.
249: */
250: boolean deleteSubAccount(CapitalAccount subAccount) {
251: return getSubAccountCollection().remove(subAccount);
252: }
253:
254: /**
255: * @param date
256: * @param date2
257: * @param includeSubAccounts
258: * @return
259: */
260: public long[] getEntryTotalsByMonth(int startYear, int startMonth,
261: int numberOfMonths, boolean includeSubAccounts) {
262: IEntryQueries queries = (IEntryQueries) getSession()
263: .getAdapter(IEntryQueries.class);
264: if (queries != null) {
265: return queries.getEntryTotalsByMonth(this , startYear,
266: startMonth, numberOfMonths, includeSubAccounts);
267: } else {
268: // IEntryQueries has not been implemented in the datastore.
269: // We must therefore provide our own implementation.
270:
271: Vector<Entry> entriesList = new Vector<Entry>();
272: entriesList.addAll(getEntries());
273: if (includeSubAccounts) {
274: addEntriesFromSubAccounts(this , entriesList);
275: }
276:
277: Collections.sort(entriesList, new Comparator<Entry>() {
278: public int compare(Entry entry1, Entry entry2) {
279: return entry1.getTransaction().getDate().compareTo(
280: entry2.getTransaction().getDate());
281: }
282: });
283:
284: long[] totals = new long[numberOfMonths];
285:
286: Calendar calendar = Calendar.getInstance();
287:
288: // calculate the sum for each month
289: int year = startYear;
290: int month = startMonth;
291: for (int i = 0; i < numberOfMonths; i++) {
292: calendar.clear();
293: calendar.setLenient(false);
294: calendar.set(year, month - 1, 1, 0, 0, 0);
295: Date startOfMonth = calendar.getTime();
296: // Date startOfMonth = new Date(year - 1900, month, 1);
297:
298: month++;
299: if (month == 13) {
300: year++;
301: month = 1;
302: }
303:
304: calendar.clear();
305: calendar.setLenient(false);
306: calendar.set(year, month - 1, 1, 0, 0, 0);
307: Date endOfMonth = calendar.getTime();
308: // Date endOfMonth = new Date(year - 1900, month, 1);
309:
310: int total = 0;
311: for (Entry entry : entriesList) {
312: if (entry.getTransaction().getDate().compareTo(
313: startOfMonth) >= 0
314: && entry.getTransaction().getDate()
315: .compareTo(endOfMonth) < 0) {
316: total += entry.getAmount();
317: }
318: }
319: totals[i] = total;
320: }
321:
322: return totals;
323: }
324: }
325:
326: public void addEntriesFromSubAccounts(CapitalAccount a,
327: Collection<Entry> entriesList) {
328: for (CapitalAccount subAccount : a.getSubAccountCollection()) {
329: entriesList.addAll(subAccount.getEntries());
330: addEntriesFromSubAccounts(subAccount, entriesList);
331: }
332: }
333:
334: }
|