Author: jlaba...@google.com Date: Thu Jan 15 12:38:29 2009 New Revision: 4474
Modified: releases/1.6/user/src/com/google/gwt/event/logical/shared/BeforeSelectionEvent.java releases/1.6/user/src/com/google/gwt/event/logical/shared/ResizeEvent.java releases/1.6/user/src/com/google/gwt/event/logical/shared/SelectionEvent.java releases/1.6/user/src/com/google/gwt/event/logical/shared/ValueChangeEvent.java releases/1.6/user/src/com/google/gwt/event/shared/HandlerManager.java releases/1.6/user/src/com/google/gwt/user/client/Event.java releases/1.6/user/src/com/google/gwt/user/client/ListenerWrapper.java releases/1.6/user/src/com/google/gwt/user/client/Window.java releases/1.6/user/src/com/google/gwt/user/client/ui/FormPanel.java releases/1.6/user/test/com/google/gwt/event/shared/HandlerManagerTest.java releases/1.6/user/test/com/google/gwt/user/client/EventTest.java Log: Adds an option to HandlerManager to fire handlers in reverse order, and converts NativePreviewEvent to use the HandlerManager instead of an ArrayList. The HandlerManager automatically handles concurrent adds and removes, which caused a problem in MenuBar. Patch by: jlabanca Review by: ecc (desk) Issue: 3289 Modified: releases/1.6/user/src/com/google/gwt/event/logical/shared/BeforeSelectionEvent.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/event/logical/shared/BeforeSelectionEvent.java (original) +++ releases/1.6/user/src/com/google/gwt/event/logical/shared/BeforeSelectionEvent.java Thu Jan 15 12:38:29 2009 @@ -91,7 +91,7 @@ // field itself does not, so we have to do an unsafe cast here. @SuppressWarnings("unchecked") @Override - public Type<BeforeSelectionHandler<I>> getAssociatedType() { + public final Type<BeforeSelectionHandler<I>> getAssociatedType() { return (Type) TYPE; } Modified: releases/1.6/user/src/com/google/gwt/event/logical/shared/ResizeEvent.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/event/logical/shared/ResizeEvent.java (original) +++ releases/1.6/user/src/com/google/gwt/event/logical/shared/ResizeEvent.java Thu Jan 15 12:38:29 2009 @@ -76,7 +76,7 @@ } @Override - public Type<ResizeHandler> getAssociatedType() { + public final Type<ResizeHandler> getAssociatedType() { return TYPE; } Modified: releases/1.6/user/src/com/google/gwt/event/logical/shared/SelectionEvent.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/event/logical/shared/SelectionEvent.java (original) +++ releases/1.6/user/src/com/google/gwt/event/logical/shared/SelectionEvent.java Thu Jan 15 12:38:29 2009 @@ -76,7 +76,7 @@ // field itself does not, so we have to do an unsafe cast here. @SuppressWarnings("unchecked") @Override - public Type<SelectionHandler<I>> getAssociatedType() { + public final Type<SelectionHandler<I>> getAssociatedType() { return (Type) TYPE; } Modified: releases/1.6/user/src/com/google/gwt/event/logical/shared/ValueChangeEvent.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/event/logical/shared/ValueChangeEvent.java (original) +++ releases/1.6/user/src/com/google/gwt/event/logical/shared/ValueChangeEvent.java Thu Jan 15 12:38:29 2009 @@ -54,7 +54,6 @@ * safe handling of null. * * @param <I> the old value type - * @param <S> The event source * @param source the source of the handlers * @param oldValue the oldValue, may be null * @param newValue the newValue, may be null @@ -110,7 +109,7 @@ // field itself does not, so we have to do an unsafe cast here. @SuppressWarnings("unchecked") @Override - public Type<ValueChangeHandler<I>> getAssociatedType() { + public final Type<ValueChangeHandler<I>> getAssociatedType() { return (Type) TYPE; } Modified: releases/1.6/user/src/com/google/gwt/event/shared/HandlerManager.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/event/shared/HandlerManager.java (original) +++ releases/1.6/user/src/com/google/gwt/event/shared/HandlerManager.java Thu Jan 15 12:38:29 2009 @@ -44,12 +44,20 @@ l.add(handler); } - private <H extends EventHandler> void fireEvent(GwtEvent<H> event) { + private <H extends EventHandler> void fireEvent(GwtEvent<H> event, + boolean isReverseOrder) { Type<H> type = event.getAssociatedType(); int count = getHandlerCount(type); - for (int i = 0; i < count; i++) { - H handler = this.<H> getHandler(type, i); - event.dispatch(handler); + 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); + } } } @@ -79,6 +87,7 @@ } private int firingDepth = 0; + private boolean isReverseOrder; // map storing the actual handlers private HandlerRegistry registry; @@ -90,13 +99,26 @@ private List<Command> deferredDeltas; /** - * Creates a handler manager with the given source. + * 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; } /** @@ -140,7 +162,7 @@ try { firingDepth++; - registry.fireEvent(event); + registry.fireEvent(event, isReverseOrder); } finally { firingDepth--; Modified: releases/1.6/user/src/com/google/gwt/user/client/Event.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/user/client/Event.java (original) +++ releases/1.6/user/src/com/google/gwt/user/client/Event.java Thu Jan 15 12:38:29 2009 @@ -20,11 +20,9 @@ import com.google.gwt.event.dom.client.HasNativeEvent; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; +import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.event.shared.HandlerRegistration; -import java.util.ArrayList; -import java.util.List; - /** * <p> * An opaque handle to a native DOM Event. An <code>Event</code> cannot be @@ -67,29 +65,18 @@ /** * Fire a {...@link NativePreviewEvent} for the native event. * - * @param handlers the list of {...@link NativePreviewHandler} + * @param handlers the {...@link HandlerManager} * @param nativeEvent the native event * @return true to fire the event normally, false to cancel the event */ - static boolean fire(List<NativePreviewHandler> handlers, Event nativeEvent) { + private static boolean fire(HandlerManager handlers, Event nativeEvent) { if (TYPE != null && handlers != null) { // Revive the event - if (singleton == null) { - singleton = new NativePreviewEvent(); - } else { - singleton.revive(); - } + singleton.revive(); singleton.setNativeEvent(nativeEvent); - // Fire the event to all handlers - int numHandlers = handlers.size(); - for (int i = numHandlers - 1; i >= 0; i--) { - handlers.get(i).onPreviewNativeEvent(singleton); - singleton.isFirstHandler = false; - } - - // Kill the event and return - singleton.kill(); + // Fire the event + handlers.fireEvent(singleton); return !(singleton.isCanceled() && !singleton.isConsumed()); } return true; @@ -139,7 +126,7 @@ } @Override - public Type<NativePreviewHandler> getAssociatedType() { + public final Type<NativePreviewHandler> getAssociatedType() { return TYPE; } @@ -181,6 +168,7 @@ @Override protected void dispatch(NativePreviewHandler handler) { handler.onPreviewNativeEvent(this); + singleton.isFirstHandler = false; } @Override @@ -360,7 +348,7 @@ * handler manager for efficiency and because we want to fire the handlers in * reverse order. When the last handler is removed, handlers is reset to null. */ - static ArrayList<NativePreviewHandler> handlers; + static HandlerManager handlers; /** * Adds an event preview to the preview stack. As long as this preview remains @@ -403,20 +391,10 @@ // Initialize the type NativePreviewEvent.getType(); if (handlers == null) { - handlers = new ArrayList<NativePreviewHandler>(); + handlers = new HandlerManager(null, true); + NativePreviewEvent.singleton = new NativePreviewEvent(); } - handlers.add(handler); - return new HandlerRegistration() { - public void removeHandler() { - if (handlers != null) { - handlers.remove(handler); - if (handlers.size() == 0) { - // Set handlers to null so we can optimize fireNativePreviewEvent - handlers = null; - } - } - } - }; + return handlers.addHandler(NativePreviewEvent.TYPE, handler); } /** Modified: releases/1.6/user/src/com/google/gwt/user/client/ListenerWrapper.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/user/client/ListenerWrapper.java (original) +++ releases/1.6/user/src/com/google/gwt/user/client/ListenerWrapper.java Thu Jan 15 12:38:29 2009 @@ -27,8 +27,6 @@ import com.google.gwt.event.shared.GwtEvent.Type; import com.google.gwt.user.client.Event.NativePreviewEvent; -import java.util.EventListener; - /** * Legacy listener support for <code>com.google.gwt.user.client</code>. Gathers * the bulk of the legacy glue code in one place, for easy deletion when @@ -66,19 +64,7 @@ } public static void remove(EventPreview listener) { - if (Event.handlers == null) { - return; - } - int handlerCount = Event.handlers.size(); - // We only want to remove the first instance, as the legacy listener does - for (int i = 0; i < handlerCount; i++) { - Event.NativePreviewHandler handler = Event.handlers.get(i); - if (handler instanceof NativePreview - && ((NativePreview) handler).listener.equals(listener)) { - Event.handlers.remove(handler); - return; - } - } + baseRemove(Event.handlers, listener, NativePreviewEvent.getType()); } private NativePreview(EventPreview listener) { @@ -175,7 +161,7 @@ // This is a direct copy of the baseRemove from // com.google.gwt.user.client.ui.ListenerWrapper. Change in parallel. static <H extends EventHandler> void baseRemove(HandlerManager manager, - EventListener listener, Type... keys) { + Object listener, Type... keys) { if (manager != null) { for (Type<H> key : keys) { int handlerCount = manager.getHandlerCount(key); Modified: releases/1.6/user/src/com/google/gwt/user/client/Window.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/user/client/Window.java (original) +++ releases/1.6/user/src/com/google/gwt/user/client/Window.java Thu Jan 15 12:38:29 2009 @@ -64,7 +64,7 @@ private String message = null; @Override - public Type<ClosingHandler> getAssociatedType() { + public final Type<ClosingHandler> getAssociatedType() { return TYPE; } @@ -332,7 +332,7 @@ } @Override - public Type<ScrollHandler> getAssociatedType() { + public final Type<ScrollHandler> getAssociatedType() { return TYPE; } Modified: releases/1.6/user/src/com/google/gwt/user/client/ui/FormPanel.java ============================================================================== --- releases/1.6/user/src/com/google/gwt/user/client/ui/FormPanel.java (original) +++ releases/1.6/user/src/com/google/gwt/user/client/ui/FormPanel.java Thu Jan 15 12:38:29 2009 @@ -87,7 +87,7 @@ private String resultHtml; /** - * Create a submit complete event + * Create a submit complete event. * * @param resultsHtml the results from submitting the form */ @@ -96,7 +96,7 @@ } @Override - public Type<SubmitCompleteHandler> getAssociatedType() { + public final Type<SubmitCompleteHandler> getAssociatedType() { return TYPE; } @@ -162,7 +162,7 @@ } @Override - public Type<FormPanel.SubmitHandler> getAssociatedType() { + public final Type<FormPanel.SubmitHandler> getAssociatedType() { return TYPE; } Modified: releases/1.6/user/test/com/google/gwt/event/shared/HandlerManagerTest.java ============================================================================== --- releases/1.6/user/test/com/google/gwt/event/shared/HandlerManagerTest.java (original) +++ releases/1.6/user/test/com/google/gwt/event/shared/HandlerManagerTest.java Thu Jan 15 12:38:29 2009 @@ -223,7 +223,6 @@ assertFired(one, mouse1); } - @SuppressWarnings("deprecation") public void testMultiFiring() { HandlerManager manager = new HandlerManager("source1"); @@ -260,5 +259,39 @@ manager.fireEvent(new MouseDownEvent() { }); assertFired(mouse1, adaptor1, mouse3); + } + + public void testReverseOrder() { + // Add some handlers to a manager + final HandlerManager manager = new HandlerManager("source1", true); + final MouseDownHandler handler0 = new MouseDownHandler() { + public void onMouseDown(MouseDownEvent event) { + add(this); + } + }; + final MouseDownHandler handler1 = new MouseDownHandler() { + public void onMouseDown(MouseDownEvent event) { + assertNotFired(handler0); + add(this); + } + }; + final MouseDownHandler handler2 = new MouseDownHandler() { + public void onMouseDown(MouseDownEvent event) { + assertNotFired(handler0, handler1); + add(this); + } + }; + HandlerRegistration reg0 = manager.addHandler(MouseDownEvent.getType(), + handler0); + HandlerRegistration reg1 = manager.addHandler(MouseDownEvent.getType(), + handler1); + HandlerRegistration reg2 = manager.addHandler(MouseDownEvent.getType(), + handler2); + + // Fire the event + reset(); + manager.fireEvent(new MouseDownEvent() { + }); + assertFired(handler0, handler1, handler2); } } Modified: releases/1.6/user/test/com/google/gwt/user/client/EventTest.java ============================================================================== --- releases/1.6/user/test/com/google/gwt/user/client/EventTest.java (original) +++ releases/1.6/user/test/com/google/gwt/user/client/EventTest.java Thu Jan 15 12:38:29 2009 @@ -94,6 +94,36 @@ } /** + * Test that concurrent removal of a {...@link NativePreviewHandler} does not + * trigger an exception. The handler should not actually be removed until the + * end of the event loop. + */ + public void testConcurrentRemovalOfNativePreviewHandler() { + // Add handler0 + final TestNativePreviewHandler handler0 = new TestNativePreviewHandler( + false, false); + final HandlerRegistration reg0 = Event.addNativePreviewHandler(handler0); + + // Add handler 1 + final TestNativePreviewHandler handler1 = new TestNativePreviewHandler( + false, false) { + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + super.onPreviewNativeEvent(event); + handler0.assertIsFired(false); + reg0.removeHandler(); + } + }; + HandlerRegistration reg1 = Event.addNativePreviewHandler(handler1); + assertTrue(Event.fireNativePreviewEvent(null)); + + // Verify both handlers fired even though one was removed + handler0.assertIsFired(true); + handler1.assertIsFired(true); + reg1.removeHandler(); + } + + /** * Test that {...@link Event#fireNativePreviewEvent(Event)} returns the correct * value if the native event is canceled. */ @@ -146,6 +176,7 @@ super.onPreviewNativeEvent(event); preview0.assertIsFired(false); preview1.assertIsFired(true); + assertFalse(event.isFirstHandler()); } }; final TestNativePreviewHandler handler1 = new TestNativePreviewHandler( @@ -156,6 +187,7 @@ handler0.assertIsFired(false); preview0.assertIsFired(false); preview1.assertIsFired(true); + assertFalse(event.isFirstHandler()); } }; final TestNativePreviewHandler handler2 = new TestNativePreviewHandler( @@ -167,6 +199,7 @@ handler1.assertIsFired(false); preview0.assertIsFired(false); preview1.assertIsFired(true); + assertFalse(event.isFirstHandler()); } }; final TestNativePreviewHandler handler3 = new TestNativePreviewHandler( @@ -179,6 +212,7 @@ handler2.assertIsFired(false); preview0.assertIsFired(false); preview1.assertIsFired(true); + assertFalse(event.isFirstHandler()); } }; DOM.addEventPreview(preview0); @@ -408,6 +442,7 @@ public void onPreviewNativeEvent(NativePreviewEvent event) { assertFalse(event.isCanceled()); assertFalse(event.isConsumed()); + assertTrue(event.isFirstHandler()); super.onPreviewNativeEvent(event); } }; --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---