From: <wes...@us...> - 2013-02-01 09:17:10
|
Revision: 1160 http://jmoney.svn.sourceforge.net/jmoney/?rev=1160&view=rev Author: westbury Date: 2013-02-01 09:17:02 +0000 (Fri, 01 Feb 2013) Log Message: ----------- remove use of the term 'extendable object' as that is nothing to do with the 'isolation' package. Also a little cleanup to the undo/redo context. Modified Paths: -------------- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/AbstractDataManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ChangeManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DeltaListManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IDataManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IListManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IObjectKey.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ModifiedObject.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ObjectCollection.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/SessionChangeListener.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/TransactionManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedListManager.java trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedObjectKey.java Removed Paths: ------------- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DataManager.java Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/AbstractDataManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/AbstractDataManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/AbstractDataManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -43,8 +43,6 @@ private ChangeManager changeManager = new ChangeManager(); - private IUndoContext undoContext = new UndoContext(); - private Vector<WeakReference<SessionChangeListener>> sessionChangeListenerRefs = new Vector<WeakReference<SessionChangeListener>>(); private Vector<SessionChangeListener> sessionChangeListeners = new Vector<SessionChangeListener>(); @@ -254,15 +252,19 @@ /** * JMoney supports undo/redo using a context that is based on the data * manager. - * + * <P> * The underlying data manager (supported by the underlying datastore) and * the transaction manager session objects each have a different context. - * + * <P> * Changes within a transaction manager have a separate context. This is * necessary because if a view is making changes to an through a transaction * manager to an uncommitted version of the datastore then the undo/redo * menu needs to show those changes for the view. - * + * <P> + * By default this implementation returns the platform undo/redo + * context. The TransactionManager implementation overrides this + * method to provide a more specific context. + * * @return the undo/redo context to be used for changes made to this session */ @Override @@ -271,10 +273,6 @@ // This may need some tidying up, but by using a common context, // this allows undo/redo to work even across a closing or // opening of a session. There may be a better way of doing this. - if (this instanceof TransactionManager) { - return undoContext; - } else { - return PlatformUI.getWorkbench().getOperationSupport().getUndoContext(); - } + return PlatformUI.getWorkbench().getOperationSupport().getUndoContext(); } } Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ChangeManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ChangeManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ChangeManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -116,7 +116,7 @@ /** * A ChangeEntry object for an update to a scalar property (excluding - * scalar properties that are references to extendable objects). + * scalar properties that are references to model objects). * * @param <V> */ @@ -141,7 +141,7 @@ /** * A ChangeEntry object for an update to a scalar property that is a - * reference to an extendable object. + * reference to an model object. * * @param <E> */ @@ -248,7 +248,7 @@ Object value = propertyAccessor.getValue(oldObject); if (value instanceof IModelObject) { /* - * We can't store extendable objects or even the object keys + * We can't store model objects or even the object keys * because those may not remain valid (the referenced object may * be deleted). We store instead a KeyProxy. If the referenced * object is later deleted, then un-deleted using an undo @@ -374,7 +374,7 @@ IScalarPropertyAccessor<V,?> propertyAccessor, V oldValue, V newValue) { /* - * If the property value is an extendable object then we need special processing to + * If the property value is a model object then we need special processing to * ensure everything works correctly. The keys are replaced by proxy keys so all * will work correctly. * Deleted: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DataManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DataManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DataManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -1,482 +0,0 @@ -/* - * - * JMoney - A Personal Finance Manager - * Copyright (c) 2004 Nigel Westbury <wes...@us...> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -package net.sf.jmoney.isolation; - -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.Vector; - -import net.sf.jmoney.model2.Account; -import net.sf.jmoney.model2.Entry; -import net.sf.jmoney.model2.Session; - -import org.eclipse.core.commands.operations.IUndoContext; -import org.eclipse.core.commands.operations.UndoContext; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.widgets.Control; -import org.eclipse.ui.PlatformUI; - -/** - * An object that manages a view on the data. - * <P> - * This is a base object that is extended by DatastoreManager to manage a view - * of data committed to a datastore and is also extended by TransactionManager - * to manage a view of uncommitted data. - */ -public abstract class DataManager<K extends IObjectKey> { - - private ChangeManager changeManager = new ChangeManager(); - - private IUndoContext undoContext = new UndoContext(); - - private Vector<WeakReference<SessionChangeListener>> sessionChangeListenerRefs = new Vector<WeakReference<SessionChangeListener>>(); - - private Vector<SessionChangeListener> sessionChangeListeners = new Vector<SessionChangeListener>(); - - private Vector<SessionChangeFirerListener> sessionChangeFirerListeners = new Vector<SessionChangeFirerListener>(); - - private boolean sessionFiring = false; - -// private ReferenceQueue referenceQueue = new ReferenceQueue(); - - public void addChangeListener(SessionChangeListener listener) { - sessionChangeListeners.add(listener); - } - - public void removeChangeListener(SessionChangeListener listener) { - sessionChangeListeners.remove(listener); - } - - /** - * Adds a change listener. - * <P> - * Adds the listener to the collection of listeners who will be notified - * when a change is made to the version of the datastore as seen through - * this data manager. Notifications will be sent when either a - * change is committed to this data view by a transaction manager - * or an uncommitted change is made through this data manager. - * <P> - * When listening for changes to a datastore, there are two options. - * If the listener is interested only in receiving committed changes - * then the listener should listen to the Session object or the JMoneyPlugin - * object. However, if a listener wants to be notified of changes - * made through a transaction manager, even though those changes are - * not committed to the datastore, then the listener should add the - * listener to the transaction manager using this method. - * <P> - * The listener will not recieve any notification at the time a transaction - * is committed as the listener will already have been notified of the - * changes. Note that there is no support for rollbacks as the transaction - * manager can be just dropped (and garbage collected) without ever having been - * committed, so getting a notification for a change that is never committed is - * not an issue. Views should do a full refresh if they change the data manager - * through which they are obtaining the data to be shown. - * <P> - * This method maintains only a weak reference to the listener. Therefore - * the caller MUST maintain a reference to the listener. If the caller does - * not maintain a reference to the listener then the listener will be garbage - * collected and the caller may wonder why no events are being notified. - */ - public void addChangeListenerWeakly(SessionChangeListener listener) { - sessionChangeListenerRefs.add(new WeakReference<SessionChangeListener>(listener)); - } - - /** - * Adds a change listener. - * <P> - * The listener is active only for as long as the given control exists. When the - * given control is disposed, the listener is removed and will receive no more - * notifications. - * <P> - * This method is generally used when a listener is used to update contents in a - * control. Typically multiple controls are updated by a listener and the parent - * composite control is passed to this method. - * <P> - * This method creates a strong reference to the listener. There is thus no need - * for the caller to maintain a reference to the listener. - * - * @param listener - * @param control - */ - public void addChangeListener(final SessionChangeListener listener, Control control) { - sessionChangeListeners.add(listener); - - // Remove the listener when the given control is disposed. - control.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - sessionChangeListeners.remove(listener); - } - }); - } - - public void addSessionChangeFirerListener(SessionChangeFirerListener listener) { - sessionChangeFirerListeners.add(listener); - } - - public void removeSessionChangeFirerListener(SessionChangeFirerListener listener) { - sessionChangeFirerListeners.remove(listener); - } - - /** - * Send change notifications to all listeners who are listening for changes - * to the version of the datastore as seen through this data manager. - * <P> - * In practice it is likely that the only listener will be the JMoneyPlugin - * object. Views should all listen to the JMoneyPlugin class for changes to - * the model. The JMoneyPlugin object will pass on events from this session - * object. - * <P> - * Listeners may register directly with a session object. However if they do - * so then they must re-register whenever the session object changes. If a - * viewer wants to listen for changes to a session even if that session is - * not the session currently shown in the workbench then it should register - * with the session object, but if the viewer wants to be told about changes - * to the current workbench window then it should register with the - * JMoneyPlugin object. - * - * This method is public because the layer above this data manager is - * responsible for calling this method. Only the above layer (code outside - * the data manager) knows, for example, when objectInserted should be - * called. - */ - public void fireEvent(ISessionChangeFirer firer) { - sessionFiring = true; - - /* - * Notify listeners who are listening to us using the - * SessionChangeFirerListener interface. - */ - if (!sessionChangeFirerListeners.isEmpty()) { - /* - * Take a copy of the listener list. By doing this we allow - * listeners to safely add or remove listeners. - */ - SessionChangeFirerListener listenerArray[] = new SessionChangeFirerListener[sessionChangeFirerListeners.size()]; - sessionChangeFirerListeners.copyInto(listenerArray); - for (int i = 0; i < listenerArray.length; i++) { - listenerArray[i].sessionChanged(firer); - } - } - - /* - * Notify listeners who are listening to us using the - * SessionChangeListener interface. - * - * Take a copy of the listener list. By doing this we allow listeners to - * safely add or remove listeners. Note that listeners from both the - * weakly referenced list and from the normal list must be processed. - */ - Vector<SessionChangeListener> listenerArray = new Vector<SessionChangeListener>(); - - for (WeakReference<SessionChangeListener> listenerRef: sessionChangeListenerRefs) { - SessionChangeListener listener = listenerRef.get(); - if (listener != null) { - listenerArray.add(listener); - } - } - - for (SessionChangeListener listener: sessionChangeListeners) { - listenerArray.add(listener); - } - - for (SessionChangeListener listener: listenerArray) { - firer.fire(listener); - } - - sessionFiring = false; - } - - /** - * This method is used by plug-ins so that they know if - * code is being called from within change notification. - * - * It is important for plug-ins to know this. Plug-ins - * MUST NOT change the session data while a listener is - * being notified of a change to the datastore. - * This can happen very indirectly. For example, suppose - * an account is deleted. The navigation view's listener - * is notified and so removes the account's node from the - * navigation tree. If an account properties panel is - * open, the panel is destroyed. Because the panel is - * being destroyed, the control that had the focus is sent - * a 'focus lost' notification. The 'focus lost' notification - * takes the edited data from the control and writes it to - * the datastore. - * <P> - * Writing data to the datastore during session change notifications - * can cause serious problems. The data may conflict. The - * undo/redo operations are almost impossible to manage. - * In the above scenario with the deleted account, an attempt - * is made to update a property for an object that has been - * deleted. The problems are endless. - * <P> - * It would be good if the datastore simply ignored such changes. - * This would provide more robust support for plug-ins, and plug-ins - * would not have to test this flag. However, for the time being, - * plug-ins must test this flag and avoid making changes when this - * flag is set. Plug-ins only need to do this in focus lost events - * as that is the only time I can think of where this problem may - * occur. - * - * @return true if the session is notifying listeners of - * a change to the session data, otherwise false - */ - // TODO: Revisit this, especially the last paragraph above. - public boolean isSessionFiring() { - return sessionFiring; - } - - /** - * This method is called when a transaction is about to start. - * <P> - * If the datastore is kept in a transactional database then the code - * needed to start a transaction should be put in the implementation - * of this method. - * <P> - * The framework will always call this method, then make changes to - * the datastore, then call <code>commitTransaction</code> within - * a single function call. The framework also ensures that no events - * are fired between the call to <code>startTransaction</code> and - * the call to <code>commitTransaction</code>. The implementation of - * this method thus has no need to support or guard against nested - * transactions. - * - * @see commitTransaction - */ - public abstract void startTransaction(); - - /** - * This method is called when a transaction is to be committed. - * <P> - * If the datastore is kept in a transactional database then the code - * needed to commit the transaction should be put in the implementation - * of this method. - * - * @see startTransaction - */ - public abstract void commitTransaction(); - - public ChangeManager getChangeManager() { - return changeManager; - } - - /** - * JMoney supports undo/redo using a context that is based on the data - * manager. - * - * The underlying data manager (supported by the underlying datastore) and - * the transaction manager session objects each have a different context. - * - * Changes within a transaction manager have a separate context. This is - * necessary because if a view is making changes to an through a transaction - * manager to an uncommitted version of the datastore then the undo/redo - * menu needs to show those changes for the view. - * - * @return the undo/redo context to be used for changes made to this session - */ - public IUndoContext getUndoContext() { - // If not in a transaction, use the workbench context. - // This may need some tidying up, but by using a common context, - // this allows undo/redo to work even across a closing or - // opening of a session. There may be a better way of doing this. - if (this instanceof TransactionManager) { - return undoContext; - } else { - return PlatformUI.getWorkbench().getOperationSupport().getUndoContext(); - } - } - - /** - * Returns the session object. Objects often need access to the session - * object. For example, the CapitalAccount constructor sets the currency to - * the default currency for the session if a null currency is passed. - * <P> - * WARNING: Be very careful about the use of this method. The session will - * not be set until it is constructed. Therefore any code that may be called - * during the initial construction of a session may not call this method. - * This includes extendable object constructors that are being called to - * construct an object store from a datastore. It does not include the - * constructor when adding a new object. As these are the same constructors, - * this is very confusing. - * - * @return the session object - */ - public abstract Session getSession(); - - /** - * @param account - * @return - */ - public abstract boolean hasEntries(Account account); - - /** - * @param account - * @return - */ - public abstract Collection<Entry> getEntries(Account account); -} - -/* -//@author Santhosh Kumar T - san...@in... -public class WeakPropertyChangeListener implements PropertyChangeListener{ - WeakReference listenerRef; - Object src; - - public WeakPropertyChangeListener(PropertyChangeListener listener, Object src){ - listenerRef = new WeakReference(listener); - this.src = src; - } - - public void propertyChange(PropertyChangeEvent evt){ - PropertyChangeListener listener = (PropertyChangeListener)listenerRef.get(); - if(listener==null){ - removeListener(); - }else - listener.propertyChange(evt); - } - - private void removeListener(){ - try{ - Method method = src.getClass().getMethod("removePropertyChangeListener" - , new Class[] {PropertyChangeListener.class}); - method.invoke(src, new Object[]{ this }); - } catch(Exception e){ - e.printStackTrace(); - } - } -} - - -How to use this: - -KeyboardFocusManager focusManager = - KeyboardFocusManager.getCurrentKeyboardFocusManager(); - -// instead of registering direclty use weak listener -// focusManager.addPropertyChangeListener(focusOwnerListener); - -focusManager.addPropertyChangeListener( - new WeakPropertyChangeListener(focusOwnerListener, focusManager)); - - -How does this work: - -Instead of registering propertyChangeListener directly to keyboardFocusManager, we wrap it inside WeakPropertyChangeListener and register this weak listener to keyboardFocusManager. This weak listener acts a delegate. -It receives the propertyChangeEvents from keyboardFocusManager and delegates it the wrapped listener. - -The interesting part of this weak listener, it hold a weakReference to the original propertyChangeListener. so this delegate is eligible for garbage collection which it is no longer reachable via references. When it gets garbage -collection, the weakReference will be pointing to null. On next propertyChangeEvent notification from keyboardFocusManager, it find that the weakReference is pointing to null, and unregisters itself from -keyboardFocusManager. Thus the weak listener will also become eligible for garbage collection in next gc cycle. - -This concept is not something new. If you have a habit of looking into swing sources, you will find that AbstractButton actually adds a weak listener to its action. The weak listener class used for this is : -javax.swing.AbstractActionPropertyChangeListener; This class is package-private, so you don't find it in javadoc. - -The full-fledged, generic implementation of weak listeners is available in Netbeans OpenAPI: WeakListeners.java . It is worth to have a look at it. - -We create a subclass of ReferenceQueue, which will help us in performing such resource cleanup very easily: - -// @author Santhosh Kumar T - san...@in... - public class ActiveReferenceQueue - extends ReferenceQueue implements Runnable{ - private static ActiveReferenceQueue singleton = null; - - public static ActiveReferenceQueue getInstance(){ - if(singleton==null) - singleton = new ActiveReferenceQueue(); - return singleton; - } - - private ActiveReferenceQueue(){ - Thread t = new Thread(this, "ActiveReferenceQueue"); - t.setDaemon(false); - t.start(); - } - - public void run(){ - for(;;){ - try{ - Reference ref = super.remove(0); - if(ref instanceof Runnable){ - try{ - ((Runnable)ref).run(); - } catch(Exception e){ - e.printStackTrace(); - } - } - } catch(InterruptedException e){ - e.printStackTrace(); - } - } - } - } - - - ActiveReferenceQueue is a singleton class, and starts a thread when it is instantiated. This thread keeps polling for weakReferences that are eligible for garbage collection, and if those weakReferenes implement Runnable interface, it executes them. - - To use this, we can create a subclass of WeakReference implementing Runnable interface, which does the cleanup it run() method. - - Let us see how we will change the WeakPropertyChangeListener to use the above reference queue: - -// @author Santhosh Kumar T - san...@in... - public class WeakPropertyChangeListener implements PropertyChangeListener{ - WeakReference listenerRef; - Object src; - - public WeakPropertyChangeListener(PropertyChangeListener listener, Object src){ - listenerRef = new ListenerReference(listener, this); - this.src = src; - } - - public void propertyChange(PropertyChangeEvent evt){ - PropertyChangeListener listener = (PropertyChangeListener)listenerRef.get(); - if(listener!=null) - listener.propertyChange(evt); - } - - private void removeListener(){ - try{ - Method method = src.getClass().getMethod("removePropertyChangeListener" - , new Class[] {PropertyChangeListener.class}); - method.invoke(src, new Object[]{ this }); - } catch(Exception e){ - e.printStackTrace(); - } - } - - static class ListenerReference extends WeakReference{ - WeakPropertyChangeListener listener; - - public ListenerReference(Object ref, WeakPropertyChangeListener listener){ - super(ref, ActiveReferenceQueue.getInstance()); - this.listener = listener; - } - - public void run(){ - listener.removeListener(); - listener = null; - } - } - } -*/ \ No newline at end of file Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DeltaListManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DeltaListManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/DeltaListManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -41,7 +41,7 @@ * This class keeps lists of inserted and deleted objects. It is ok for this * object to maintain these lists because all data managers must guarantee that * only a single instance of the same object is returned, and as - * DeltaListManager objects are held by extendable objects, we can be sure that + * DeltaListManager objects are held by model objects, we can be sure that * only a single instance of this object will exist for a given list. * * @author Nigel Westbury @@ -94,7 +94,7 @@ } /** - * Create a new extendable object in the list represented by this object. + * Create a new model object in the list represented by this object. * <P> * This method does not create the object in the underlying committed list, * because if it did that then other views would see the object before it is @@ -106,11 +106,11 @@ @Override public <F extends E> F createNewElement(IExtendablePropertySet<F> propertySet) { UncommittedObjectKey objectKey = new UncommittedObjectKey(transactionManager); - F extendableObject = propertySet.constructDefaultImplementationObject(objectKey, new ListKey<E,S>(uncommittedParentKey, listAccessor)); + F element = propertySet.constructDefaultImplementationObject(objectKey, new ListKey<E,S>(uncommittedParentKey, listAccessor)); - objectKey.setObject(extendableObject); + objectKey.setObject(element); - addedObjects.add(extendableObject); + addedObjects.add(element); /* * Ensure this list is in the list of lists that have been @@ -118,11 +118,11 @@ */ transactionManager.modifiedLists.add(this); - return extendableObject; + return element; } /** - * Create a new extendable object in the list represented by this object. + * Create a new model object in the list represented by this object. * This version of this method takes an array of values of the properties in * the object. * <P> @@ -143,11 +143,11 @@ UncommittedObjectKey objectKey = new UncommittedObjectKey(transactionManager); // We can now create the object. - F extendableObject = propertySet.constructImplementationObject(objectKey, new ListKey<E,S>(uncommittedParentKey, listAccessor), values); + F newObject = propertySet.constructImplementationObject(objectKey, new ListKey<E,S>(uncommittedParentKey, listAccessor), values); - objectKey.setObject(extendableObject); + objectKey.setObject(newObject); - addedObjects.add(extendableObject); + addedObjects.add(newObject); /* * Ensure this list is in the list of lists that have been @@ -155,7 +155,7 @@ */ transactionManager.modifiedLists.add(this); - return extendableObject; + return newObject; } /** @@ -163,12 +163,12 @@ * when an attempt is made to commit the transaction. */ @Override - public void deleteElement(E extendableObject) throws ReferenceViolationException { + public void deleteElement(E element) throws ReferenceViolationException { boolean isRemoved; - UncommittedObjectKey uncommittedKey = (UncommittedObjectKey)extendableObject.getObjectKey(); + UncommittedObjectKey uncommittedKey = (UncommittedObjectKey)element.getObjectKey(); if (uncommittedKey.isNewObject()) { - isRemoved = addedObjects.remove(extendableObject); + isRemoved = addedObjects.remove(element); } else { if (deletedObjects.contains(uncommittedKey.getCommittedObjectKey())) { isRemoved = false; @@ -201,7 +201,7 @@ */ // TODO: This code may not be necessary. It is probably better to flag the object itself // when an object is deleted. - IObjectKey committedObjectKey = ((UncommittedObjectKey)extendableObject.getObjectKey()).getCommittedObjectKey(); + IObjectKey committedObjectKey = ((UncommittedObjectKey)element.getObjectKey()).getCommittedObjectKey(); if (committedObjectKey != null) { ModifiedObject modifiedObject = transactionManager.modifiedObjects.get(committedObjectKey); if (modifiedObject == null) { @@ -213,7 +213,7 @@ } @Override - public <F extends E> void moveElement(F extendableObject, IListManager<? super F> originalList) { + public <F extends E> void moveElement(F element, IListManager<? super F> originalList) { /* * It is fairly complex to implement this inside a transaction. * Therefore we do not support this. Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IDataManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IDataManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IDataManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -3,6 +3,17 @@ import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.swt.widgets.Control; +/** + * Interface to be implemented by all data managers. + * <P> + * A 'data manager' manages a version of the model. There will + * be a data manager that manages the persisted version of the model. + * If changes are being to the model inside a transaction then that + * 'copy' of the model will also have a data manager. + * + * @author Nigel Westbury + * + */ public interface IDataManager { void addChangeListener(SessionChangeListener listener); Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IListManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IListManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IListManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -70,18 +70,18 @@ * to/from a file then the implementation must check for references before * attempting the delete. * - * @param extendableObject + * @param element * @return true if the element was deleted, false if it could not be deleted * because there were references to it */ - void deleteElement(E extendableObject) throws ReferenceViolationException; + void deleteElement(E element) throws ReferenceViolationException; /** * Moves the given object into this list, removing it from its * original list. * - * @param extendableObject + * @param element * @param originalList */ - <F extends E> void moveElement(F extendableObject, IListManager<? super F> originalList); + <F extends E> void moveElement(F element, IListManager<? super F> originalList); } Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IObjectKey.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IObjectKey.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/IObjectKey.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -26,7 +26,7 @@ /** * Interface into a key object that holds the data required to - * obtain an extendable data object. + * obtain an object in the model. * <P> * JMoney data storage is implemented in plug-ins. This allows * a choice of methods of data storage. Some data storage implementations @@ -34,16 +34,13 @@ * An example of this type of datastore plug-in is the XML serialized data * storage plug-in. Other plug-ins, however, may only load data on demand. * An example is a plug-in that stores the data in a JDBC database. - * The JDBC database plug-in, for example, constructs an <code>Entry</code> object - * only when the user requests an account entry list view of the account in - * which the <code>Entry</code> occurs. * <P> * In order to support this, the data model implementation classes do not - * directly contain references to other classes in the data model. + * directly contain references to other objects in the data model. * Instead, they contain a reference to an object that * implements the <code>IObjectKey</code> interface. The * object key implements the <code>getObject</code> - * method which returns a reference to the actual extendable + * method which returns a reference to the actual model * object. This method should be called when and only when * a reference to the property object is required. The <code>getObject</code> method * should be called by the getter for the property. @@ -64,7 +61,7 @@ */ public interface IObjectKey { /** - * Returns a reference to the actual extendable + * Returns a reference to the actual model * object. This method should be called when and only when * a reference to the property object is required. The <code>getObject</code> method * should be called by the getter for the property. @@ -72,21 +69,24 @@ * Implementations of this method may construct the object * every time the method is called. Construction of the object * may require reading data from a database. Users of this method - * must therefore be aware that + * must therefore be aware that: * <LI> * <UL>This method may not be efficient. Users of this method should * therefore cache values returned by this method. * </UL> * <UL>This method may return an object with a different java identity - * each time it is called. However, every call of this method on + * each time it is called. However a weak reference map of all objects is maintained and + * this ensures that an object with a different Java identity can be returned only if there are no + * references anywhere in the JVM to the original instance of the object. So this should not be an issue. + * Every call of this method on * a given IObjectKey object will return objects that all are - * equal when using the <code>equals</code> method. + * equal when using the <code>equals</code> method and all have the same hashcode. * </UL> * <UL>This method is used when getting property values using the getter methods. * users of getter methods that return references to objects in the data model * must therefore also be aware of the above two points. * </UL> - * @return a reference to the actual extendable object + * @return a reference to the actual model object */ IModelObject getObject(); @@ -119,7 +119,8 @@ * Constructs a list manager that is suitable for managing a list * property in the object represented by this object key. * - * @param <E> + * @param <E> type of elements in the list + * @param <S> type of the model object that defines this list * @param listAccessor a list property, which must be a property of * the object represented by this object key * @return Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ModifiedObject.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ModifiedObject.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ModifiedObject.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -27,7 +27,7 @@ /** * An instance of this class contains the changes that have been made to an - * extendable object. These changes consist of either a map containing an entry + * model object. These changes consist of either a map containing an entry * for each property whose value has changed, or an indication that the object * has been deleted. * <P> Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ObjectCollection.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ObjectCollection.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/ObjectCollection.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -101,17 +101,17 @@ * Moves the given object into this collection, removing it from its * current parent. */ - public <F extends E> void moveElement(final F extendableObject) { - Assert.isTrue(listKey.getParentKey().getDataManager() == extendableObject.getDataManager()); + public <F extends E> void moveElement(final F element) { + Assert.isTrue(listKey.getParentKey().getDataManager() == element.getDataManager()); - ListKey<? super F,?> originalListKey = extendableObject.getParentListKey(); + ListKey<? super F,?> originalListKey = element.getParentListKey(); - moveIt(extendableObject, originalListKey); + moveIt(element, originalListKey); - listKey.getParentKey().getDataManager().getChangeManager().processObjectMove(extendableObject, originalListKey); + listKey.getParentKey().getDataManager().getChangeManager().processObjectMove(element, originalListKey); } - private <F extends E, S extends IModelObject> void moveIt(final F extendableObject, + private <F extends E, S extends IModelObject> void moveIt(final F element, final ListKey<? super F, S> originalListKey) { /* * Note that if the parent object is not materialized (meaning that the @@ -124,18 +124,18 @@ IListManager<? super F> originalListManager = originalCollection.listManager; // Move in the underlying datastore. - listManager.moveElement(extendableObject, originalListManager); + listManager.moveElement(element, originalListManager); - listManager.add(extendableObject); - originalListManager.remove(extendableObject); - extendableObject.replaceParentListKey(listKey); + listManager.add(element); + originalListManager.remove(element); + element.replaceParentListKey(listKey); listKey.getParentKey().getDataManager().fireEvent( new ISessionChangeFirer() { @Override public void fire(SessionChangeListener listener) { listener.objectMoved( - extendableObject, + element, originalListKey.getParentKey().getObject(), listKey.getParentKey().getObject(), originalListKey.getListPropertyAccessor(), @@ -206,8 +206,8 @@ * @throws RuntimeException * if the object does not exist in the collection */ - public void deleteElement(E extendableObject) throws ReferenceViolationException { - if (extendableObject.getDataManager() != listKey.getParentKey().getDataManager()) { + public void deleteElement(E element) throws ReferenceViolationException { + if (element.getDataManager() != listKey.getParentKey().getDataManager()) { throw new RuntimeException("Invalid call to remove. The object passed does not belong to the data manager that is the base data manager of this collection."); //$NON-NLS-1$ } @@ -215,11 +215,11 @@ * Check that the object is in the list. It is in this list if the parent * object is the same and the list property is the same. */ - if (!extendableObject.getParentListKey().equals(listKey)) { + if (!element.getParentListKey().equals(listKey)) { throw new RuntimeException("Passed object is not in the list."); } - final E objectToRemove = listKey.getListPropertyAccessor().getElementPropertySet().getImplementationClass().cast(extendableObject); + final E objectToRemove = listKey.getListPropertyAccessor().getElementPropertySet().getImplementationClass().cast(element); /* * Deletion events are fired before the object is removed from the Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/SessionChangeListener.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/SessionChangeListener.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/SessionChangeListener.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -32,7 +32,7 @@ */ public interface SessionChangeListener extends EventListener { /** - * An extendable object has been added to the datastore. + * An object has been added to the datastore. * <P> * If an object with child objects is added to the datastore as a single * transaction then this method is called only for the object itself and not @@ -64,7 +64,7 @@ void objectInserted(IModelObject newObject); /** - * An extendable object has been added to the datastore. + * An object has been added to the datastore. * <P> * If an object with child objects is added to the datastore then this * method is called for the inserted object and all its descendent objects. @@ -75,12 +75,12 @@ * Listeners should put code in this method if it needs to process new objects in * the same way regardless of how the object was added. * - * @param extendableObject + * @param newObject */ void objectCreated(IModelObject newObject); /** - * An extendable object has been deleted. + * An object has been deleted. * * If an object with child objects is deleted from the datastore as a single * transaction then this method is called only for the object itself and not @@ -93,12 +93,12 @@ * Listeners should put code in this method to avoid complications that can * arise if objects and there children are removed piecemeal. * - * @param extendableObject + * @param deletedObject */ void objectRemoved(IModelObject deletedObject); /** - * An extendable object has been deleted. + * An object has been deleted. * * If an object with child objects is deleted from the datastore then this method is called for the object itself and * for all the descendent objects. For example, if a Transaction object is deleted @@ -108,17 +108,17 @@ * Listeners should put code in this method if it needs to process deleted objects in * the same way regardless of how the object was deleted. * - * @param extendableObject + * @param deletedObject */ void objectDestroyed(IModelObject deletedObject); /** - * A scalar property in an extendable object has been changed. + * A scalar property in an object has been changed. */ void objectChanged(IModelObject changedObject, IScalarPropertyAccessor changedProperty, Object oldValue, Object newValue); /** - * An extendable object has been moved from one list property to another list property. + * An object has been moved from one list property to another list property. * The contents of the object, including the contents of any list properties in it, remain * intact. References to the object also remain intact. */ Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/TransactionManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/TransactionManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/TransactionManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -33,13 +33,15 @@ import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.commands.operations.IUndoableOperation; +import org.eclipse.core.commands.operations.UndoContext; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.ui.PlatformUI; /** * A transaction manager must be set before the datastore can be modified. * An exception will be throw if an attempt is made to modify the datastore - * (setting a property, or creating or deleting an extendable object) when + * (setting a property, or creating or deleting an object) when * no transaction manager is set in the session. * <P> * Changes to the datastore are stored in the transaction manager. They @@ -85,7 +87,7 @@ protected Map<IObjectKey, ModifiedObject> modifiedObjects = new HashMap<IObjectKey, ModifiedObject>(); /** - * Every extendable object that exists in the base data manager and that has + * Every model object that exists in the base data manager and that has * already had a copy created in this data manager will be put in this map. * This is required because all DataManager objects must guarantee that * there is only ever a single instance of an object in existence. @@ -130,6 +132,8 @@ */ private MySessionChangeListener baseSessionChangeListener = new MySessionChangeListener(); + private IUndoContext undoContext = new UndoContext(); + /** * Construct a transaction manager for use with the given session. * The transaction manager does not become the active transaction @@ -804,11 +808,11 @@ */ // TODO For the time being, this does, but it is imperfect. - IModelObject extendableObject = getCopyInTransaction(changedObject); + IModelObject modelObject = getCopyInTransaction(changedObject); if (newValue instanceof IModelObject) { newValue = getCopyInTransaction((IModelObject)newValue); } - changedProperty.setValue(extendableObject, newValue); + changedProperty.setValue(modelObject, newValue); } @Override @@ -862,4 +866,9 @@ */ } } + + @Override + public IUndoContext getUndoContext() { + return undoContext; + } } Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedListManager.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedListManager.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedListManager.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -46,18 +46,18 @@ } /** - * Create a new extendable object in the list represented by this object. + * Create a new model object in the list represented by this object. */ @Override public <F extends E> F createNewElement(IExtendablePropertySet<F> propertySet) { UncommittedObjectKey objectKey = new UncommittedObjectKey(transactionManager); - F extendableObject = propertySet.constructDefaultImplementationObject(objectKey, listKey); + F newObject = propertySet.constructDefaultImplementationObject(objectKey, listKey); - objectKey.setObject(extendableObject); + objectKey.setObject(newObject); - add(extendableObject); + add(newObject); - return extendableObject; + return newObject; } /* @@ -67,13 +67,13 @@ @Override public <F extends E> F createNewElement(IExtendablePropertySet<F> propertySet, IValues<F> values) { UncommittedObjectKey objectKey = new UncommittedObjectKey(transactionManager); - F extendableObject = propertySet.constructImplementationObject(objectKey, listKey, values); + F newObject = propertySet.constructImplementationObject(objectKey, listKey, values); - objectKey.setObject(extendableObject); + objectKey.setObject(newObject); - add(extendableObject); + add(newObject); - return extendableObject; + return newObject; } /* @@ -81,15 +81,15 @@ * its changes into this transaction manager. */ @Override - public void deleteElement(E extendableObject) { - boolean found = remove(extendableObject); + public void deleteElement(E element) { + boolean found = remove(element); if (!found) { throw new RuntimeException("internal error - element not in list"); } } @Override - public <F extends E> void moveElement(F extendableObject, IListManager<? super F> originalList) { + public <F extends E> void moveElement(F element, IListManager<? super F> originalList) { /* * It is fairly complex to implement this inside a transaction. * Therefore we do not support this. Modified: trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedObjectKey.java =================================================================== --- trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedObjectKey.java 2013-02-01 09:15:03 UTC (rev 1159) +++ trunk/net.sf.jmoney/src/net/sf/jmoney/isolation/UncommittedObjectKey.java 2013-02-01 09:17:02 UTC (rev 1160) @@ -61,7 +61,7 @@ // exists in the committed datastore. // If a newly created object is committed then - // extendableObject is set to null and any requests for + // modelObject is set to null and any requests for // the object will then be carried out by instantiating // the committed object and applying any changes to // the scalar properties before instantiating the version @@ -70,7 +70,7 @@ // must be visible. private IObjectKey committedObjectKey = null; - private IModelObject extendableObject = null; + private IModelObject modelObject = null; /** * This constructor is used to create an object key for an object that @@ -86,7 +86,7 @@ /** * This constructor is used to create an object key for an object that is - * being returned by a getter for a property referencing another extendable + * being returned by a getter for a property referencing another model * object or is being returned by a list property iterator or is being * created from a given object in the datastore. In all these cases way, the * object key from the datastore is available. @@ -102,9 +102,9 @@ // TODO: make this default protection // Called only when this object constructed with the single parameter constructor. - public void setObject(IModelObject extendableObject) { - Assert.isNotNull(extendableObject); - this.extendableObject = extendableObject; + public void setObject(IModelObject modelObject) { + Assert.isNotNull(modelObject); + this.modelObject = modelObject; } @Override @@ -116,12 +116,12 @@ return false; } - if (this.extendableObject != null && otherKey.extendableObject != null) { + if (this.modelObject != null && otherKey.modelObject != null) { // These are new objects, never previously committed to the datastore, // so there can be only one instance and we can use the default // compare. return (this == otherKey); - } else if (this.extendableObject != null || otherKey.extendableObject != null) { + } else if (this.modelObject != null || otherKey.modelObject != null) { // One is new, one is not, so cannot be equal return false; } else { @@ -134,7 +134,7 @@ @Override public int hashCode() { - if (extendableObject != null) { + if (modelObject != null) { // These are new objects, never previously committed to the datastore, // so there can be only one instance and we can use the default // hashCode. @@ -152,8 +152,8 @@ // We then would not need this 'switch' construct. if (committedObjectKey != null) { return transactionManager.getCopyInTransaction(committedObjectKey.getObject()); - } else if (extendableObject != null) { - return extendableObject; + } else if (modelObject != null) { + return modelObject; } else { throw new RuntimeException("bad case"); //$NON-NLS-1$ } @@ -233,7 +233,7 @@ * datastore */ public IObjectKey getCommittedObjectKey() { - if (extendableObject != null) { + if (modelObject != null) { // This is a new object created in this transaction return null; } else { @@ -254,7 +254,7 @@ */ public void setCommittedObject(IModelObject committedObject) { Assert.isTrue(committedObjectKey == null); - Assert.isNotNull(extendableObject); + Assert.isNotNull(modelObject); committedObjectKey = committedObject.getObjectKey(); @@ -262,7 +262,7 @@ // datastore, we do not cache the object. This is important // because once the object is committed, others can change it // and so we can not longer keep a cached version. - extendableObject = null; + modelObject = null; } @Override This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |