Source Code Cross Referenced for ChangeManager.java in  » ERP-CRM-Financial » jmoney » net » sf » jmoney » model2 » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » ERP CRM Financial » jmoney » net.sf.jmoney.model2 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.Vector;
027:
028:        /**
029:         * Keeps track of changes made to the model.  This is done to enable the 
030:         * undo/redo feature.
031:         * 
032:         * As changes are undone and redone, the id of each object may change.
033:         * For example, in the serializeddatastore plug-in, the id of each object
034:         * is a reference to the object itself, i.e. the java identity.  Unless
035:         * we keep a reference to these objects, which we don't, the identity of
036:         * objects will not be the same when the object is re-created.  (Even if
037:         * we kept a reference to an object, it is not possible to insert that
038:         * object back into the object store for various technical reasons).
039:         * If the datastore is a database, for example in the jdbcdatastore plug-in,
040:         * the id is automatically generated as a value of a unique column.
041:         * The database may decide to re-use the id of a delete row.
042:         * Therefore, this class never stores ids of objects that have been
043:         * deleted.  When an object is deleted, all old values that reference
044:         * the object are replaced with references to the delete entry.
045:         * This allows the data to be re-created correctly by the undo method.
046:         * 
047:         * @author Nigel Westbury
048:         */
049:        public class ChangeManager {
050:
051:            /**
052:             * When we delete an object, we know that nothing in the
053:             * datastore references it.  However, there may be old
054:             * values that referenced it.  It is important that these
055:             * old values are updated to reference this deleted object.
056:             * Otherwise, if the object is re-created with a different
057:             * id then those old values cannot be restored correctly.
058:             */
059:            private class KeyProxy {
060:                IObjectKey key;
061:
062:                KeyProxy(IObjectKey key) {
063:                    this .key = key;
064:                }
065:            }
066:
067:            /*
068:             * If there are no references to the KeyProxy then this means there are no changes that
069:             * need the key proxy to undo the change.  The entry can be removed from the map.
070:             * Thus we use a map with weak value references.
071:             */
072:            private WeakValuedMap<IObjectKey, KeyProxy> keyProxyMap = new WeakValuedMap<IObjectKey, KeyProxy>();
073:
074:            private UndoableChange currentUndoableChange = null;
075:
076:            public class UndoableChange {
077:                /**
078:                 * Vector of ChangeEntry objects.  Changes are added to
079:                 * this vector in order.  If changes are undone, they must
080:                 * be undone in reverse order, starting at the end of this
081:                 * vector.
082:                 */
083:                private Vector<ChangeEntry> changes = new Vector<ChangeEntry>();
084:
085:                /**
086:                 * Submit a series of updates, which have been stored,
087:                 * to the datastore.  These updates carry out the reverse
088:                 * of the updates stored.
089:                 */
090:                public void undoChanges() {
091:                    // Undo the changes in reverse order.
092:                    for (int i = changes.size() - 1; i >= 0; i--) {
093:                        ChangeEntry changeEntry = changes.get(i);
094:                        changeEntry.undo();
095:                    }
096:                }
097:
098:                /**
099:                 * @param newChangeEntry
100:                 */
101:                void addChange(ChangeEntry newChangeEntry) {
102:                    changes.add(newChangeEntry);
103:                }
104:            }
105:
106:            /**
107:             * Base class for all objects that represent a component of
108:             * a change.  Derived classes represent property changes,
109:             * insertion of new objects, and deletion of objects.
110:             * 
111:             * These objects have only a constructor and the <code>undo</code> method.
112:             * Once the <code>undo</code> is called the object is dead.
113:             */
114:            abstract class ChangeEntry {
115:                abstract void undo();
116:            }
117:
118:            /**
119:             * A ChangeEntry object for an update to a scalar property (excluding
120:             * scalar properties that are references to extendable objects).
121:             *
122:             * @param <V>
123:             */
124:            class ChangeEntry_UpdateScalar<V> extends ChangeEntry {
125:                private KeyProxy objectKeyProxy;
126:                private ScalarPropertyAccessor<V> propertyAccessor;
127:                private V oldValue = null;
128:
129:                ChangeEntry_UpdateScalar(KeyProxy objectKeyProxy,
130:                        ScalarPropertyAccessor<V> propertyAccessor, V oldValue) {
131:                    this .objectKeyProxy = objectKeyProxy;
132:                    this .propertyAccessor = propertyAccessor;
133:                    this .oldValue = oldValue;
134:                }
135:
136:                @Override
137:                void undo() {
138:                    ExtendableObject object = objectKeyProxy.key.getObject(); // efficient???
139:                    object.setPropertyValue(propertyAccessor, oldValue);
140:                }
141:            }
142:
143:            /**
144:             * A ChangeEntry object for an update to a scalar property that is a
145:             * reference to an extendable object.
146:             *
147:             * @param <E>
148:             */
149:            // TODO: E should be bounded to classes that extend ExtendableObject.
150:            // However, this method does not currently make use of such bounding,
151:            // and to do that we would have to push back seperate methods for
152:            // reference properties and other scalar properties.
153:            class ChangeEntry_UpdateReference<E> extends ChangeEntry {
154:                private KeyProxy objectKeyProxy;
155:                private ScalarPropertyAccessor<E> propertyAccessor;
156:                private KeyProxy oldValueProxy = null;
157:
158:                ChangeEntry_UpdateReference(KeyProxy objectKeyProxy,
159:                        ScalarPropertyAccessor<E> propertyAccessor,
160:                        KeyProxy oldValueProxy) {
161:                    this .objectKeyProxy = objectKeyProxy;
162:                    this .propertyAccessor = propertyAccessor;
163:                    this .oldValueProxy = oldValueProxy;
164:                }
165:
166:                @Override
167:                void undo() {
168:                    ExtendableObject object = objectKeyProxy.key.getObject(); // efficient???
169:                    // If IObjectKey had a type parameter, we would not need
170:                    // this cast.
171:                    object.setPropertyValue(propertyAccessor, propertyAccessor
172:                            .getClassOfValueObject().cast(
173:                                    oldValueProxy.key.getObject()));
174:                }
175:            }
176:
177:            class ChangeEntry_Insert extends ChangeEntry {
178:                private KeyProxy parentKeyProxy;
179:                private ListPropertyAccessor<?> owningListProperty;
180:                private KeyProxy objectKeyProxy;
181:
182:                ChangeEntry_Insert(KeyProxy parentKeyProxy,
183:                        ListPropertyAccessor<?> owningListProperty,
184:                        KeyProxy objectKeyProxy) {
185:                    this .parentKeyProxy = parentKeyProxy;
186:                    this .owningListProperty = owningListProperty;
187:                    this .objectKeyProxy = objectKeyProxy;
188:                }
189:
190:                @Override
191:                void undo() {
192:                    // Delete the object.
193:                    ExtendableObject object = objectKeyProxy.key.getObject(); // efficient???
194:                    ExtendableObject parent = parentKeyProxy.key.getObject();
195:
196:                    // Delete the object from the datastore.
197:                    parent.getListPropertyValue(owningListProperty).remove(
198:                            object);
199:                }
200:            }
201:
202:            /**
203:             * @param <E> the type of the object being deleted
204:             */
205:            class ChangeEntry_Delete<E extends ExtendableObject> extends
206:                    ChangeEntry {
207:                private Object[] oldValues;
208:                private Collection<ExtensionPropertySet<?>> nonDefaultExtensions;
209:
210:                private KeyProxy parentKeyProxy;
211:                private ListPropertyAccessor<E> owningListProperty;
212:                private KeyProxy objectKeyProxy;
213:                private ExtendablePropertySet<? extends E> actualPropertySet;
214:
215:                ChangeEntry_Delete(KeyProxy parentKeyProxy,
216:                        ListPropertyAccessor<E> owningListProperty, E oldObject) {
217:                    this .parentKeyProxy = parentKeyProxy;
218:                    this .owningListProperty = owningListProperty;
219:
220:                    this .objectKeyProxy = getKeyProxy(oldObject.getObjectKey());
221:                    this .actualPropertySet = owningListProperty
222:                            .getElementPropertySet().getActualPropertySet(
223:                                    (Class<? extends E>) oldObject.getClass());
224:
225:                    /*
226:                     * Save all the property values from the deleted object. We need
227:                     * these to re-create the object if this change is undone.
228:                     */
229:                    nonDefaultExtensions = oldObject.getExtensions();
230:
231:                    int count = actualPropertySet.getScalarProperties3().size();
232:                    oldValues = new Object[count];
233:                    int index = 0;
234:                    for (ScalarPropertyAccessor<?> propertyAccessor : actualPropertySet
235:                            .getScalarProperties3()) {
236:                        if (index != propertyAccessor
237:                                .getIndexIntoScalarProperties()) {
238:                            throw new RuntimeException("index mismatch");
239:                        }
240:
241:                        Object value = oldObject
242:                                .getPropertyValue(propertyAccessor);
243:                        if (value instanceof  ExtendableObject) {
244:                            /*
245:                             * We can't store extendable objects or even the object keys
246:                             * because those may not remain valid (the referenced object may
247:                             * be deleted). We store instead a KeyProxy. If the referenced
248:                             * object is later deleted, then un-deleted using an undo
249:                             * operation, then this change is also undone, the key proxy
250:                             * will give us the new object key for the referenced object.
251:                             */
252:                            IObjectKey objectKey = ((ExtendableObject) value)
253:                                    .getObjectKey();
254:                            oldValues[index++] = getKeyProxy(objectKey);
255:                        } else {
256:                            oldValues[index++] = value;
257:                        }
258:                    }
259:                }
260:
261:                @Override
262:                void undo() {
263:                    /* Create the object in the datastore.
264:                     * However, we must first convert the key proxies back to keys before passing
265:                     * on to the constructor.
266:                     */
267:                    IValues oldValues2 = new IValues() {
268:
269:                        public <V> V getScalarValue(
270:                                ScalarPropertyAccessor<V> propertyAccessor) {
271:                            return propertyAccessor
272:                                    .getClassOfValueObject()
273:                                    .cast(
274:                                            oldValues[propertyAccessor
275:                                                    .getIndexIntoScalarProperties()]);
276:                        }
277:
278:                        public IObjectKey getReferencedObjectKey(
279:                                ReferencePropertyAccessor<? extends ExtendableObject> propertyAccessor) {
280:                            KeyProxy keyProxy = (KeyProxy) oldValues[propertyAccessor
281:                                    .getIndexIntoScalarProperties()];
282:                            return keyProxy == null ? null : keyProxy.key;
283:                        }
284:
285:                        public <E2 extends ExtendableObject> IListManager<E2> getListManager(
286:                                IObjectKey listOwnerKey,
287:                                ListPropertyAccessor<E2> listAccessor) {
288:                            return listOwnerKey
289:                                    .constructListManager(listAccessor);
290:                        }
291:
292:                        public Collection<ExtensionPropertySet<?>> getNonDefaultExtensions() {
293:                            return nonDefaultExtensions;
294:                        }
295:                    };
296:
297:                    ExtendableObject parent = parentKeyProxy.key.getObject();
298:                    ExtendableObject object = parent.getListPropertyValue(
299:                            owningListProperty).createNewElement(
300:                            actualPropertySet, oldValues2, false);
301:
302:                    /*
303:                     * Set the new object key back into the proxy. This ensures that
304:                     * earlier changes to this object will be undone in this object. We
305:                     * must also add to our map so that if further changes are made that
306:                     * reference this object key, they will be using the same proxy.
307:                     */
308:                    if (objectKeyProxy.key != null) {
309:                        throw new RuntimeException(
310:                                "internal error - key proxy error");
311:                    }
312:                    objectKeyProxy.key = object.getObjectKey();
313:                    keyProxyMap.put(objectKeyProxy.key, objectKeyProxy);
314:                }
315:            }
316:
317:            /**
318:             * A ChangeEntry object for a move of an object from one list to another.
319:             */
320:            class ChangeEntry_Move<E extends ExtendableObject> extends
321:                    ChangeEntry {
322:                private E movedObject;
323:                private KeyProxy originalParentKeyProxy;
324:                private ListPropertyAccessor<? super  E> originalListProperty;
325:
326:                ChangeEntry_Move(E movedObject,
327:                        KeyProxy originalParentKeyProxy,
328:                        ListPropertyAccessor<? super  E> originalListProperty) {
329:                    this .movedObject = movedObject;
330:                    this .originalParentKeyProxy = originalParentKeyProxy;
331:                    this .originalListProperty = originalListProperty;
332:                }
333:
334:                @Override
335:                void undo() {
336:                    ExtendableObject originalParent = originalParentKeyProxy.key
337:                            .getObject(); // efficient???
338:                    originalParent.getListPropertyValue(originalListProperty)
339:                            .moveElement(movedObject);
340:                }
341:            }
342:
343:            private KeyProxy getKeyProxy(IObjectKey objectKey) {
344:                if (objectKey != null) {
345:                    KeyProxy keyProxy = keyProxyMap.get(objectKey);
346:                    if (keyProxy == null) {
347:                        keyProxy = new KeyProxy(objectKey);
348:                        keyProxyMap.put(objectKey, keyProxy);
349:                    }
350:                    return keyProxy;
351:                } else {
352:                    return null;
353:                }
354:            }
355:
356:            private void addUndoableChangeEntry(ChangeEntry changeEntry) {
357:                if (currentUndoableChange != null) {
358:                    currentUndoableChange.addChange(changeEntry);
359:                }
360:
361:                /*
362:                 * If changes are made while currentUndoableChange is set to null then
363:                 * the changes are not undoable. This is supported but is not common. It
364:                 * is typically used for very large transactions such as imports of
365:                 * entire databases by the copier plug-in.
366:                 */
367:                // TODO: We should really clear out the change history as
368:                // prior changes are not likely to be undoable after this
369:                // change has been applied. 
370:            }
371:
372:            /**
373:             * The property may be any property in the passed object.
374:             * The property may be defined in the actual class or
375:             * any super classes which the class extends.  The property
376:             * may also be a property in any extension class which extends
377:             * the class of this object or which extends any super class
378:             * of the class of this object.
379:             */
380:            public <V> void processPropertyUpdate(ExtendableObject object,
381:                    ScalarPropertyAccessor<V> propertyAccessor, V oldValue,
382:                    V newValue) {
383:
384:                // Replace any keys with proxy keys
385:                if (propertyAccessor.getClassOfValueObject().isAssignableFrom(
386:                        ExtendableObject.class)) {
387:                    ChangeEntry newChangeEntry = new ChangeEntry_UpdateReference<V>(
388:                            getKeyProxy(object.getObjectKey()),
389:                            propertyAccessor,
390:                            getKeyProxy((IObjectKey) oldValue));
391:
392:                    addUndoableChangeEntry(newChangeEntry);
393:                } else {
394:                    ChangeEntry newChangeEntry = new ChangeEntry_UpdateScalar<V>(
395:                            getKeyProxy(object.getObjectKey()),
396:                            propertyAccessor, oldValue);
397:
398:                    addUndoableChangeEntry(newChangeEntry);
399:                }
400:            }
401:
402:            public void processObjectCreation(ListKey<?> owningListKey,
403:                    ExtendableObject newObject) {
404:
405:                ChangeEntry newChangeEntry = new ChangeEntry_Insert(
406:                        getKeyProxy(owningListKey.getParentKey()),
407:                        owningListKey.getListPropertyAccessor(),
408:                        getKeyProxy(newObject.getObjectKey()));
409:
410:                addUndoableChangeEntry(newChangeEntry);
411:            }
412:
413:            /**
414:             * Processes the deletion of an object. This involves adding the property
415:             * values to the change list so that the deletion can be undone.
416:             * <P>
417:             * Also we must call this method recursively on any objects contained in any
418:             * list properties in the object. This is because this object 'owns' such
419:             * objects, and so those objects will also be deleted and must be restored
420:             * if this operation is undone.
421:             * 
422:             * @param <E>
423:             * @param parent
424:             * @param owningListProperty
425:             * @param oldObject
426:             */
427:            public <E extends ExtendableObject> void processObjectDeletion(
428:                    ExtendableObject parent,
429:                    ListPropertyAccessor<E> owningListProperty, E oldObject) {
430:
431:                /*
432:                 * We must also process objects owned by this object in a recursive
433:                 * manner. Otherwise, undoing the deletion of an object will not restore
434:                 * any objects owned by that object.
435:                 */
436:                for (ListPropertyAccessor<?> subList : PropertySet
437:                        .getPropertySet(oldObject.getClass())
438:                        .getListProperties3()) {
439:                    processObjectListDeletion(oldObject, subList);
440:                }
441:
442:                ChangeEntry_Delete<E> newChangeEntry = new ChangeEntry_Delete<E>(
443:                        getKeyProxy(parent.getObjectKey()), owningListProperty,
444:                        oldObject);
445:
446:                /*
447:                 * The actual key is no longer valid, so we remove the proxy from the
448:                 * map that maps object keys to proxies. For safety we also set this to
449:                 * null.
450:                 * 
451:                 * Note that the proxy itself still exists.  If this deletion is later
452:                 * undone then the object is re-inserted and will be given a new object
453:                 * key by the underlying datastore.  That new object key will then be set in
454:                 * the proxy and the proxy will be added back to the map with the new
455:                 * object key.   
456:                 */
457:
458:                // Remove from the map.
459:                keyProxyMap.remove(newChangeEntry.objectKeyProxy.key);
460:
461:                // This line may not be needed, as the key should never
462:                // be accessed if the proxy represents a key that currently
463:                // does not exist in the datastore.  This line is here for
464:                // safety only.
465:                newChangeEntry.objectKeyProxy.key = null;
466:
467:                addUndoableChangeEntry(newChangeEntry);
468:            }
469:
470:            /**
471:             * Processes the deletion of an object. This involves adding the property
472:             * values to the change list so that the deletion can be undone.
473:             * <P>
474:             * Also we must call this method recursively on any objects contained in any
475:             * list properties in the object. This is because this object 'owns' such
476:             * objects, and so those objects will also be deleted and must be restored
477:             * if this operation is undone.
478:             * 
479:             * @param <E>
480:             * @param parent
481:             * @param owningListProperty
482:             * @param oldObject
483:             */
484:            public <E extends ExtendableObject> void processObjectMove(
485:                    E movedObject, ListKey<? super  E> originalParentListKey) {
486:
487:                ChangeEntry_Move<E> newChangeEntry = new ChangeEntry_Move<E>(
488:                        movedObject, getKeyProxy(originalParentListKey
489:                                .getParentKey()), originalParentListKey
490:                                .getListPropertyAccessor());
491:
492:                addUndoableChangeEntry(newChangeEntry);
493:            }
494:
495:            /**
496:             * Helper function to process the deletion of all objects in a list
497:             * property.
498:             * 
499:             * @param <E>
500:             * @param parent the object containing the list
501:             * @param listProperty the property accessor for the list
502:             */
503:            private <E extends ExtendableObject> void processObjectListDeletion(
504:                    ExtendableObject parent,
505:                    ListPropertyAccessor<E> listProperty) {
506:                for (E childObject : parent.getListPropertyValue(listProperty)) {
507:                    processObjectDeletion(parent, listProperty, childObject);
508:                }
509:            }
510:
511:            public void setUndoableChange() {
512:                currentUndoableChange = new UndoableChange();
513:            }
514:
515:            public UndoableChange takeUndoableChange() {
516:                UndoableChange result = currentUndoableChange;
517:                currentUndoableChange = null;
518:                return result;
519:            }
520:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.