package com.kartoflane.superluminal2.components; import com.kartoflane.superluminal2.events.SLEvent; import com.kartoflane.superluminal2.events.SLListener; /** * A copy of SWT's own {@link org.eclipse.swt.widgets.EventTable EventTable}, with the addition of comments. */ public class EventHandler { private static final int growSize = 4; // Lazily instantiated arrays private int[] types; private SLListener[] listeners; /** * Helper variable to detect when a listener that has been informed of an event has unregistered * itself from the handler as a result of the event. * This allows the handler to clean up the removed listeners without messing up the sendEvent loop. */ private int level; public EventHandler() { } public SLListener[] getListeners(int eventType) { if (types == null) return new SLListener[0]; int count = 0; for (int i = 0; i < types.length; i++) { if (types[i] == eventType) count++; } if (count == 0) return new SLListener[0]; SLListener[] result = new SLListener[count]; count = 0; for (int i = 0; i < types.length; i++) { if (types[i] == eventType) { result[count++] = listeners[i]; } } return result; } /** * Registers the listener with the handler, listening for the specified event. * * @param eventType * id of the event type the listener is interested in being notified about * @param listener * the listener that will be notified when an event of the specified type is sent */ public void hook(int eventType, SLListener listener) { if (types == null) types = new int[growSize]; if (listeners == null) listeners = new SLListener[growSize]; int length = types.length; int index = length - 1; while (index >= 0) { if (types[index] != 0) break; --index; } index++; if (index == length) { int[] newTypes = new int[length + growSize]; System.arraycopy(types, 0, newTypes, 0, length); types = newTypes; SLListener[] newListeners = new SLListener[length + growSize]; System.arraycopy(listeners, 0, newListeners, 0, length); listeners = newListeners; } types[index] = eventType; listeners[index] = listener; } /** * @return true if the handler has at least one listener listening for this event, false otherwise. */ public boolean hooks(int eventType) { if (types == null) return false; for (int i = 0; i < types.length; i++) { if (types[i] == eventType) return true; } return false; } /** * Sends the event, notifying all interested listeners. * * @param event * the event to be sent */ public void sendEvent(SLEvent event) { if (types == null) return; level += level >= 0 ? 1 : -1; try { for (int i = 0; i < types.length; i++) { if (event.type == SLEvent.NONE) return; if (types[i] == event.type) { SLListener listener = listeners[i]; if (listener != null) listener.handleEvent(event); } } } finally { // If a listener unregisters itself from the handler as a result of the event, // then the level variable will become negative, triggering the cleanup boolean compact = level < 0; level -= level >= 0 ? 1 : -1; if (compact && level == 0) { int index = 0; // Remove null type events for (int i = 0; i < types.length; i++) { if (types[i] != 0) { types[index] = types[i]; listeners[index] = listeners[i]; index++; } } // Nullify the tail of the array, since it was shrunk for (int i = index; i < types.length; i++) { types[i] = 0; listeners[i] = null; } } } } /** * @return the number of listeners currently registered with the handler */ public int size() { if (types == null) return 0; int count = 0; for (int i = 0; i < types.length; i++) { if (types[i] != 0) count++; } return count; } /** * Unregisters the listener under the specified index from the handler. * * @param index * index to be removed */ private void remove(int index) { // If a listener unregisters itself from the handler as a result of an event, then shifting // the array will mess up the sending loop. If that is the case, only nullify the index to // be removed, and negate the level variable -- sendEvent() will shift the array on its own // after it's done sending events. Otherwise, proceed normally and shift the array here. if (level == 0) { int end = types.length - 1; // Shift all array contents one index to the left System.arraycopy(types, index + 1, types, index, end - index); System.arraycopy(listeners, index + 1, listeners, index, end - index); index = end; } else { if (level > 0) level = -level; } // Either nullifies the tail of the array in case it was shifted to the left, // or removes the event-listener pair at the given index types[index] = 0; listeners[index] = null; } /** * Unregisters the event-listener pair from the handler. * * @param eventType * type of the event to be removed * @param listener * listener to be removed */ public void unhook(int eventType, SLListener listener) { if (types == null) return; for (int i = 0; i < types.length; i++) { if (types[i] == eventType && listeners[i] == listener) { remove(i); break; } } } /** * Completely unregisters the listener from the event handler, essentially forgetting it. * * @param listener * listener to be removed */ public void unhook(SLListener listener) { if (listeners == null) return; for (int i = 0; i < listeners.length; i++) { if (listeners[i] == listener) remove(i); } } /** * Nullifies all arrays. */ public void dispose() { if (types == null) return; for (int i = 0; i < types.length; i++) { types[i] = 0; listeners[i] = null; } types = null; listeners = null; } }