/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.dragome.forms.bindings.extra.event.shared; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import com.dragome.forms.bindings.extra.core.client.Scheduler.ScheduledCommand; import com.dragome.model.interfaces.EventHandler; import com.dragome.model.interfaces.GwtEvent; import com.dragome.model.interfaces.GwtEvent.Type; import com.dragome.model.interfaces.HandlerRegistration; /** * Basic implementation of {@link EventBus}. */ public class SimpleEventBus extends EventBus { private final boolean isReverseOrder; private int firingDepth= 0; /** * Add and remove operations received during dispatch. */ private List<ScheduledCommand> deferredDeltas; /** * Map of event type to map of event source to list of their handlers. */ private final Map<GwtEvent.Type<?>, Map<Object, List<?>>> map= new HashMap<GwtEvent.Type<?>, Map<Object, List<?>>>(); public SimpleEventBus() { this(false); } /** * Allows creation of an instance that fires its handlers in the reverse of * the order in which they were added, although filtered handlers all fire * before unfiltered handlers. * <p> * * @deprecated This is a legacy feature, required by HandlerManager. Package * protected because it is a bad idea to rely upon the order of * event dispatch, and because fully supporting it (that is, not * segregating filtered and unfiltered handlers, a distinction not * used by HandlerManager) is not worth the effort. */ @Deprecated SimpleEventBus(boolean fireInReverseOrder) { isReverseOrder= fireInReverseOrder; } @Override public <H extends EventHandler> HandlerRegistration addHandler(Type<H> type, H handler) { if (type == null) { throw new NullPointerException("Cannot add a handler with a null type"); } if (handler == null) { throw new NullPointerException("Cannot add a null handler"); } return doAdd(type, null, handler); } @Override public <H extends EventHandler> HandlerRegistration addHandlerToSource(final GwtEvent.Type<H> type, final Object source, final H handler) { if (type == null) { throw new NullPointerException("Cannot add a handler with a null type"); } if (source == null) { throw new NullPointerException("Cannot add a handler with a null source"); } if (handler == null) { throw new NullPointerException("Cannot add a null handler"); } return doAdd(type, source, handler); } @Override public void fireEvent(GwtEvent<?> event) { if (event == null) { throw new NullPointerException("Cannot fire null event"); } doFire(event, null); } @Override public void fireEventFromSource(GwtEvent<?> event, Object source) { if (event == null) { throw new NullPointerException("Cannot fire null event"); } if (source == null) { throw new NullPointerException("Cannot fire from a null source"); } doFire(event, source); } /** * Package protected to support legacy features in HandlerManager. */ <H extends EventHandler> void doRemove(GwtEvent.Type<H> type, Object source, H handler) { if (firingDepth > 0) { enqueueRemove(type, source, handler); } else { doRemoveNow(type, source, handler); } } /** * Package protected to support legacy features in HandlerManager. */ @Deprecated <H extends EventHandler> H getHandler(GwtEvent.Type<H> type, int index) { assert index < getHandlerCount(type) : "handlers for " + type.getClass() + " have size: " + getHandlerCount(type) + " so do not have a handler at index: " + index; List<H> l= getHandlerList(type, null); return l.get(index); } /** * Package protected to support legacy features in HandlerManager. */ @Deprecated int getHandlerCount(GwtEvent.Type<?> eventKey) { return getHandlerList(eventKey, null).size(); } /** * Package protected to support legacy features in HandlerManager. */ @Deprecated boolean isEventHandled(GwtEvent.Type<?> eventKey) { return map.containsKey(eventKey); } private void defer(ScheduledCommand command) { if (deferredDeltas == null) { deferredDeltas= new ArrayList<ScheduledCommand>(); } deferredDeltas.add(command); } private <H extends EventHandler> HandlerRegistration doAdd(final GwtEvent.Type<H> type, final Object source, final H handler) { if (firingDepth > 0) { enqueueAdd(type, source, handler); } else { doAddNow(type, source, handler); } return new HandlerRegistration() { public void removeHandler() { doRemove(type, source, handler); } }; } private <H extends EventHandler> void doAddNow(GwtEvent.Type<H> type, Object source, H handler) { List<H> l= ensureHandlerList(type, source); l.add(handler); } private <H extends EventHandler> void doFire(GwtEvent<H> event, Object source) { try { firingDepth++; if (source != null) { event.setSource(source); } List<H> handlers= getDispatchList(event.getAssociatedType(), source); Set<Throwable> causes= null; ListIterator<H> it= isReverseOrder ? handlers.listIterator(handlers.size()) : handlers.listIterator(); while (isReverseOrder ? it.hasPrevious() : it.hasNext()) { H handler= isReverseOrder ? it.previous() : it.next(); try { event.dispatch(handler); } catch (Throwable e) { if (causes == null) { causes= new HashSet<Throwable>(); } causes.add(e); } } if (causes != null) { throw new UmbrellaException(causes); } } finally { firingDepth--; if (firingDepth == 0) { handleQueuedAddsAndRemoves(); } } } private <H> void doRemoveNow(GwtEvent.Type<H> type, Object source, H handler) { List<H> l= getHandlerList(type, source); boolean removed= l.remove(handler); assert removed : "redundant remove call"; if (removed && l.isEmpty()) { prune(type, source); } } private <H extends EventHandler> void enqueueAdd(final GwtEvent.Type<H> type, final Object source, final H handler) { defer(new ScheduledCommand() { public void execute() { doAddNow(type, source, handler); } }); } private <H extends EventHandler> void enqueueRemove(final GwtEvent.Type<H> type, final Object source, final H handler) { defer(new ScheduledCommand() { public void execute() { doRemoveNow(type, source, handler); } }); } private <H> List<H> ensureHandlerList(GwtEvent.Type<H> type, Object source) { Map<Object, List<?>> sourceMap= map.get(type); if (sourceMap == null) { sourceMap= new HashMap<Object, List<?>>(); map.put(type, sourceMap); } // safe, we control the puts. @SuppressWarnings("unchecked") List<H> handlers= (List<H>) sourceMap.get(source); if (handlers == null) { handlers= new ArrayList<H>(); sourceMap.put(source, handlers); } return handlers; } private <H> List<H> getDispatchList(GwtEvent.Type<H> type, Object source) { List<H> directHandlers= getHandlerList(type, source); if (source == null) { return directHandlers; } List<H> globalHandlers= getHandlerList(type, null); List<H> rtn= new ArrayList<H>(directHandlers); rtn.addAll(globalHandlers); return rtn; } private <H> List<H> getHandlerList(GwtEvent.Type<H> type, Object source) { Map<Object, List<?>> sourceMap= map.get(type); if (sourceMap == null) { return Collections.emptyList(); } // safe, we control the puts. @SuppressWarnings("unchecked") List<H> handlers= (List<H>) sourceMap.get(source); if (handlers == null) { return Collections.emptyList(); } return handlers; } private void handleQueuedAddsAndRemoves() { if (deferredDeltas != null) { try { for (ScheduledCommand c : deferredDeltas) { c.execute(); } } finally { deferredDeltas= null; } } } private void prune(GwtEvent.Type<?> type, Object source) { Map<Object, List<?>> sourceMap= map.get(type); List<?> pruned= sourceMap.remove(source); assert pruned != null : "Can't prune what wasn't there"; assert pruned.isEmpty() : "Pruned unempty list!"; if (sourceMap.isEmpty()) { map.remove(type); } } }