KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > mdr > util > EventNotifier


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.mdr.util;
20
21 import java.util.Map JavaDoc;
22 import org.netbeans.api.mdr.events.*;
23 import org.netbeans.mdr.handlers.BaseObjectHandler;
24 import org.netbeans.mdr.storagemodel.CompositeCollection;
25 import java.util.*;
26 import javax.jmi.reflect.*;
27
28 /**
29  * Utility class for the management of listener notifications during and
30  * after transactions.
31  *
32  * @author Martin Matula
33  * @author <a HREF="mailto:hkrug@rationalizer.com">Holger Krug</a>.
34  * @version
35  */

36 public final class EventNotifier {
37
38     /* -------------------------------------------------------------------- */
39     /* -- Constants ------------------------------------------------------- */
40     /* -------------------------------------------------------------------- */
41
42     /** Bitmask representing all event types which may be received by listeners
43      * listening on associations. */

44     public static final int EVENTMASK_BY_ASSOCIATION = MDRChangeEvent.EVENTMASK_ON_ASSOCIATION;
45     /** Bitmask representing all event types which may be received by listeners
46      * listening on instances. */

47     public static final int EVENTMASK_BY_INSTANCE = EVENTMASK_BY_ASSOCIATION | MDRChangeEvent.EVENTMASK_ON_INSTANCE;
48     /** Bitmask representing all event types which may be received by listeners
49      * listening on class proxies. */

50     public static final int EVENTMASK_BY_CLASS = EVENTMASK_BY_INSTANCE | MDRChangeEvent.EVENTMASK_ON_CLASS;
51     /** Bitmask representing all event types which may be received by listeners
52      * listening on package proxies. */

53     public static final int EVENTMASK_BY_PACKAGE = EVENTMASK_BY_CLASS | MDRChangeEvent.EVENTMASK_ON_PACKAGE;
54     /** Bitmask representing all event types which may be received by listeners
55      * listening on repository proxies. */

56     public static final int EVENTMASK_BY_REPOSITORY = EVENTMASK_BY_CLASS | MDRChangeEvent.EVENTMASK_ON_REPOSITORY;
57
58     /* -------------------------------------------------------------------- */
59     /* -- Methods for debugging purposes (static) ------------------------- */
60     /* -------------------------------------------------------------------- */
61
62     /**
63      * Pretty prints an event type.
64      *
65      * <p>[XXX]: Probably this method should be used to a test utility
66      * class ?!</p>
67      */

68     public static String JavaDoc prettyPrintType(MDRChangeEvent e) {
69         if (e.isOfType(AttributeEvent.EVENT_ATTRIBUTE_ADD)) return "EVENT_ATTRIBUTE_ADD";
70         else if (e.isOfType(AttributeEvent.EVENT_ATTRIBUTE_REMOVE)) return "EVENT_ATTRIBUTE_REMOVE";
71         else if (e.isOfType(AttributeEvent.EVENT_ATTRIBUTE_SET)) return "EVENT_ATTRIBUTE_SET";
72         else if (e.isOfType(AttributeEvent.EVENT_CLASSATTR_ADD)) return "EVENT_CLASSATTR_ADD";
73         else if (e.isOfType(AttributeEvent.EVENT_CLASSATTR_REMOVE)) return "EVENT_CLASSATTR_REMOVE";
74         else if (e.isOfType(AttributeEvent.EVENT_CLASSATTR_SET)) return "EVENT_CLASSATTR_SET";
75         else if (e.isOfType(InstanceEvent.EVENT_INSTANCE_CREATE)) return "EVENT_INSTANCE_CREATE";
76         else if (e.isOfType(InstanceEvent.EVENT_INSTANCE_DELETE)) return "EVENT_INSTANCE_DELETE";
77         else if (e.isOfType(AssociationEvent.EVENT_ASSOCIATION_ADD)) return "EVENT_ASSOCIATION_ADD";
78         else if (e.isOfType(AssociationEvent.EVENT_ASSOCIATION_REMOVE)) return "EVENT_ASSOCIATION_REMOVE";
79         else if (e.isOfType(AssociationEvent.EVENT_ASSOCIATION_SET)) return "EVENT_ASSOCIATION_SET";
80         else if (e.isOfType(ExtentEvent.EVENT_EXTENT_CREATE)) return "EVENT_EXTENT_CREATE";
81         else if (e.isOfType(ExtentEvent.EVENT_EXTENT_DELETE)) return "EVENT_EXTENT_DELETE";
82         else if (e.isOfType(TransactionEvent.EVENT_TRANSACTION_END)) return "EVENT_TRANSACTION_END";
83         else if (e.isOfType(TransactionEvent.EVENT_TRANSACTION_START)) return "EVENT_TRANSACTION_START";
84         else return "<UNKNOWN>";
85     }
86     /**
87      * Pretty prints a event type mask.
88      *
89      * <p>[XXX]: Probably this method should be used to a test utility
90      * class ?!</p>
91      */

92     public static String JavaDoc prettyPrintMask(int mask) {
93         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
94         if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_ADD) == AttributeEvent.EVENT_ATTRIBUTE_ADD ) buf.append("IAA ");
95         if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_REMOVE) == AttributeEvent.EVENT_ATTRIBUTE_REMOVE ) buf.append("IAR ");
96         if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_SET) == AttributeEvent.EVENT_ATTRIBUTE_SET ) buf.append("IAS ");
97         if ( (mask & AttributeEvent.EVENT_CLASSATTR_ADD) == AttributeEvent.EVENT_CLASSATTR_ADD ) buf.append("CAA ");
98         if ( (mask & AttributeEvent.EVENT_CLASSATTR_REMOVE) == AttributeEvent.EVENT_CLASSATTR_REMOVE ) buf.append("CAR ");
99         if ( (mask & AttributeEvent.EVENT_CLASSATTR_SET) == AttributeEvent.EVENT_CLASSATTR_SET ) buf.append("CAS ");
100         if ( (mask & InstanceEvent.EVENT_INSTANCE_CREATE) == InstanceEvent.EVENT_INSTANCE_CREATE ) buf.append("IC ");
101         if ( (mask & InstanceEvent.EVENT_INSTANCE_DELETE) == InstanceEvent.EVENT_INSTANCE_DELETE ) buf.append("ID ");
102         if ( (mask & AssociationEvent.EVENT_ASSOCIATION_ADD) == AssociationEvent.EVENT_ASSOCIATION_ADD ) buf.append("AA ");
103         if ( (mask & AssociationEvent.EVENT_ASSOCIATION_REMOVE) == AssociationEvent.EVENT_ASSOCIATION_REMOVE ) buf.append("AR ");
104         if ( (mask & AssociationEvent.EVENT_ASSOCIATION_SET) == AssociationEvent.EVENT_ASSOCIATION_SET ) buf.append("AS ");
105         if ( (mask & ExtentEvent.EVENT_EXTENT_CREATE) == ExtentEvent.EVENT_EXTENT_CREATE ) buf.append("EC ");
106         if ( (mask & ExtentEvent.EVENT_EXTENT_DELETE) == ExtentEvent.EVENT_EXTENT_DELETE ) buf.append("ED ");
107         if ( (mask & TransactionEvent.EVENT_TRANSACTION_END) == TransactionEvent.EVENT_TRANSACTION_END ) buf.append("TE ");
108         if ( (mask & TransactionEvent.EVENT_TRANSACTION_START) == TransactionEvent.EVENT_TRANSACTION_START ) buf.append("TS ");
109         if ( buf.length() > 0 ) buf.deleteCharAt(buf.length()-1);
110         return buf.toString();
111     }
112
113     /* -------------------------------------------------------------------- */
114     /* -- Public attributes ----------------------------------------------- */
115     /* -------------------------------------------------------------------- */
116
117     public final Association ASSOCIATION = new Association();
118     public final Clazz CLASS = new Clazz();
119     public final Instance INSTANCE = new Instance();
120     public final Package JavaDoc PACKAGE = new Package JavaDoc();
121     public final Repository REPOSITORY = new Repository();
122
123     /* -------------------------------------------------------------------- */
124     /* -- Private attributes ---------------------------------------------- */
125     /* -------------------------------------------------------------------- */
126
127     /**
128      * Thread for the dispatching of events after transaction success.
129      */

130     private final Thread JavaDoc dispatcher = new Thread JavaDoc(new EventsDelivery(), "MDR event dispatcher");
131
132     /**
133      * Maps: event =&gt; set of pre-change listeners.
134      */

135     private final HashMap preChangeListeners = new HashMap();
136
137     /**
138      * Maps: event =&gt; set of change listeners.
139      */

140     private final Hashtable changeListeners = new Hashtable();
141
142     /**
143      * Queue for the events of the currently running write transaction.
144      * The events are stored to inform their listeners either about
145      * rollback or success.
146      */

147     private final LinkedList localQueue = new LinkedList();
148
149     /**
150      * Queue for the events of successfully finished transactions, the
151      * listeners of which still have to be informed.
152      */

153     private final LinkedList globalQueue = new LinkedList();
154
155     /** Flag that tells the event dispatching thread to stop (set to true in
156      * shutdown method.
157      */

158     private boolean shuttingDown = false;
159     
160     /* -------------------------------------------------------------------- */
161     /* -- Constructor (public) -------------------------------------------- */
162     /* -------------------------------------------------------------------- */
163
164     /**
165      * Starts the dispatcher thread as daemon.
166      */

167     public EventNotifier() {
168         dispatcher.setDaemon(true);
169         dispatcher.start();
170     }
171
172     /* -------------------------------------------------------------------- */
173     /* -- Methods to fire events at transaction commit or rollback time --- */
174     /* -------------------------------------------------------------------- */
175
176     /**
177      * Calls {@link MDRPreChangeListener.changeCancelled(MDRChangeEvent)} for
178      * all pre-change listeners on <code>event</code> to inform them
179      * about cancelling the event.
180      */

181     public void fireCancelled(MDRChangeEvent event) {
182         localQueue.remove(event);
183         Set collected = (Set) preChangeListeners.remove(event);
184         changeListeners.remove(event);
185         if (collected == null) {
186             Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("Change cancelled event not corresponding to any planned change event."));
187             return;
188         }
189         // fire changeCancelled event
190
for (Iterator it = collected.iterator(); it.hasNext();) {
191             try {
192                 ((MDRPreChangeListener) it.next()).changeCancelled(event);
193             } catch (RuntimeException JavaDoc e) {
194                 // log the exception
195
Logger.getDefault().notify(Logger.INFORMATIONAL, e);
196             }
197         }
198     }
199
200     /**
201      * Calls {@link MDRPreChangeListener.changeCancelled(MDRChangeEvent)}
202      * on all pre-change listeners of all events of the transaction to be
203      * rolled back.
204      */

205     public void fireCancelled() {
206         while (!localQueue.isEmpty()) {
207             MDRChangeEvent event = (MDRChangeEvent) localQueue.getFirst();
208             fireCancelled(event);
209         }
210     }
211
212     /**
213      * Enqueues all events of a transaction successfully finished to inform
214      * the listeners in a separate thread.
215      */

216     public void fireChanged() {
217         preChangeListeners.clear();
218         synchronized (globalQueue) {
219             globalQueue.addAll(localQueue);
220             globalQueue.notify();
221         }
222         localQueue.clear();
223     }
224     
225     /** Shuts down event dispatcher (by stopping event dispatching thread)
226      */

227     public void shutdown() {
228         synchronized (globalQueue) {
229             shuttingDown = true;
230             globalQueue.notify();
231         }
232     }
233
234     /* -------------------------------------------------------------------- */
235     /* -- EventNotifier.EventDelivery (inner class, private) -------------- */
236     /* -------------------------------------------------------------------- */
237
238     /**
239      * Runnable to be executed by the event dispatcher thread. Waits on the
240      * global queue if it is empty. Informs the listeners on queued events.
241      * Catches all exceptions and logs the stack trace.
242      *
243      * <p>Returns if a queued change event was not planned before (i.e. does
244      * not correspond to an entry in {@link #changeListeners}.
245      */

246     private class EventsDelivery implements Runnable JavaDoc {
247         /**
248          *
249          */

250         public void run() {
251             Collection collected;
252             MDRChangeEvent event;
253
254             while (true) {
255                 synchronized (globalQueue) {
256                     for (;;) {
257                         if (!globalQueue.isEmpty())
258                             break;
259                         else if (shuttingDown)
260                             return;
261                         
262                         try {
263                             globalQueue.wait();
264                         } catch (InterruptedException JavaDoc e) {
265                             Logger.getDefault().notify(Logger.INFORMATIONAL, e);
266                         }
267                     }
268                     collected = (Collection) changeListeners.remove(event = (MDRChangeEvent) globalQueue.removeFirst());
269                 }
270                 if (collected == null) {
271                     Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("Change event not corresponding to any planned change event."));
272                     return;
273                 }
274                 for (Iterator it = collected.iterator(); it.hasNext();) {
275                     try {
276                         ((MDRChangeListener) it.next()).change(event);
277                     } catch (RuntimeException JavaDoc e) {
278                         Logger.getDefault().notify(Logger.INFORMATIONAL, e);
279                     }
280                 }
281             }
282         }
283     }
284     
285     /* -------------------------------------------------------------------- */
286     /* -- EventNotifier.Abstract (inner class, public abstract) ----------- */
287     /* -------------------------------------------------------------------- */
288
289     /**
290      * This class and its derived classes are used to enqueue all changes into
291      * the local queue and to fire the pre-change events.
292      */

293     public abstract class Abstract {
294         /**
295          * Maps repository objects to instances of <code>RegisteredListenerSet</code>.
296          */

297         private final Map JavaDoc registeredListenerSets = new Map JavaDoc() {
298             public Set keySet() { throw new UnsupportedOperationException JavaDoc(); }
299             public Set entrySet() { throw new UnsupportedOperationException JavaDoc(); }
300             public void putAll(Map JavaDoc t) { throw new UnsupportedOperationException JavaDoc(); }
301             public boolean isEmpty() { throw new UnsupportedOperationException JavaDoc(); }
302             public boolean containsKey(Object JavaDoc key) { throw new UnsupportedOperationException JavaDoc(); }
303             public boolean containsValue(Object JavaDoc value) { throw new UnsupportedOperationException JavaDoc(); }
304             public Collection values() { throw new UnsupportedOperationException JavaDoc(); }
305             public void clear() { throw new UnsupportedOperationException JavaDoc(); }
306             public int size() { throw new UnsupportedOperationException JavaDoc(); }
307             
308             private final Map JavaDoc inner = new HashMap();
309             
310             private Object JavaDoc extractKey(Object JavaDoc key) {
311                 if (key instanceof BaseObjectHandler) {
312                     try {
313                         key = ((BaseObjectHandler) key)._getMofId();
314                     } catch (InvalidObjectException e) {
315                         // ignore
316
return null;
317                     }
318                 }
319                 return key;
320             }
321
322             public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
323                 key = extractKey(key);
324                 if (key != null) {
325                     return inner.put(key, value);
326                 } else {
327                     return null;
328                 }
329             }
330
331             public Object JavaDoc get(Object JavaDoc key) {
332                 key = extractKey(key);
333                 if (key != null) {
334                     return inner.get(key);
335                 } else {
336                     return null;
337                 }
338             }
339
340             public Object JavaDoc remove(Object JavaDoc key) {
341                 key = extractKey(key);
342                 if (key != null) {
343                     return inner.remove(key);
344                 } else {
345                     return null;
346                 }
347             }
348         };
349         
350         /**
351          * Adds <code>listener</code> to <code>source</code> for event types
352          * matching <code>mask</code>.
353          */

354         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
355             if ( (listener == null) || (mask == 0) ) return;
356             
357             synchronized (registeredListenerSets) {
358                 RegisteredListenerSet value = (RegisteredListenerSet) registeredListenerSets.get(source);
359                 if ( value == null ) {
360                     value = new RegisteredListenerSet();
361                     registeredListenerSets.put(source, value);
362                 }
363                 value.addListener(listener, mask);
364             }
365         }
366         
367         /**
368          * Removes <code>listener</code> from <code>source</code> for all event types.
369          */

370         public void removeListener(MDRChangeListener listener, Object JavaDoc source) {
371             if ( listener == null ) return;
372             
373             synchronized (registeredListenerSets) {
374                 RegisteredListenerSet value = (RegisteredListenerSet) registeredListenerSets.get(source);
375                 if ( value != null ) {
376                     value.removeListener(listener);
377                     if ( value.isEmpty() ) {
378                         registeredListenerSets.remove(source);
379                     }
380                 }
381             }
382         }
383         
384         /**
385          * Removes <code>listener</code> from <code>source</code> for event types
386          * matching <code>mask</code>.
387          */

388         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
389             if ( (listener == null) || (mask == 0) ) return;
390             
391             synchronized (registeredListenerSets) {
392                 RegisteredListenerSet value = (RegisteredListenerSet) registeredListenerSets.get(source);
393                 if ( value != null ) {
394                     value.removeListener(listener, mask);
395                     if ( value.isEmpty() ) {
396                         registeredListenerSets.remove(source);
397                     }
398                 }
399             }
400         }
401
402         /** Informs pre-change listeners about the given event. Internally the
403          * event is stored together with its pre-change listeners and change
404          * listeners to allow further event processing at transaction rollback
405          * resp. commit time.
406          *
407          * @param current The source object of this event.
408          * @param event Event object.
409          * @exception DebugException if the event was already fired
410          */

411         public void firePlannedChange(Object JavaDoc current, MDRChangeEvent event) {
412             HashSet preChange = new HashSet();
413             HashSet postChange = new HashSet();
414             // collect preChange and postChange listeners
415
collectListeners(current, event, preChange, postChange);
416             if (!(preChange.isEmpty() && postChange.isEmpty())) {
417                 localQueue.addLast(event);
418                 for (Iterator it = preChange.iterator(); it.hasNext();) {
419                     try {
420                         ((MDRPreChangeListener) it.next()).plannedChange(event);
421                     } catch (RuntimeException JavaDoc e) {
422                         if (e instanceof VetoChangeException) {
423                             throw e;
424                         }
425                         Logger.getDefault().notify(Logger.INFORMATIONAL, e);
426                     }
427                 }
428                 if (preChangeListeners.put(event, preChange) != null) {
429                     throw new DebugException("Same event fired twice.");
430                 }
431                 CompositeCollection all = new CompositeCollection();
432                 all.addCollection(preChange);
433                 all.addCollection(postChange);
434                 if (changeListeners.put(event, all) != null) {
435                     throw new DebugException("Same event fired twice.");
436                 }
437             }
438         }
439
440         /**
441          * Collects the listeners for the given <code>event</code> on object
442          * <code>current</code>. This method has to be overwritten by derived
443          * classes to inform listeners on objects to which the events are
444          * propagated. Overwriting methods shall call this method first and
445          * then add any further listeners.
446          *
447          * @param current the object on which the event was fired
448          * @param event the event
449          * @param post if <code>false</code>, listeners implementing
450          * {@link org.netbeans.api.mdr.events.MDRPreChangeListener} are
451          * collected, otherwise listeners implementing only
452          * {@link org.netbeans.api.mdr.events.MDRChangeListener}
453          * @param preChange the set where the pre-change listeners are collected
454          * @param postChange the set where the post-change only listeners are collected
455          */

456         protected void collectListeners(Object JavaDoc current, MDRChangeEvent event, Set preChange, Set postChange) {
457             // fire event on all listeners registered on this object
458
synchronized (registeredListenerSets) {
459                 RegisteredListenerSet value = (RegisteredListenerSet) registeredListenerSets.get(current);
460                 if (value != null) {
461                     value.collectListeners(event, preChange, postChange);
462                 }
463             }
464         }
465         
466         public Collection getListeners(Object JavaDoc source) {
467             synchronized (registeredListenerSets) {
468                 RegisteredListenerSet value = (RegisteredListenerSet) registeredListenerSets.get(source);
469                 if (value != null) {
470                     return new ArrayList(value.map.keySet());
471                 } else {
472                     return Collections.EMPTY_LIST;
473                 }
474             }
475         }
476     }
477     
478     /* -------------------------------------------------------------------- */
479     /* -- EventNotifier.Assocation (inner class, public) ------------------ */
480     /* -------------------------------------------------------------------- */
481     
482     /**
483      * Handles events for associations.
484      */

485     public final class Association extends Abstract {
486         private Association() {
487             super();
488         }
489         
490         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
491             super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_ASSOCIATION, source);
492         }
493         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
494             super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_ASSOCIATION, source);
495         }
496         
497         /**
498          * Adds listeners on the instances participating in the association link
499          * added resp. removed and on the owning package.
500          */

501         protected void collectListeners(Object JavaDoc current, MDRChangeEvent event, Set preChange, Set postChange) {
502             super.collectListeners(current, event, preChange, postChange);
503             
504             // fire event on all listeners registered on instances that were affected
505
if (event instanceof AssociationEvent) {
506                 AssociationEvent assocEvent = (AssociationEvent) event;
507                 if (assocEvent.getFixedElement() != null) INSTANCE.collectListeners(assocEvent.getFixedElement(), event, preChange, postChange);
508                 if (assocEvent.getOldElement() != null) INSTANCE.collectListeners(assocEvent.getOldElement(), event, preChange, postChange);
509                 if (assocEvent.getNewElement() != null) INSTANCE.collectListeners(assocEvent.getNewElement(), event, preChange, postChange);
510             }
511             
512             // fire event on the immediate package extent
513
PACKAGE.collectListeners(((RefAssociation) current).refImmediatePackage(), event, preChange, postChange);
514         }
515     }
516     
517     /* -------------------------------------------------------------------- */
518     /* -- EventNotifier.Clazz (inner class, public) ----------------------- */
519     /* -------------------------------------------------------------------- */
520
521     /**
522      * Handles events for class proxies.
523      */

524     public final class Clazz extends Abstract {
525         private Clazz() {
526             super();
527         }
528         
529         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
530             super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_CLASS, source);
531         }
532         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
533             super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_CLASS, source);
534         }
535         
536         /**
537          * Adds listeners on the owning package.
538          */

539         protected void collectListeners(Object JavaDoc current, MDRChangeEvent event, Set preChange, Set postChange) {
540             super.collectListeners(current, event, preChange, postChange);
541             PACKAGE.collectListeners(((RefClass) current).refImmediatePackage(), event, preChange, postChange);
542         }
543     }
544     
545     /* -------------------------------------------------------------------- */
546     /* -- EventNotifier.Instance (inner class, public) -------------------- */
547     /* -------------------------------------------------------------------- */
548
549     /**
550      * Handles events for instances.
551      */

552     public final class Instance extends Abstract {
553         private Instance() {
554             super();
555         }
556         
557         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
558             super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_INSTANCE, source);
559         }
560         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
561             super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_INSTANCE, source);
562         }
563         
564         /**
565          * Adds listeners on the owning class proxy.
566          */

567         protected void collectListeners(Object JavaDoc current, MDRChangeEvent event, Set preChange, Set postChange) {
568             super.collectListeners(current, event, preChange, postChange);
569             CLASS.collectListeners(((RefObject) current).refClass(), event, preChange, postChange);
570         }
571     }
572     
573     /* -------------------------------------------------------------------- */
574     /* -- EventNotifier.Package (inner class, public) --------------------- */
575     /* -------------------------------------------------------------------- */
576
577     /**
578      * Handles events for packages.
579      */

580     public final class Package extends Abstract {
581         private Package() {
582             super();
583         }
584         
585         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
586             super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_PACKAGE, source);
587         }
588         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
589             super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_PACKAGE, source);
590         }
591         
592         /**
593          * Adds listeners on the owning package resp., if this package is
594          * outermost, on the repository.
595          */

596         protected void collectListeners(Object JavaDoc current, MDRChangeEvent event, Set preChange, Set postChange) {
597             super.collectListeners(current, event, preChange, postChange);
598             RefPackage immediate = ((RefPackage) current).refImmediatePackage();
599             if (immediate != null) {
600                 collectListeners(immediate, event, preChange, postChange);
601             } else {
602                 REPOSITORY.collectListeners(((BaseObjectHandler) current)._getDelegate().getMdrStorage(), event, preChange, postChange);
603             }
604         }
605     }
606     
607     /* -------------------------------------------------------------------- */
608     /* -- EventNotifier.Repository (inner class, public) ------------------ */
609     /* -------------------------------------------------------------------- */
610     
611     /**
612      * Handles events for repositories.
613      */

614     public final class Repository extends Abstract {
615         private Repository() {
616             super();
617         }
618         
619         public void addListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
620             super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_REPOSITORY, source);
621         }
622         public void removeListener(MDRChangeListener listener, int mask, Object JavaDoc source) {
623             super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_REPOSITORY, source);
624         }
625     }
626     
627     /* ===================================================================== */
628     /* == INNER CLASS CONTAINING OPTIMIZED/OPTIMIZABLE DATASTRUCTURES ====== */
629     /* == FOR LISTENER MANAGEMENT ========================================== */
630     /* ===================================================================== */
631     
632     /* -------------------------------------------------------------------- */
633     /* -- EventNotifier.RegisteredListenerSet (inner class) --------------- */
634     /* -------------------------------------------------------------------- */
635     
636     /**
637      * Instances of this class manage listeners registered for change sources.
638      *
639      * <p>[XXX]: This class should be the subject of optimization. Because the
640      * optimization to be used can heavily depend on the application it could
641      * make sense to make this class an interface, to add a factory interface
642      * for <code>RegisteredListenerSet</code>s, to provide a default factory
643      * implementation and to llow applications to register a factory
644      * optimized for their specific purposes. The factory interface should
645      * look like:</p>
646      *
647      * <pre>
648      * public interface RegisteredListenerSetFactory {
649      * public RegisteredListenerSet createInstanceListenerSet(RefObject inst);
650      * public RegisteredListenerSet createClassListenerSet(RefClass cls);
651      * public RegisteredListenerSet createAssociationListenerSet(RefAssociation ass);
652      * public RegisteredListenerSet createPackageListenerSet(RefPackage pckg);
653      * public RegisteredListenerSet createRepositoryListenerSet(MDRepository rep);
654      * }
655      * </pre>
656      */

657     static class RegisteredListenerSet {
658         
659         /**
660          * Maps listeners to event masks.
661          */

662         HashMap map = new HashMap();
663         
664         /**
665          * Adds <code>listener</code> listening on events matching <code>mask</code>.
666          */

667         void addListener(MDRChangeListener listener, int mask) {
668             Integer JavaDoc value = (Integer JavaDoc) map.get(listener);
669             if ( value != null ) {
670                 // create a composite mask, if the listener was already registered
671
int currentMask = value.intValue();
672                 mask |= currentMask;
673                 if ( (mask ^ currentMask) == 0 ) {
674                     // the new mask does not contain any new bit
675
return;
676                 }
677             }
678             map.put(listener, new Integer JavaDoc(mask));
679         }
680         
681         /**
682          * Removes <code>listener</code> for all events.
683          */

684         void removeListener(MDRChangeListener listener) {
685             map.remove(listener);
686         }
687         
688         /**
689          * Removes <code>listener</code> for events matching <code>mask</code>.
690          */

691         void removeListener(MDRChangeListener listener, int mask) {
692             Object JavaDoc value = map.get(listener);
693             if ( value != null ) {
694                 // create a reduced mask, if the listener was already registered
695
int currentMask = ((Integer JavaDoc) value).intValue();
696                 mask = currentMask & (~mask);
697                 if (mask == 0) {
698                     // the resulting mask is empty
699
removeListener(listener);
700                 } else {
701                     // the resulting mask still contains some bits
702
map.put(listener, new Integer JavaDoc(mask));
703                 }
704             }
705         }
706         
707         /**
708          * Adds the listeners which are registered for events of type
709          * <code>event</code> to <code>preChange</code> resp. <code>postChange</code>.
710          *
711          * @param event the event the listeners are collected for
712          * @param preChange the set where the pre-change listeners are collected
713          * @param postChange the set where the post-change only listeners are collected
714          */

715         void collectListeners(MDRChangeEvent event, Set preChange, Set postChange) {
716             Iterator it = map.entrySet().iterator();
717
718             while ( it.hasNext() ) {
719                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
720                 if ( event.isOfType(((Integer JavaDoc)entry.getValue()).intValue()) ) {
721                     Object JavaDoc key = entry.getKey();
722                     if ( key instanceof MDRPreChangeListener ) {
723                         preChange.add(key);
724                     } else {
725                         postChange.add(key);
726                     }
727                 }
728             }
729         }
730         
731         /**
732          * Returns <code>true</code> if the listener set is empty,
733          * <code>false</code> otherwise.
734          */

735         boolean isEmpty() {
736             return map.isEmpty();
737         }
738     }
739 }
740
Popular Tags