blob: db8617e3b6306a3fed2e9e01235338a449389238 [file] [log] [blame]
/*
* Copyright 2009 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.google.gwt.event.shared;
import com.google.gwt.event.shared.GwtEvent.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Manager responsible for adding handlers to event sources and firing those
* handlers on passed in events.
*/
public class HandlerManager {
/**
* Interface for queued add/remove operations.
*/
private interface AddOrRemoveCommand {
public void execute();
}
/**
* Inner class used to actually contain the handlers.
*/
private static class HandlerRegistry {
private final HashMap<GwtEvent.Type<?>, ArrayList<?>> map = new HashMap<GwtEvent.Type<?>, ArrayList<?>>();
private <H extends EventHandler> void addHandler(Type<H> type, H handler) {
ArrayList<H> l = get(type);
if (l == null) {
l = new ArrayList<H>();
map.put(type, l);
}
l.add(handler);
}
private <H extends EventHandler> void fireEvent(GwtEvent<H> event,
boolean isReverseOrder) {
Type<H> type = event.getAssociatedType();
int count = getHandlerCount(type);
if (isReverseOrder) {
for (int i = count - 1; i >= 0; i--) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
}
} else {
for (int i = 0; i < count; i++) {
H handler = this.<H> getHandler(type, i);
event.dispatch(handler);
}
}
}
@SuppressWarnings("unchecked")
private <H> ArrayList<H> get(GwtEvent.Type<H> type) {
// This cast is safe because we control the puts.
return (ArrayList<H>) map.get(type);
}
private <H extends EventHandler> H getHandler(GwtEvent.Type<H> eventKey,
int index) {
ArrayList<H> l = get(eventKey);
return l.get(index);
}
private int getHandlerCount(GwtEvent.Type<?> eventKey) {
ArrayList<?> l = map.get(eventKey);
return l == null ? 0 : l.size();
}
private boolean isEventHandled(GwtEvent.Type<?> eventKey) {
return map.containsKey(eventKey);
}
private <H> void removeHandler(GwtEvent.Type<H> eventKey, H handler) {
ArrayList<H> l = get(eventKey);
boolean result = l.remove(handler);
if (l.size() == 0) {
map.remove(eventKey);
}
assert result : "Tried to remove unknown handler: " + handler + " from "
+ eventKey;
}
}
private int firingDepth = 0;
private boolean isReverseOrder;
// map storing the actual handlers
private HandlerRegistry registry;
// source of the event.
private final Object source;
// Add and remove operations received during dispatch.
private List<AddOrRemoveCommand> deferredDeltas;
/**
* Creates a handler manager with the given source. Handlers will be fired in
* the order that they are added.
*
* @param source the event source
*/
public HandlerManager(Object source) {
this(source, false);
}
/**
* Creates a handler manager with the given source, specifying the order in
* which handlers are fired.
*
* @param source the event source
* @param fireInReverseOrder true to fire handlers in reverse order
*/
public HandlerManager(Object source, boolean fireInReverseOrder) {
registry = new HandlerRegistry();
this.source = source;
this.isReverseOrder = fireInReverseOrder;
}
/**
* Adds a handle.
*
* @param <H> The type of handler
* @param type the event type associated with this handler
* @param handler the handler
* @return the handler registration, can be stored in order to remove the
* handler later
*/
public <H extends EventHandler> HandlerRegistration addHandler(
GwtEvent.Type<H> type, final H handler) {
assert type != null : "Cannot add a handler with a null type";
assert handler != null : "Cannot add a null handler";
if (firingDepth > 0) {
enqueueAdd(type, handler);
} else {
doAdd(type, handler);
}
return new DefaultHandlerRegistration(this, type, handler);
}
/**
* Fires the given event to the handlers listening to the event's type.
*
* Note, any subclass should be very careful about overriding this method, as
* adds/removes of handlers will not be safe except within this
* implementation.
*
* @param event the event
*/
public void fireEvent(GwtEvent<?> event) {
// If it not live we should revive it.
if (!event.isLive()) {
event.revive();
}
Object oldSource = event.getSource();
event.setSource(source);
try {
firingDepth++;
registry.fireEvent(event, isReverseOrder);
} finally {
firingDepth--;
if (firingDepth == 0) {
handleQueuedAddsAndRemoves();
}
}
if (oldSource == null) {
// This was my event, so I should kill it now that I'm done.
event.kill();
} else {
// Restoring the source for the next handler to use.
event.setSource(oldSource);
}
}
/**
* Gets the handler at the given index.
*
* @param <H> the event handler type
* @param index the index
* @param type the handler's event type
* @return the given handler
*/
public <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;
return registry.getHandler(type, index);
}
/**
* Gets the number of handlers listening to the event type.
*
* @param type the event type
* @return the number of registered handlers
*/
public int getHandlerCount(Type<?> type) {
return registry.getHandlerCount(type);
}
/**
* Does this handler manager handle the given event type?
*
* @param e the event type
* @return whether the given event type is handled
*/
public boolean isEventHandled(Type<?> e) {
return registry.isEventHandled(e);
}
/**
* Removes the given handler from the specified event type. Normally,
* applications should call {@link HandlerRegistration#removeHandler()}
* instead.
*
* @param <H> handler type
*
* @param type the event type
* @param handler the handler
* @deprecated This method is likely to be removed along with "listener"
* interfaces in a future release. If you have a reason it should
* be retained beyond that time, please add your comments to GWT
* <a href="http://code.google.com/p/google-web-toolkit/issues/detail?id=3102">issue 3102</a>
*/
@Deprecated
public <H extends EventHandler> void removeHandler(GwtEvent.Type<H> type,
final H handler) {
if (firingDepth > 0) {
enqueueRemove(type, handler);
} else {
doRemove(type, handler);
}
}
/**
* Not part of the public API, available only to allow visualization tools to
* be developed in gwt-incubator.
*
* @return a map of all handlers in this handler manager
*/
Map<GwtEvent.Type<?>, ArrayList<?>> createHandlerInfo() {
return registry.map;
}
private void defer(AddOrRemoveCommand command) {
if (deferredDeltas == null) {
deferredDeltas = new ArrayList<AddOrRemoveCommand>();
}
deferredDeltas.add(command);
}
private <H extends EventHandler> void doAdd(GwtEvent.Type<H> type,
final H handler) {
registry.addHandler(type, handler);
}
private <H extends EventHandler> void doRemove(GwtEvent.Type<H> type,
final H handler) {
registry.removeHandler(type, handler);
}
private <H extends EventHandler> void enqueueAdd(final GwtEvent.Type<H> type,
final H handler) {
defer(new AddOrRemoveCommand() {
public void execute() {
doAdd(type, handler);
}
});
}
private <H extends EventHandler> void enqueueRemove(
final GwtEvent.Type<H> type, final H handler) {
defer(new AddOrRemoveCommand() {
public void execute() {
doRemove(type, handler);
}
});
}
private void handleQueuedAddsAndRemoves() {
if (deferredDeltas != null) {
try {
for (AddOrRemoveCommand c : deferredDeltas) {
c.execute();
}
} finally {
deferredDeltas = null;
}
}
}
}