001: /*
002: *
003: * JMoney - A Personal Finance Manager
004: * Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
005: *
006: *
007: * This program is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU General Public License as published by
009: * the Free Software Foundation; either version 2 of the License, or
010: * (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
020: *
021: */
022:
023: package net.sf.jmoney.model2;
024:
025: import java.util.Collection;
026: import java.util.Iterator;
027:
028: import org.eclipse.core.runtime.Assert;
029:
030: /**
031: * This class is used to provide access to lists of objects
032: * contained in a list property of a parent object.
033: *
034: * @author Nigel Westbury
035: */
036: public class ObjectCollection<E extends ExtendableObject> implements
037: Collection<E> {
038:
039: private IListManager<E> listManager;
040: ListKey<E> listKey;
041:
042: public ObjectCollection(IListManager<E> listManager,
043: ExtendableObject parent,
044: ListPropertyAccessor<E> listPropertyAccessor) {
045: this .listManager = listManager;
046: this .listKey = new ListKey<E>(parent.getObjectKey(),
047: listPropertyAccessor);
048: }
049:
050: /**
051: * This version of this method is called only by the end-user code, i.e. this method
052: * is not called when a transaction manager is committing its changes to the underlying
053: * data manager.
054: *
055: * @param <F> the class of the object being created in this list
056: * @param actualPropertySet
057: * @return
058: */
059: public <F extends E> F createNewElement(
060: ExtendablePropertySet<F> actualPropertySet) {
061: final F newObject = listManager
062: .createNewElement(actualPropertySet);
063:
064: listKey.getParentKey().getSession().getChangeManager()
065: .processObjectCreation(listKey, newObject);
066:
067: // Fire the event.
068: listKey.getParentKey().getDataManager().fireEvent(
069: new ISessionChangeFirer() {
070: public void fire(SessionChangeListener listener) {
071: listener.objectInserted(newObject);
072: listener.objectCreated(newObject);
073: }
074: });
075:
076: return newObject;
077: }
078:
079: /**
080: * This version of this method is called only from within a transaction. The values of
081: * the scalar properties are passed so that:
082: *
083: * - the underlying database need only do a single insert, instead of inserting with
084: * default values and then updating each value as they are set.
085: *
086: * - a single notification is fired, passing the object with its final property values,
087: * rather than sending out an object with default values and then a property change
088: * notification for each property.
089: *
090: * This may be a top level insert or a descendant of an object that was inserted in
091: * the same transaction. We must know the difference so we can fire the objectInserted
092: * event methods correctly. We therefore need a flag to indicate this.
093: *
094: * @param isDescendentInsert true if this object is being inserted because its parent is
095: * being inserted in the same transaction, false if this object is being inserted
096: * into a list that existed prior to this transaction
097: */
098: public <F extends E> F createNewElement(
099: ExtendablePropertySet<F> actualPropertySet, IValues values,
100: final boolean isDescendentInsert) {
101: final F newObject = listManager.createNewElement(
102: actualPropertySet, values);
103:
104: listKey.getParentKey().getSession().getChangeManager()
105: .processObjectCreation(listKey, newObject);
106:
107: return newObject;
108: }
109:
110: /**
111: * Moves the given object into this collection, removing it from its
112: * current parent.
113: */
114: public void moveElement(final E extendableObject) {
115: Assert
116: .isTrue(listKey.getParentKey().getDataManager() == extendableObject
117: .getDataManager());
118:
119: final ListKey originalListKey = extendableObject
120: .getParentListKey();
121:
122: /*
123: * Note that if the parent object is not materialized (meaning that the
124: * getObject method in the following line needs to materialize the
125: * parent) then we really don't need to do anything to remove the object
126: * from the parent's list. However, there is no API for this, and the
127: * extra code for such a small optimization is not worth it.
128: */
129: ObjectCollection originalCollection = originalListKey
130: .getParentKey().getObject().getListPropertyValue(
131: originalListKey.getListPropertyAccessor());
132: IListManager originalListManager = originalCollection.listManager;
133:
134: // Move in the underlying datastore.
135: listManager.moveElement(extendableObject, originalListManager);
136:
137: listManager.add(extendableObject);
138: originalListManager.remove(extendableObject);
139: extendableObject.parentKey = listKey;
140:
141: listKey.getParentKey().getDataManager().fireEvent(
142: new ISessionChangeFirer() {
143: public void fire(SessionChangeListener listener) {
144: listener.objectMoved(extendableObject,
145: originalListKey.getParentKey()
146: .getObject(), listKey
147: .getParentKey().getObject(),
148: originalListKey
149: .getListPropertyAccessor(),
150: listKey.getListPropertyAccessor());
151: }
152: });
153:
154: listKey.getParentKey().getSession().getChangeManager()
155: .processObjectMove(extendableObject, originalListKey);
156: }
157:
158: public int size() {
159: return listManager.size();
160: }
161:
162: public boolean isEmpty() {
163: return listManager.isEmpty();
164: }
165:
166: public boolean contains(Object arg0) {
167: return listManager.contains(arg0);
168: }
169:
170: public Iterator<E> iterator() {
171: return listManager.iterator();
172: }
173:
174: public Object[] toArray() {
175: return listManager.toArray();
176: }
177:
178: public <T> T[] toArray(T[] arg0) {
179: return listManager.toArray(arg0);
180: }
181:
182: public boolean add(E arg0) {
183: /*
184: * The Collection methods that mutate the collection should not be
185: * used. Use instead createNewElement, deleteElement, and moveElement.
186: */
187: throw new UnsupportedOperationException();
188: }
189:
190: /**
191: * Removes an object from the collection. This method ensures that listeners
192: * are notified as appropriate.
193: *
194: * @return true if the object existed in this collection,
195: * false if the object was not in the collection
196: */
197: // TODO: rename to DeleteElement.
198: public boolean remove(Object object) {
199: if (!(object instanceof ExtendableObject)) {
200: return false;
201: }
202:
203: ExtendableObject extendableObject = (ExtendableObject) object;
204:
205: if (extendableObject.getDataManager() != listKey.getParentKey()
206: .getDataManager()) {
207: throw new RuntimeException(
208: "Invalid call to remove. The object passed does not belong to the data manager that is the base data manager of this collection.");
209: }
210:
211: /*
212: * Check that the object is in the list. It is in this list if the parent
213: * object is the same and the list property is the same.
214: */
215: if (extendableObject.parentKey.equals(listKey)) {
216: final E objectToRemove = listKey.getListPropertyAccessor()
217: .getElementPropertySet().getImplementationClass()
218: .cast(extendableObject);
219:
220: /*
221: * Deletion events are fired before the object is removed from the
222: * datastore. This is necessary because listeners processing the
223: * object deletion may need to fetch information about the object
224: * from the datastore.
225: */
226: listKey.getParentKey().getDataManager().fireEvent(
227: new ISessionChangeFirer() {
228: public void fire(SessionChangeListener listener) {
229: listener.objectRemoved(objectToRemove);
230: }
231: });
232:
233: // Notify the change manager.
234: listKey.getParentKey().getSession().getChangeManager()
235: .processObjectDeletion(
236: listKey.getParentKey().getObject(),
237: listKey.getListPropertyAccessor(),
238: objectToRemove);
239:
240: boolean found = listManager.deleteElement(objectToRemove);
241: Assert.isTrue(found);
242:
243: return true;
244: }
245:
246: // Object was not in the collection
247: return false;
248: }
249:
250: public boolean containsAll(Collection<?> arg0) {
251: return listManager.containsAll(arg0);
252: }
253:
254: public boolean addAll(Collection<? extends E> arg0) {
255: /*
256: * The Collection methods that mutate the collection should not be
257: * used. Use instead createNewElement, deleteElement, and moveElement.
258: */
259: throw new UnsupportedOperationException();
260: }
261:
262: public boolean removeAll(Collection<?> arg0) {
263: /*
264: * The Collection methods that mutate the collection should not be
265: * used. Use instead createNewElement, deleteElement, and moveElement.
266: */
267: throw new UnsupportedOperationException();
268: }
269:
270: public boolean retainAll(Collection<?> arg0) {
271: /*
272: * The Collection methods that mutate the collection should not be
273: * used. Use instead createNewElement, deleteElement, and moveElement.
274: */
275: throw new UnsupportedOperationException();
276: }
277:
278: public void clear() {
279: /*
280: * The Collection methods that mutate the collection should not be
281: * used. Use instead createNewElement, deleteElement, and moveElement.
282: */
283: throw new UnsupportedOperationException();
284: }
285: }
|