Index: org/netbeans/mdr/util/EventNotifier.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/util/EventNotifier.java,v retrieving revision 1.5 diff -u -r1.5 EventNotifier.java --- org/netbeans/mdr/util/EventNotifier.java 22 May 2002 15:22:08 -0000 1.5 +++ org/netbeans/mdr/util/EventNotifier.java 28 Jun 2002 10:37:39 -0000 @@ -22,6 +22,7 @@ * after transactions. * * @author mmatula + * @author Holger Krug * @version */ public final class EventNotifier { @@ -144,7 +145,7 @@ * Catches all exceptions and logs the stack trace. * *

Returns if a queued change event was not planned before (i.e. does - * not correspond to an entry in {@link #changeListeners}. + * not correspond to an entry in {@link #changeListeners}).

*/ private class EventsDelivery implements Runnable { /** @@ -191,13 +192,25 @@ * the local queue and to fire the pre-change events. */ public abstract class Abstract { + /** + * Maps source objects to pre-change listeners. + */ private final HashMap preChange = new HashMap(); + /** + * Maps source objects to pre-change hierarchy listeners. + */ + private final HashMap preChangeHierarchy = new HashMap(); + /** + * Maps source objects to change listeners. + */ private final HashMap change = new HashMap(); + /** + * Maps source objects to change hierarchy listeners. + */ + private final HashMap changeHierarchy = new HashMap(); /** Registers a new listener in the map listeners. * - *

[PENDING]: make this method static - * * @param listeners a map from source objects to sets of listeners * @param listener the listener to be added to listeners * @param the source object the listener shall be added for @@ -217,8 +230,6 @@ /** Removes a listener from the map listeners. * - *

[PENDING]: make this method static - * * @param listeners a map from source objects to sets of listeners * @param listener the listener to be removed from listeners * @param the source object the listener shall be removed for @@ -241,6 +252,9 @@ * Adds listener for object source. */ public void addListener(MDRChangeListener listener, Object source) { + /* We do not check if the listener was already added as hierarchy listener. + * If a listener is added as well as simple as also as hierarchy listener, + * the correctness of the implementation will not be affected. */ if (listener instanceof MDRPreChangeListener) { register(preChange, listener, source); } else { @@ -249,13 +263,29 @@ } /** + * Adds hierarchy listener for object source. + */ + public void addHierarchyListener(MDRChangeListener listener, Object source) { + /* We do not check if the listener was already added as simple listener. + * If a listener is added as well as simple as also as hierarchy listener, + * the correctness of the implementation will not be affected. */ + if (listener instanceof MDRPreChangeListener) { + register(preChangeHierarchy, listener, source); + } else { + register(changeHierarchy, listener, source); + } + } + + /** * Removes listener for object source. */ public void removeListener(MDRChangeListener listener, Object source) { if (listener instanceof MDRPreChangeListener) { unregister(preChange, listener, source); + unregister(preChangeHierarchy, listener, source); } else { unregister(change, listener, source); + unregister(changeHierarchy, listener, source); } } @@ -271,7 +301,8 @@ public void firePlannedChange(Object current, MDRChangeEvent event) { localQueue.addLast(event); HashSet collected = new HashSet(); - collectListeners(current, event, false, collected); + collectSimpleListeners(current, false, collected); + collectHierarchyListeners(current, event, false, collected); for (Iterator it = collected.iterator(); it.hasNext();) { try { ((MDRPreChangeListener) it.next()).plannedChange(event); @@ -283,31 +314,20 @@ if (preChangeListeners.put(event, collected.clone()) != null) { throw new DebugException("Same event fired twice."); } - collectListeners(current, event, true, collected); + collectSimpleListeners(current, true, collected); + collectHierarchyListeners(current, event, true, collected); if (changeListeners.put(event, collected) != null) { throw new DebugException("Same event fired twice."); } } /** - * Collect the listeners for the given event on object - * current. This method has to be overwritten by derived - * classes to inform listeners on owner objects. Overwriting methods - * shall call this method on then add any further listeners. - * - * @param current the object on which the event was fired - * @param event the event - * @param post if false, listeners implementing - * {@link org.netbeans.api.mdr.events.MDRPreChangeListener} are - * collected, otherwise listeners implementing only - * {@link org.netbeans.api.mdr.events.MDRChangeListener} - * @param collected the set to which the collected listeners have to be - * added + * Collect listeners from map listeners for object current + * into set collected. */ - protected void collectListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + private void collectListenersImpl(Map listeners, Object current, Set collected) { Set value; - Map listeners = post ? change : preChange; - + // fire event on all listeners registered on this object synchronized (listeners) { value = (Set) listeners.get(current); @@ -316,6 +336,41 @@ } } } + + /** + * Collect the simple listeners for the given event on object + * current. + * @param current the object on which the event was fired + * @param post if false, simple listeners implementing + * {@link org.netbeans.api.mdr.events.MDRPreChangeListener} are + * collected, otherwise simple listeners implementing only + * {@link org.netbeans.api.mdr.events.MDRChangeListener} + * @param collected the set to which the collected simple listeners have to be + * added + */ + private void collectSimpleListeners(Object current, boolean post, Set collected) { + collectListenersImpl(post ? change : preChange, current, collected); + } + + /** + * Collect the hierarchy listeners for the given event on object + * current. This method has to be overridden by derived + * classes to inform hierarchy listeners on owner objects. Overwriting methods + * shall call this method and then add any further hierarchy listeners. + * + * @param current the object on which the event was fired + * @param event the event, super-classes will use this parameter to decide + * how to forward the event + * @param post if false, hierarchy listeners implementing + * {@link org.netbeans.api.mdr.events.MDRPreChangeListener} are + * collected, otherwise hierarchy listeners implementing only + * {@link org.netbeans.api.mdr.events.MDRChangeListener} + * @param collected the set to which the collected hierarchy listeners have to be + * added + */ + protected void collectHierarchyListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + collectListenersImpl(post ? changeHierarchy : preChangeHierarchy, current, collected); + } } /* -------------------------------------------------------------------- */ @@ -331,22 +386,22 @@ } /** - * Adds listeners on the instances participating in the association link + * Adds hierarchy listeners on the instances participating in the association link * added resp. removed and on the owning package. */ - protected void collectListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { - super.collectListeners(current, event, post, collected); + protected void collectHierarchyListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + super.collectHierarchyListeners(current, event, post, collected); // fire event on all listeners registered on instances that were affected if (event instanceof AssociationEvent) { AssociationEvent assocEvent = (AssociationEvent) event; - if (assocEvent.getFixedElement() != null) INSTANCE.collectListeners(assocEvent.getFixedElement(), event, post, collected); - if (assocEvent.getOldElement() != null) INSTANCE.collectListeners(assocEvent.getOldElement(), event, post, collected); - if (assocEvent.getNewElement() != null) INSTANCE.collectListeners(assocEvent.getNewElement(), event, post, collected); + if (assocEvent.getFixedElement() != null) INSTANCE.collectHierarchyListeners(assocEvent.getFixedElement(), event, post, collected); + if (assocEvent.getOldElement() != null) INSTANCE.collectHierarchyListeners(assocEvent.getOldElement(), event, post, collected); + if (assocEvent.getNewElement() != null) INSTANCE.collectHierarchyListeners(assocEvent.getNewElement(), event, post, collected); } // fire event on the immediate package extent - PACKAGE.collectListeners(((RefAssociation) current).refImmediatePackage(), event, post, collected); + PACKAGE.collectHierarchyListeners(((RefAssociation) current).refImmediatePackage(), event, post, collected); } } @@ -365,9 +420,9 @@ /** * Adds listeners on the owning package. */ - protected void collectListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { - super.collectListeners(current, event, post, collected); - PACKAGE.collectListeners(((RefClass) current).refImmediatePackage(), event, post, collected); + protected void collectHierarchyListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + super.collectHierarchyListeners(current, event, post, collected); + PACKAGE.collectHierarchyListeners(((RefClass) current).refImmediatePackage(), event, post, collected); } } @@ -384,11 +439,11 @@ } /** - * Adds listeners on the owning class proxy. + * Adds hierarchy listeners on the owning class proxy. */ - protected void collectListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { - super.collectListeners(current, event, post, collected); - CLASS.collectListeners(((RefObject) current).refClass(), event, post, collected); + protected void collectHierarchyListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + super.collectHierarchyListeners(current, event, post, collected); + CLASS.collectHierarchyListeners(((RefObject) current).refClass(), event, post, collected); } } @@ -405,16 +460,16 @@ } /** - * Adds listeners on the owning package resp., if this package is + * Adds hierarchy listeners on the owning package resp., if this package is * outermost, on the repository. */ - protected void collectListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { - super.collectListeners(current, event, post, collected); + protected void collectHierarchyListeners(Object current, MDRChangeEvent event, boolean post, Set collected) { + super.collectHierarchyListeners(current, event, post, collected); RefPackage immediate = ((RefPackage) current).refImmediatePackage(); if (immediate != null) { - collectListeners(immediate, event, post, collected); + collectHierarchyListeners(immediate, event, post, collected); } else { - REPOSITORY.collectListeners(((BaseObjectHandler) current)._getDelegate().getMdrStorage(), event, post, collected); + REPOSITORY.collectHierarchyListeners(((BaseObjectHandler) current)._getDelegate().getMdrStorage(), event, post, collected); } } } Index: org/netbeans/mdr/handlers/InstanceHandler.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/handlers/InstanceHandler.java,v retrieving revision 1.16 diff -u -r1.16 InstanceHandler.java --- org/netbeans/mdr/handlers/InstanceHandler.java 25 Jun 2002 15:48:01 -0000 1.16 +++ org/netbeans/mdr/handlers/InstanceHandler.java 28 Jun 2002 10:37:39 -0000 @@ -355,6 +355,13 @@ _getMdrStorage().getEventNotifier().INSTANCE.addListener(listener, this); } + /** Registers a hierarchy listener for receiving event notifications. + * @param listener Object that implements {@link Listener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener) { + _getMdrStorage().getEventNotifier().INSTANCE.addHierarchyListener(listener, this); + } + /** Removes listener from the list of objects registered for events notifications. * @param listener Object that implements {@link Listener} interface. */ Index: org/netbeans/mdr/handlers/ClassProxyHandler.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/handlers/ClassProxyHandler.java,v retrieving revision 1.18 diff -u -r1.18 ClassProxyHandler.java --- org/netbeans/mdr/handlers/ClassProxyHandler.java 26 Jun 2002 12:41:02 -0000 1.18 +++ org/netbeans/mdr/handlers/ClassProxyHandler.java 28 Jun 2002 10:37:39 -0000 @@ -241,6 +241,13 @@ public void addListener(MDRChangeListener listener) { _getMdrStorage().getEventNotifier().CLASS.addListener(listener, this); } + + /** Registers a hierarchy listener for receiving event notifications. + * @param listener Object that implements {@link Listener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener) { + _getMdrStorage().getEventNotifier().CLASS.addHierarchyListener(listener, this); + } /** Removes listener from the list of objects registered for events notifications. * @param listener Object that implements {@link Listener} interface. Index: org/netbeans/mdr/handlers/PackageProxyHandler.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/handlers/PackageProxyHandler.java,v retrieving revision 1.12 diff -u -r1.12 PackageProxyHandler.java --- org/netbeans/mdr/handlers/PackageProxyHandler.java 17 Jun 2002 09:47:22 -0000 1.12 +++ org/netbeans/mdr/handlers/PackageProxyHandler.java 28 Jun 2002 10:37:39 -0000 @@ -333,6 +333,13 @@ public void addListener(MDRChangeListener listener) { _getMdrStorage().getEventNotifier().PACKAGE.addListener(listener, this); } + + /** Registers a hierarchy listener for receiving event notifications. + * @param listener Object that implements {@link Listener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener) { + _getMdrStorage().getEventNotifier().PACKAGE.addHierarchyListener(listener, this); + } /** Removes listener from the list of objects registered for events notifications. * @param listener Object that implements {@link Listener} interface. Index: org/netbeans/mdr/handlers/AssociationHandler.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/handlers/AssociationHandler.java,v retrieving revision 1.21 diff -u -r1.21 AssociationHandler.java --- org/netbeans/mdr/handlers/AssociationHandler.java 26 Jun 2002 12:41:02 -0000 1.21 +++ org/netbeans/mdr/handlers/AssociationHandler.java 28 Jun 2002 10:37:40 -0000 @@ -310,6 +310,13 @@ _getMdrStorage().getEventNotifier().ASSOCIATION.addListener(listener, this); } + /** Registers a hierarchy listener for receiving event notifications. + * @param listener Object that implements {@link Listener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener) { + _getMdrStorage().getEventNotifier().ASSOCIATION.addHierarchyListener(listener, this); + } + /** Removes listener from the list of objects registered for events notifications. * @param listener Object that implements {@link Listener} interface. */ Index: org/netbeans/modules/mdrexplorer/looks/MDREventHandler.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/modules/mdrexplorer/looks/MDREventHandler.java,v retrieving revision 1.1 diff -u -r1.1 MDREventHandler.java --- org/netbeans/modules/mdrexplorer/looks/MDREventHandler.java 22 Apr 2002 10:35:41 -0000 1.1 +++ org/netbeans/modules/mdrexplorer/looks/MDREventHandler.java 28 Jun 2002 10:37:39 -0000 @@ -26,24 +26,55 @@ import org.netbeans.api.looks.Look; /** + * Instances of MDREventHandler are event handlers which listen + * on repository changes on behalf of {@link org.netbeans.api.looks.LookNodeSubstitutes + * node substitutes}. They manage a map from repository objects to node substitutes + * to be able to access the node substitute for a given repository object. Looks + * have to register their node subsitutes by calling {@link #addNodeSubstitute(Look.NodeSubstitute)} + * or {@link #addNodeSubstitute(Object, Look.NodeSubstitute)} from within + * {@link org.netbeans.api.looks.Look#attachTo(ook.NodeSubstitute)}. * + * @deprecated register NodeSubstitutes directly on repository objects instead, see + * issue 25186 * @author Tomas Zezula */ public class MDREventHandler implements MDRPreChangeListener { + /* --------------------------------------------------------------------- */ + /* -- Attributes ------------------------------------------------------- */ + /* --------------------------------------------------------------------- */ + + /** + * Maps repository content to node substitutes. + */ private HashMap registeredNodeSubstitutes; + + /** + * Map storing the parents of objects to be deleted. After an object has + * been deleted, its parent node is not accessible any more. Nevertheless it + * must be refreshed to reflect the deletion of the node representing the object. + * Hence the parents of the objects to be deleted are stored in the map. + */ private HashMap pendingEvents; private ReferenceQueue queue; private MDRepository repository; + /* --------------------------------------------------------------------- */ + /* -- Constructor ------------------------------------------------------ */ + /* --------------------------------------------------------------------- */ + public MDREventHandler (MDRepository repository) { this.queue = new ReferenceQueue(); this.registeredNodeSubstitutes = new HashMap(); this.pendingEvents = new HashMap(); this.repository = repository; - this.repository.addListener(this); + this.repository.addHierarchyListener(this); } + /* --------------------------------------------------------------------- */ + /* -- Management of NodeSubstitutes ------------------------------------ */ + /* --------------------------------------------------------------------- */ + public synchronized void addNodeSubstitute(Look.NodeSubstitute substitute) { if (substitute == null) return; @@ -56,32 +87,25 @@ this.registeredNodeSubstitutes.put(key, new WeakReference(substitute,this.queue)); } - /** This method is called when the node was garbage collected. You shoud - * unregister from all objects where this object was registered as a - * listener. - */ - - /** This method gets called if a planned change (which was already announced - * by calling {@link #plannedChange} was cancelled (e.g. the operation that was - * going to perform the change failed). This method is called synchronously by - * the operation that tried to perform the change.

- * Any run-time exception thrown by the implementation of this method should - * not affect the events dispatching (i.e. it should be ignored by the event source). - * @param e Object describing the cancelled change (has to be the same instance - * as passed to the {@link #plannedChange} method). - */ - public void changeCancelled(MDRChangeEvent e) { - if ((e.getType() & 2) == 2) { - this.pendingEvents.remove(e.getSource()); + private synchronized Look.NodeSubstitute getNodeSubstituteForObject(Object object) { + if (object ==null) + return null; + Reference ref = (Reference) this.registeredNodeSubstitutes.get(object); + if (ref == null) { + return null; // Not registered object } + Look.NodeSubstitute substitute = (Look.NodeSubstitute) ref.get(); + return substitute; } - /** This method gets called when a repository change is planned to occur. - * Any operation that performs a change in MDR has to fire this notification - * synchronously on each registered pre-change listener before the change is performed.

- * Any run-time exception thrown by the implementation of this method should - * not affect the events dispatching (i.e. it should be ignored by the event source). - * @param e Object describing the planned change. + /* --------------------------------------------------------------------- */ + /* -- Implements org.netbeans.api.mdr.MDRPreChangeListener ------------- */ + /* --------------------------------------------------------------------- */ + + /** + * Handles deletion events only. Stores the parents of objects to be deleted. + * This is necessary to refresh parent nodes when the object to be deleted + * has been deleted. */ public void plannedChange(MDRChangeEvent e) { if ((e.getType() & ExtentEvent.EVENT_EXTENT_DELETE) == ExtentEvent.EVENT_EXTENT_DELETE) { @@ -107,16 +131,18 @@ } } - /** This method gets called after a repository change is performed. This method - * is called asynchronously. - * If a listener implements {@link MDRPreChangeListener} which is a descedant - * of this interface, the event object passed to this method must be the same - * instance as the event object previously passed to the corresponding - * {@link MDRPreChangeListener#plannedChange} method call of the listener.

- * Any run-time exception thrown by the implementation of this method should - * not affect the events dispatching (i.e. it should be ignored by the event source). - * - * @param e Object describing the performed change. + /** + * Removes the information stored for the pending event e. + * @see #plannedChange(MDRChangeEvent) + */ + public void changeCancelled(MDRChangeEvent e) { + if ((e.getType() & 2) == 2) { + this.pendingEvents.remove(e.getSource()); + } + } + + /** + * Refreshes the nodes affected by e. */ public void change(MDRChangeEvent e) { if ((e.getType() & ExtentEvent.EVENT_EXTENT_CREATE) == ExtentEvent.EVENT_EXTENT_CREATE) { @@ -171,6 +197,14 @@ } } + /* --------------------------------------------------------------------- */ + /* -- Clean-up dangling map entries ------------------------------------ */ + /* --------------------------------------------------------------------- */ + + /** + * Removes entries with garbage collected values from the map associating + * repository objects with node substitutes. + */ synchronized void flushReferenceQueue() { if (this.queue.poll()!=null) { Iterator values = this.registeredNodeSubstitutes.values().iterator(); @@ -184,17 +218,6 @@ } } - - private synchronized Look.NodeSubstitute getNodeSubstituteForObject(Object object) { - if (object ==null) - return null; - Reference ref = (Reference) this.registeredNodeSubstitutes.get(object); - if (ref == null) { - return null; // Not registered object - } - Look.NodeSubstitute substitute = (Look.NodeSubstitute) ref.get(); - return substitute; - } } Index: org/netbeans/api/mdr/events/MDRChangeSource.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/api/mdr/events/MDRChangeSource.java,v retrieving revision 1.1 diff -u -r1.1 MDRChangeSource.java --- org/netbeans/api/mdr/events/MDRChangeSource.java 16 Feb 2002 01:30:39 -0000 1.1 +++ org/netbeans/api/mdr/events/MDRChangeSource.java 28 Jun 2002 10:37:39 -0000 @@ -18,21 +18,32 @@ * The objects that have to be registered for event * notifications need to implement {@link MDRChangeListener} * or {@link MDRPreChangeListener} interface.

- * The repository should distribute all the events in the following way: + * + *

Listeners can be registered for events originating on the object the + * listener is registered for by calling {@link #addListener(MDRChangeListener)}. + * Listeners can also be registered for events originating on a hierarchy of + * objects by calling {@link #addHierarchyListener(MDRChangeListener)}.

+ * + * The repository should distribute all the events to hierarchy listeners in the following way: * - * All the events are propagated recursively till they reach the repository object (e.g. each + * All the events are propagated recursively to hierarchy listeners till they reach + * the repository object (e.g. each * instance event is as a result of propagation always fired on the instance itself, * on its class proxy, on its immediate package proxy, on all other package proxies containing * the immediate package proxy to the outermost package proxy and at the end on the repository containing - * instance).
+ * instance - all hierarchy listeners in this chain of objects are informed about the + * event, and the simple listeners at the starting point of the chain).
* In addition, any event is fired only once for each listener (so no matter how many objects * on the event's propagation path is a listener registered on - e.g. on both * class proxy and its instances - it receives each notification only once per event). @@ -40,10 +51,16 @@ * @author Martin Matula */ public interface MDRChangeSource { - /** Registers a listener for receiving event notifications. + /** Registers a listener for receiving event notifications for this + * MDRChangeSource. * @param listener Object that implements {@link MDRChangeListener} interface. */ public void addListener(MDRChangeListener listener); + /** Registers a listener for receiving event notifications for this + * MDRChangeSource and its children. + * @param listener Object that implements {@link MDRChangeListener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener); /** Removes listener from the list of objects registered for events notifications. * @param listener Object that implements {@link MDRChangeListener} interface. */ Index: org/netbeans/mdr/NBMDRepositoryImpl.java =================================================================== RCS file: /cvs/mdr/src/org/netbeans/mdr/NBMDRepositoryImpl.java,v retrieving revision 1.36 diff -u -r1.36 NBMDRepositoryImpl.java --- org/netbeans/mdr/NBMDRepositoryImpl.java 26 Jun 2002 12:41:01 -0000 1.36 +++ org/netbeans/mdr/NBMDRepositoryImpl.java 28 Jun 2002 10:37:40 -0000 @@ -110,14 +110,16 @@ } /** Creates new {@link org.netbeans.api.mdr.MDRepository} with given parameters. - * The following parameters are processed: + * The following parameter is processed: * *

    *
  1. storage: name of a class implementing {@link * org.netbeans.mdr.persistence.StorageFactory}
  2. - *
  3. fileName: storage location
  4. - *
+ *

* + *

All other parameters depend on the StorageFactory and are + * forwarded to + * {@link rg.netbeans.mdr.persistence.StorageFactory#createStorage(java.util.Map)}.

*/ public NBMDRepositoryImpl(Map parameters) { Log.out.println("Creating MDRepository implementation ..."); @@ -135,6 +137,14 @@ public void addListener(MDRChangeListener listener) { initCheck(); mdrStorage.getEventNotifier().REPOSITORY.addListener(listener, mdrStorage); + } + + /** Registers a hierarchy listener for receiving event notifications. + * @param listener Object that implements {@link Listener} interface. + */ + public void addHierarchyListener(MDRChangeListener listener) { + initCheck(); + mdrStorage.getEventNotifier().REPOSITORY.addHierarchyListener(listener, mdrStorage); } /** Removes listener from the list of objects registered for events notifications.