/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.mdr.util; import org.netbeans.api.mdr.events.*; import org.netbeans.mdr.handlers.BaseObjectHandler; import org.netbeans.mdr.storagemodel.CompositeCollection; import java.util.*; import javax.jmi.reflect.*; /** * Utility class for the management of listener notifications during and * after transactions. * * @author mmatula * @author "; } /** * Pretty prints a event type mask. * *

[XXX]: Probably this method should be used to a test utility * class ?!

*/ public static String prettyPrintMask(int mask) { StringBuffer buf = new StringBuffer(); if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_ADD) == AttributeEvent.EVENT_ATTRIBUTE_ADD ) buf.append("IAA "); if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_REMOVE) == AttributeEvent.EVENT_ATTRIBUTE_REMOVE ) buf.append("IAR "); if ( (mask & AttributeEvent.EVENT_ATTRIBUTE_SET) == AttributeEvent.EVENT_ATTRIBUTE_SET ) buf.append("IAS "); if ( (mask & AttributeEvent.EVENT_CLASSATTR_ADD) == AttributeEvent.EVENT_CLASSATTR_ADD ) buf.append("CAA "); if ( (mask & AttributeEvent.EVENT_CLASSATTR_REMOVE) == AttributeEvent.EVENT_CLASSATTR_REMOVE ) buf.append("CAR "); if ( (mask & AttributeEvent.EVENT_CLASSATTR_SET) == AttributeEvent.EVENT_CLASSATTR_SET ) buf.append("CAS "); if ( (mask & InstanceEvent.EVENT_INSTANCE_CREATE) == InstanceEvent.EVENT_INSTANCE_CREATE ) buf.append("IC "); if ( (mask & InstanceEvent.EVENT_INSTANCE_DELETE) == InstanceEvent.EVENT_INSTANCE_DELETE ) buf.append("ID "); if ( (mask & AssociationEvent.EVENT_ASSOCIATION_ADD) == AssociationEvent.EVENT_ASSOCIATION_ADD ) buf.append("AA "); if ( (mask & AssociationEvent.EVENT_ASSOCIATION_REMOVE) == AssociationEvent.EVENT_ASSOCIATION_REMOVE ) buf.append("AR "); if ( (mask & AssociationEvent.EVENT_ASSOCIATION_SET) == AssociationEvent.EVENT_ASSOCIATION_SET ) buf.append("AS "); if ( (mask & ExtentEvent.EVENT_EXTENT_CREATE) == ExtentEvent.EVENT_EXTENT_CREATE ) buf.append("EC "); if ( (mask & ExtentEvent.EVENT_EXTENT_DELETE) == ExtentEvent.EVENT_EXTENT_DELETE ) buf.append("ED "); if ( (mask & TransactionEvent.EVENT_TRANSACTION_COMMIT) == TransactionEvent.EVENT_TRANSACTION_COMMIT ) buf.append("TC "); if ( (mask & TransactionEvent.EVENT_TRANSACTION_ROLLBACK) == TransactionEvent.EVENT_TRANSACTION_ROLLBACK ) buf.append("TR "); if ( (mask & TransactionEvent.EVENT_TRANSACTION_START) == TransactionEvent.EVENT_TRANSACTION_START ) buf.append("TS "); if ( buf.length() > 0 ) buf.deleteCharAt(buf.length()-1); return buf.toString(); } /* -------------------------------------------------------------------- */ /* -- Public attributes ----------------------------------------------- */ /* -------------------------------------------------------------------- */ public final Association ASSOCIATION = new Association(); public final Clazz CLASS = new Clazz(); public final Instance INSTANCE = new Instance(); public final Package PACKAGE = new Package(); public final Repository REPOSITORY = new Repository(); /* -------------------------------------------------------------------- */ /* -- Private attributes ---------------------------------------------- */ /* -------------------------------------------------------------------- */ /** * Thread for the dispatching of events after transaction success. */ private final Thread dispatcher = new Thread(new EventsDelivery()); /** * Maps: event => instance of QueuedListenerSet. */ private final Hashtable queuedListenerMap = new Hashtable(); /** * Queue for the events of the currently running write transaction. * The events are stored to inform their listeners either about * rollback or success. */ private final LinkedList localQueue = new LinkedList(); /** * Queue for the events of successfully finished transactions, the * listeners of which still have to be informed. */ private final LinkedList globalQueue = new LinkedList(); /* -------------------------------------------------------------------- */ /* -- Constructor (public) -------------------------------------------- */ /* -------------------------------------------------------------------- */ /** * Starts the dispatcher thread as daemon. */ public EventNotifier() { dispatcher.setDaemon(true); dispatcher.start(); } /* -------------------------------------------------------------------- */ /* -- Methods to fire events at transaction commit or rollback time --- */ /* -------------------------------------------------------------------- */ /** * Calls {@link MDRPreChangeListener.changeCancelled(MDRChangeEvent)} for * all pre-change listeners on event to inform them * about cancelling the event. */ public void fireCancelled(MDRChangeEvent event) { localQueue.remove(event); QueuedListenerSet collected = (QueuedListenerSet) queuedListenerMap.remove(event); if (collected == null) { System.err.println("Change cancelled event not corresponding to any planned change event."); Thread.dumpStack(); return; } // fire changeCancelled event for (QueuedListenerSet.PreListenerIterator it = collected.preListenerIterator(); it.hasNext();) { try { it.next().changeCancelled(event); } catch (RuntimeException e) { // log the exception e.printStackTrace(); } } } /** * Calls {@link MDRPreChangeListener.changeCancelled(MDRChangeEvent)} * on all pre-change listeners of all events of the transaction to be * rolled back. */ public void fireCancelled() { while (!localQueue.isEmpty()) { MDRChangeEvent event = (MDRChangeEvent) localQueue.getFirst(); fireCancelled(event); } } /** * Enqueues all events of a transaction successfully finished to inform * the listeners in a separate thread. */ public void fireChanged() { synchronized (globalQueue) { globalQueue.addAll(localQueue); globalQueue.notify(); } localQueue.clear(); } /* -------------------------------------------------------------------- */ /* -- EventNotifier.EventDelivery (inner class, private) -------------- */ /* -------------------------------------------------------------------- */ /** * Runnable to be executed by the event dispatcher thread. Waits on the * global queue if it is empty. Informs the listeners on queued events. * 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}. */ private class EventsDelivery implements Runnable { /** * */ public void run() { QueuedListenerSet collected; MDRChangeEvent event; while (true) { synchronized (globalQueue) { while (globalQueue.isEmpty()) { try { globalQueue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } collected = (QueuedListenerSet) queuedListenerMap.remove(event = (MDRChangeEvent) globalQueue.removeFirst()); } if (collected == null) { System.err.println("Change event not corresponding to any planned change event."); Thread.dumpStack(); return; } for (QueuedListenerSet.AllListenerIterator it = collected.allListenerIterator(); it.hasNext();) { try { it.next().change(event); } catch (RuntimeException e) { // log the exception e.printStackTrace(); } } } } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Abstract (inner class, public abstract) ----------- */ /* -------------------------------------------------------------------- */ /** * This class and its derived classes are used to enqueue all changes into * the local queue and to fire the pre-change events. */ public abstract class Abstract { /** * Maps repository objects to instances of RegisteredListenerSet. */ private final HashMap RegisteredListenerSets = new HashMap(); /** * Adds listener to source for event types * matching mask. */ public void addListener(MDRChangeListener listener, int mask, Object source) { //System.out.println("-- CALLED: addListener on " + source + " with " + prettyPrintMask(mask)); if ( (listener == null) || (mask == 0) ) return; synchronized (RegisteredListenerSets) { RegisteredListenerSet value = (RegisteredListenerSet) RegisteredListenerSets.get(source); if ( value == null ) { value = new RegisteredListenerSet(); RegisteredListenerSets.put(source, value); } value.addListener(listener, mask); } } /** * Removes listener from source for all event types. */ public void removeListener(MDRChangeListener listener, Object source) { if ( listener == null ) return; synchronized (RegisteredListenerSets) { RegisteredListenerSet value = (RegisteredListenerSet) RegisteredListenerSets.get(source); if ( value != null ) { value.removeListener(listener); if ( value.isEmpty() ) { RegisteredListenerSets.remove(source); } } } } /** * Removes listener from source for event types * matching mask. */ public void removeListener(MDRChangeListener listener, int mask, Object source) { if ( (listener == null) || (mask == 0) ) return; synchronized (RegisteredListenerSets) { RegisteredListenerSet value = (RegisteredListenerSet) RegisteredListenerSets.get(source); if ( value != null ) { value.removeListener(listener, mask); if ( value.isEmpty() ) { RegisteredListenerSets.remove(source); } } } } /** Informs pre-change listeners about the given event. Internally the * event is stored together with its pre-change listeners and change * listeners to allow further event processing at transaction rollback * resp. commit time. * * @param current The source object of this event. * @param event Event object. * @exception DebugException if the event was already fired */ public void firePlannedChange(Object current, MDRChangeEvent event) { //System.out.println("-- CALLED: firePlannedChange on " + current + " with type " + prettyPrintType(event)); localQueue.addLast(event); // collect all listeners QueuedListenerSet queuedListeners = new QueuedListenerSet(event); collectListeners(current, event, queuedListeners); for (QueuedListenerSet.PreListenerIterator it = queuedListeners.preListenerIterator(); it.hasNext();) { try { it.next().plannedChange(event); } catch (RuntimeException e) { // log the exception e.printStackTrace(); } } if (queuedListenerMap.put(event,queuedListeners) != null) { throw new DebugException("Same event fired twice."); } } /** * Collects the listeners for the given event on object * current. This method has to be overwritten by derived * classes to inform listeners on objects to which the events are * propagated. Overwriting methods shall call this method first and * 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 preChange the set where the pre-change listeners are collected * @param postChange the set where the post-change only listeners are collected */ protected void collectListeners(Object current, MDRChangeEvent event, QueuedListenerSet queuedListeners) { // fire event on all listeners registered on this object synchronized (RegisteredListenerSets) { RegisteredListenerSet value = (RegisteredListenerSet) RegisteredListenerSets.get(current); if (value != null) { value.collectListeners(queuedListeners); } } } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Assocation (inner class, public) ------------------ */ /* -------------------------------------------------------------------- */ /** * Handles events for associations. */ public final class Association extends Abstract { private Association() { super(); } public void addListener(MDRChangeListener listener, int mask, Object source) { super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_ASSOCIATION, source); } public void removeListener(MDRChangeListener listener, int mask, Object source) { super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_ASSOCIATION, source); } /** * Adds listeners on the instances participating in the association link * added resp. removed and on the owning package. */ protected void collectListeners(Object current, MDRChangeEvent event, QueuedListenerSet queuedListeners) { super.collectListeners(current, event, queuedListeners); // 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, queuedListeners); if (assocEvent.getOldElement() != null) INSTANCE.collectListeners(assocEvent.getOldElement(), event, queuedListeners); if (assocEvent.getNewElement() != null) INSTANCE.collectListeners(assocEvent.getNewElement(), event, queuedListeners); } // fire event on the immediate package extent PACKAGE.collectListeners(((RefAssociation) current).refImmediatePackage(), event, queuedListeners); } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Clazz (inner class, public) ----------------------- */ /* -------------------------------------------------------------------- */ /** * Handles events for class proxies. */ public final class Clazz extends Abstract { private Clazz() { super(); } public void addListener(MDRChangeListener listener, int mask, Object source) { super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_CLASS, source); } public void removeListener(MDRChangeListener listener, int mask, Object source) { super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_CLASS, source); } /** * Adds listeners on the owning package. */ protected void collectListeners(Object current, MDRChangeEvent event, QueuedListenerSet queuedListeners) { super.collectListeners(current, event, queuedListeners); PACKAGE.collectListeners(((RefClass) current).refImmediatePackage(), event, queuedListeners); } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Instance (inner class, public) -------------------- */ /* -------------------------------------------------------------------- */ /** * Handles events for instances. */ public final class Instance extends Abstract { private Instance() { super(); } public void addListener(MDRChangeListener listener, int mask, Object source) { super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_INSTANCE, source); } public void removeListener(MDRChangeListener listener, int mask, Object source) { super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_INSTANCE, source); } /** * Adds listeners on the owning class proxy. */ protected void collectListeners(Object current, MDRChangeEvent event, QueuedListenerSet queuedListeners) { super.collectListeners(current, event, queuedListeners); CLASS.collectListeners(((RefObject) current).refClass(), event, queuedListeners); } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Package (inner class, public) --------------------- */ /* -------------------------------------------------------------------- */ /** * Handles events for packages. */ public final class Package extends Abstract { private Package() { super(); } public void addListener(MDRChangeListener listener, int mask, Object source) { super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_PACKAGE, source); } public void removeListener(MDRChangeListener listener, int mask, Object source) { super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_PACKAGE, source); } /** * Adds listeners on the owning package resp., if this package is * outermost, on the repository. */ protected void collectListeners(Object current, MDRChangeEvent event, QueuedListenerSet queuedListeners) { super.collectListeners(current, event, queuedListeners); RefPackage immediate = ((RefPackage) current).refImmediatePackage(); if (immediate != null) { collectListeners(immediate, event, queuedListeners); } else { REPOSITORY.collectListeners(((BaseObjectHandler) current)._getDelegate().getMdrStorage(), event, queuedListeners); } } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.Repository (inner class, public) ------------------ */ /* -------------------------------------------------------------------- */ /** * Handles events for repositories. */ public final class Repository extends Abstract { private Repository() { super(); } public void addListener(MDRChangeListener listener, int mask, Object source) { super.addListener(listener, mask & EventNotifier.EVENTMASK_BY_REPOSITORY, source); } public void removeListener(MDRChangeListener listener, int mask, Object source) { super.removeListener(listener, mask & EventNotifier.EVENTMASK_BY_REPOSITORY, source); } } /* ===================================================================== */ /* == INNER CLASS CONTAINING OPTIMIZED/OPTIMIZABLE DATASTRUCTURES ====== */ /* == FOR LISTENER MANAGEMENT ========================================== */ /* ===================================================================== */ /* -------------------------------------------------------------------- */ /* -- EventNotifier.AbstractListenerSet (inner class) ----------------- */ /* -------------------------------------------------------------------- */ static abstract class AbstractListenerSet { protected static final int NOT_FOUND = -1; MDRChangeListenerData[] listenerData = null; MDRPreChangeListenerData[] preListenerData = null; int listenerCnt = 0; int preListenerCnt = 0; /** * Ensures that at least moreListenerCnt listeners may be * added to listenerData. * * @param moreListenerCnt number of listeners to be added * @param enhanceByListenerCnt if the array is to be changed its * capacity should be enhanced by at least * enhanceByListenerCnt (its is required that * enhanceByListenerCnt > moreListenerCnt) * @param minListenerCnt if the array is to be created its capacity * should be at least minListenerCnt * @return listenerCnt + moreListenerCnt * */ protected int ensureListenerCapacity(int moreListenerCnt, int enhanceByListenerCnt, int minListenerCnt) { //System.out.println("\tAbstractListenerSet.ensureListenerCapacity(int moreListenerCnt, int enhanceByListenerCnt, int minListenerCnt)"); int newListenerCnt = listenerCnt + moreListenerCnt; if ( listenerData == null ) { listenerData = new MDRChangeListenerData[Math.max(enhanceByListenerCnt,minListenerCnt)]; } else if ( newListenerCnt > listenerData.length ) { MDRChangeListenerData[] oldListenerData = listenerData; listenerData = new MDRChangeListenerData[listenerCnt+enhanceByListenerCnt]; System.arraycopy(oldListenerData, 0, listenerData, 0, listenerCnt); } return newListenerCnt; } /** * Ensures that at least morePreListenerCnt pre-change * listeners may be added to preListenerData. * * @param morePreListenerCnt number of listeners to be added * @param enhanceByPreListenerCnt if the array is to be changed its * capacity should be enhanced by at least * enhanceByPreListenerCnt (its is required that * enhanceByPreListenerCnt > morePreListenerCnt) * @param minPreListenerCnt if the array is to be created its capacity * should be at least minPreListenerCnt * @return preListenerCnt + morePreListenerCnt * */ protected int ensurePreListenerCapacity(int morePreListenerCnt, int enhanceByPreListenerCnt, int minPreListenerCnt) { //System.out.println("\tAbstractListenerSet.ensurePreListenerCapacity(int morePreListenerCnt, int enhanceByPreListenerCnt, int minPreListenerCnt)"); int newPreListenerCnt = preListenerCnt + morePreListenerCnt; if ( preListenerData == null ) { preListenerData = new MDRPreChangeListenerData[Math.max(enhanceByPreListenerCnt,minPreListenerCnt)]; } else if ( newPreListenerCnt > preListenerData.length ) { MDRPreChangeListenerData[] oldPreListenerData = preListenerData; preListenerData = new MDRPreChangeListenerData[preListenerCnt+enhanceByPreListenerCnt]; System.arraycopy(oldPreListenerData, 0, preListenerData, 0, preListenerCnt); } return newPreListenerCnt; } /** * Returns the index of the listener data for the given listener * or {@link #NOT_FOUND}. */ protected int findListener(MDRChangeListener listener) { //System.out.println("\tAbstractListenerSet.findListener(MDRChangeListener listener)"); for ( int i=0; iObject.equals(Object) here ? if ( listenerData[i].listener == listener ) return i; } return NOT_FOUND; } /** * Returns the index of the pre-change listener data for the given * pre-change listener or {@link #NOT_FOUND}. */ protected int findPreListener(MDRPreChangeListener preListener) { //System.out.println("\tAbstractListenerSet.findListener(MDRPreChangeListener preListener)"); for ( int i=0; iObject.equals(Object) here ? if ( preListenerData[i].listener == preListener ) return i; } return NOT_FOUND; } /** * Removes the listener and trims the listener array. * * @param idx a valid listener index (no check is made). * @param minTrimCount trimming must at least yield a * reduction of array size of minTrimCount entries, * otherwise no trimming is made * @param freeSize the free size of the listener array after * triming (note: the listener array is set to null * if it is empty) */ protected void removeListener(int idx, int minTrimCount, int freeSize) { //System.out.println("\tAbstractListenerSet.removeListener(int idx, int minTrimCount, int freeSize)"); if ( listenerCnt-- == 0 ) { listenerData = null; } else { if ( listenerData.length - (listenerCnt + freeSize) >= minTrimCount ) { // trimming MDRChangeListenerData[] newListenerData = new MDRChangeListenerData[listenerCnt + freeSize]; if ( idx > 0 ) System.arraycopy(listenerData, 0, newListenerData, 0, idx); int numMoved = listenerCnt - idx; if ( numMoved > 0 ) System.arraycopy(listenerData, idx+1, newListenerData, idx, numMoved); listenerData = newListenerData; } else { // no trimming int numMoved = listenerCnt - idx; if ( numMoved > 0 ) System.arraycopy(listenerData, idx+1, listenerData, idx, numMoved); listenerData[listenerCnt] = null; } } } /** * Removes the pre-change listener and trims the pre-change listener * array. * * @param idx a valid pre-change listener index (no check is made). * @param minTrimCount trimming must at least yield a * reduction of array size of minTrimCount entries, * otherwise no trimming is made * @param freeSize the free size of the pre-change listener array after * triming (note: the pre-change listener array is set to * null if it is empty) */ protected void removePreListener(int idx, int minTrimCount, int freeSize) { //System.out.println("\tAbstractListenerSet.removePreListener(int idx, int minTrimCount, int freeSize)"); if ( listenerCnt-- == 0 ) { listenerData = null; } else { if ( listenerData.length - (listenerCnt + freeSize) >= minTrimCount ) { // trimming MDRChangeListenerData[] newListenerData = new MDRChangeListenerData[listenerCnt + freeSize]; if ( idx > 0 ) System.arraycopy(listenerData, 0, newListenerData, 0, idx); int numMoved = listenerCnt - idx; if ( numMoved > 0 ) System.arraycopy(listenerData, idx+1, newListenerData, idx, numMoved); listenerData = newListenerData; } else { // no trimming int numMoved = listenerCnt - idx; if ( numMoved > 0 ) System.arraycopy(listenerData, idx+1, listenerData, idx, numMoved); listenerData[listenerCnt] = null; } } } /** * Returns true if the listener set is empty, * false otherwise. */ public boolean isEmpty() { //System.out.println("\tAbstractListenerSet.isEmpty()"); return listenerCnt + preListenerCnt == 0; } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.RegisteredListenerSet (inner class) --------------- */ /* -------------------------------------------------------------------- */ /** * Instances of this class manage listeners registered for change sources. * *

[XXX]: This class should be the subject of optimization. Because the * optimization to be used can heavily depend on the application it could * make sense to make this class an interface, to add a factory interface * for RegisteredListenerSets, to provide a default factory * implementation and to llow applications to register a factory * optimized for their specific purposes. The factory interface should * look like:

* *
   *  public interface RegisteredListenerSetFactory {
   *    public RegisteredListenerSet createInstanceListenerSet(RefObject inst);
   *    public RegisteredListenerSet createClassListenerSet(RefClass cls);
   *    public RegisteredListenerSet createAssociationListenerSet(RefAssociation ass);
   *    public RegisteredListenerSet createPackageListenerSet(RefPackage pckg);
   *    public RegisteredListenerSet createRepositoryListenerSet(MDRepository rep);
   *  }
   *  
*/ static class RegisteredListenerSet extends AbstractListenerSet { /** * Adds listener listening on events matching mask. * * @param listener a post-change or pre-change listener * @param mask a valid mask != 0 */ public void addListener(MDRChangeListener listener, int mask) { //System.out.println("\tRegisteredListenerSet.addListener(MDRChangeListener listener, int mask)"); if ( listener instanceof MDRPreChangeListener ) { MDRPreChangeListener preListener = (MDRPreChangeListener) listener; int idx = findPreListener(preListener); if ( idx == NOT_FOUND) { ensurePreListenerCapacity(1, 3, 3); preListenerData[preListenerCnt++] = new MDRPreChangeListenerData(preListener, mask); } else { preListenerData[idx].mask |= mask; } } else { int idx = findListener(listener); if ( idx == NOT_FOUND ) { ensureListenerCapacity(1, 3, 3); listenerData[listenerCnt++] = new MDRChangeListenerData(listener, mask); } else { listenerData[idx].mask |= mask; } } } /** * Removes listener for all events. */ public void removeListener(MDRChangeListener listener) { //System.out.println("\tRegisteredListenerSet.removeListener(MDRChangeListener listener)"); if ( listener instanceof MDRPreChangeListener ) { MDRPreChangeListener preListener = (MDRPreChangeListener) listener; int idx = findPreListener(preListener); if ( idx != NOT_FOUND) { removePreListener(idx, 5, 3); } } else { int idx = findListener(listener); if ( idx != NOT_FOUND ) { removeListener(idx, 5, 3); } } } /** * Removes listener for events matching mask. */ public void removeListener(MDRChangeListener listener, int mask) { //System.out.println("\tRegisteredListenerSet.removeListener(MDRChangeListener listener, int mask)"); if ( listener instanceof MDRPreChangeListener ) { MDRPreChangeListener preListener = (MDRPreChangeListener) listener; int idx = findPreListener(preListener); if ( (idx != NOT_FOUND) && ( (preListenerData[idx].mask &= (~mask)) == 0 ) ) { removePreListener(idx, 5, 3); } } else { int idx = findListener(listener); if ( (idx != NOT_FOUND) && ( (listenerData[idx].mask &= (~mask)) == 0 ) ) { removeListener(idx, 5, 3); } } } /** * Adds the listeners which are registered for events of type * event to preChange resp. postChange. * * @param queuedListener the set of queued listeners to be filled */ void collectListeners(QueuedListenerSet queuedListeners) { //System.out.println("\tRegisteredListenerSet.collectListeners(QueuedListenerSet queuedListeners)"); if ( listenerData != null ) queuedListeners.addListenerData(listenerData, listenerCnt, 1); if ( preListenerData != null ) queuedListeners.addPreListenerData(preListenerData, preListenerCnt, 1); } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.QueuedListenerSet (inner class) ------------------- */ /* -------------------------------------------------------------------- */ /** * Instances of this class manage listeners queued for execution. * *

[XXX]: This class should be the subject of optimization.

*/ static class QueuedListenerSet extends AbstractListenerSet { int type; /** * @param type the type of the event the listeners are enqueued for */ public QueuedListenerSet(int type) { //System.out.println("\tQueuedListenerSet(int type)"); this.type = type; } /** * @param event the event the listeners are enqueued for */ public QueuedListenerSet(MDRChangeEvent event) { //System.out.println("\tQueuedListenerSet(MDRChangeEvent event)"); this.type = event.getType(); } /** * @param newListenerData data to be added (not null) * @param cnt the number of entries of newListenerData to be added * @param factor the expected number of further data to be added after * this method call is newListenerData.length * factor */ public void addListenerData(MDRChangeListenerData[] newListenerData, int cnt, int factor) { //System.out.println("\tQueuedListenerSet.addListenerData(MDRChangeListenerData[] newListenerData, int factor)"); int newListenerCnt = ensureListenerCapacity(cnt, cnt*factor, 5); System.arraycopy(newListenerData, 0, listenerData, listenerCnt, cnt); listenerCnt = newListenerCnt; } /** * @param newPreListenerData data to be added (not null) * @param cnt the number of entries of newListenerData to be added * @param factor the expected number of further data to be added after * this method call is newPreListenerData.length * factor */ public void addPreListenerData(MDRPreChangeListenerData[] newPreListenerData, int cnt, int factor) { //System.out.println("\tQueuedListenerSet.addPreListenerData(MDRPreChangeListenerData[] newPreListenerData, int factor)"); int newPreListenerCnt = ensurePreListenerCapacity(cnt, cnt*factor, 5); System.arraycopy(newPreListenerData, 0, preListenerData, preListenerCnt, cnt); preListenerCnt = newPreListenerCnt; } public AllListenerIterator allListenerIterator() { //System.out.println("\tQueuedListenerSet.allListenerIterator()"); return new AllListenerIterator(); } public PreListenerIterator preListenerIterator() { //System.out.println("\tQueuedListenerSet.preListenerIterator()"); return new PreListenerIterator(); } public class AllListenerIterator { private static final int INITIAL = -1; private static final int EXHAUSTED = -2; HashSet alreadyMet = new HashSet(); int listenerIdx = INITIAL; int preListenerIdx = INITIAL; MDRChangeListener next; AllListenerIterator() { //System.out.println("\tAllListenerIterator()"); if ( listenerData == null ) listenerIdx = EXHAUSTED; if ( preListenerData == null ) preListenerIdx = EXHAUSTED; fetchNext(); } private void fetchNext() { //System.out.println("\tAllListenerIterator.fetchNext()"); if ( listenerIdx != EXHAUSTED ) { while ( ++listenerIdx < listenerCnt ) { MDRChangeListenerData data = listenerData[listenerIdx]; next = data.listener; if ( (type & data.mask) == type && alreadyMet.add(next)) return; } listenerIdx = EXHAUSTED; } if ( preListenerIdx != EXHAUSTED ) { while ( ++preListenerIdx < preListenerCnt ) { MDRPreChangeListenerData data = preListenerData[preListenerIdx]; next = data.listener; if ( (type & data.mask) == type && alreadyMet.add(next)) return; } preListenerIdx = EXHAUSTED; } next = null; } public boolean hasNext() { //System.out.println("\tAllListenerIterator.hasNext()"); return next != null; } public MDRChangeListener next() { //System.out.println("\tAllListenerIterator.next()"); MDRChangeListener res = next; fetchNext(); return res; } } public class PreListenerIterator { private static final int INITIAL = -1; private static final int EXHAUSTED = -2; HashSet alreadyMet = new HashSet(); int idx = INITIAL; MDRPreChangeListener next; PreListenerIterator() { //System.out.println("\tPreListenerIterator()"); if ( preListenerData == null ) idx = EXHAUSTED; fetchNext(); } private void fetchNext() { //System.out.println("\tPreListenerIterator.fetchNext()"); if ( idx != EXHAUSTED ) { while ( ++idx < preListenerCnt ) { MDRPreChangeListenerData data = preListenerData[idx]; next = data.listener; if ( (type & data.mask) == type && alreadyMet.add(next)) return; } idx = EXHAUSTED; } next = null; } public boolean hasNext() { //System.out.println("\tPreListenerIterator.hasNext()"); return next != null; } public MDRPreChangeListener next() { //System.out.println("\tPreListenerIterator.next()"); MDRPreChangeListener res = next; fetchNext(); return res; } } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.MDRChangeListenerData (inner class) --------------- */ /* -------------------------------------------------------------------- */ static class MDRChangeListenerData { MDRChangeListener listener; int mask; MDRChangeListenerData(MDRChangeListener listener, int mask) { this.listener = listener; this.mask = mask; } } /* -------------------------------------------------------------------- */ /* -- EventNotifier.MDRPreChangeListener (inner class) ---------------- */ /* -------------------------------------------------------------------- */ static class MDRPreChangeListenerData { MDRPreChangeListener listener; int mask; MDRPreChangeListenerData(MDRPreChangeListener listener, int mask) { this.listener = listener; this.mask = mask; } } }