Have you thought about implementing a custom EventBus/HandlerManager that will register handlers based on type of event and class of the model object? I don't have time to code it up right now, but it seems like what you really need is information about the target handler based on (1) the type of event (CRUD) and (2) the class of model object. This, of course, means you would have to do some work emulating reflection to get some sort of instanceof operator.
However, given that GWT's JRE emulation gives you this capability to some extent (Class.getSuperclass() is emulated), this may not be a show stopper. There are a few cons to this method, namely: 1) The GWT community has generally assumed that HandlerManager == EventBus. You might not get a lot of help if you leave the "well- beaten path" (inasmuch as I can use that term with such a young technology) 2) Depending on how you implement it, you may end up needing two HandlerRegistry classes, the one defined in HandlerManager for all "normal" events, and one in your subclass for all "model-crudding" events. This may add some complexity. 3) It might not work :) But, if you follow my reasoning, there isn't a reason why it shouldn't work... But who knows until you try. You might end up redoing the entire GwtEvent/HandlerManager toolset, but if it's important enough (i.e. the problems introduced by code bulk are painful enough), there is always a way :) On Jan 12, 12:21 pm, jarrod <jarrod.carl...@gmail.com> wrote: > Actually I've just realized that the first of the two options I just > mentioned still does not resolve the subclass issue - when registering > your handler, you'd still have to know if you expect a subclass to be > returned or not, which sort of negates the point of interfaces on the > model classes in the first place! I vote for option 2. > > And this: > > > It's really too bad that HandlerManager relies on GwtEvent.Type > > instances as the HandlerRegistry#map key instead of comparing Type > > instances to see if they are equivalent! > > On Jan 12, 2:18 pm, jarrod <jarrod.carl...@gmail.com> wrote: > > > > > You are exactly correct, and that will be a problem I hadn't thought > > of since I am using interfaces for my model objects. I am hereby > > revoking my co-worker's lunch freebie! > > > So assuming the GenericEvent class from my previous post, here's a > > unit test that fails, according to your statements: > > > public class GenericEventTest { > > > public static interface Foo { > > } > > > public static class FooImpl implements Foo { > > } > > > private boolean fooFired = false; > > > @Test > > public void subclassHandlerRegistrationTest() { > > HandlerManager manager = new HandlerManager(null); > > > manager.addHandler(GenericEvent.getType(Foo.class), > > new GenericHandler<Foo>() { > > @Override > > public void onGenericEvent(GenericEvent<Foo> > > event) { > > Assert.assertTrue(event.getResource() > > instanceof Foo); > > GenericEventTest.this.fooFired = true; > > } > > }); > > > manager.fireEvent(new GenericEvent<Foo>(new FooImpl())); > > Assert.assertTrue(this.fooFired); > > > } > > > } > > > So what can be done? Well, not much, since this is destined for > > JavaScript and we can't do much class inspection. One option, though, > > is to register the GenericHandler with the concrete type expected > > (although the GenericHandler itself can remain agnostic and deal with > > the interface). This works: > > > manager.addHandler(GenericEvent.getType(FooImpl.class), // Registered > > with FooImpl.class > > new GenericHandler<Foo>() { // Handler is still for Foo, > > though > > @Override > > public void onGenericEvent(GenericEvent<Foo> event) { > > } > > }); > > > manager.fireEvent(new GenericEvent<Foo>(new FooImpl())); > > > But this couples the code to the implementations, and depending on the > > project, may not always be what you want. I only have one > > implementation of my model classes at the moment, but in the next ten > > minutes? Who knows - businesses change their minds ;-) > > > The other option I have come up with is to simply store the > > GwtEvent.Type instances elsewhere and pass them in to the event at > > creation time: > > > public interface MyTypes { > > > public static Type<GenericHandler<Foo>> FOO = new > > Type<GenericHandler<Foo>>(); > > > } > > > public class GenericEvent<T> extends GwtEvent<GenericHandler<T>> { > > > private T resource; > > private Type<GenericHandler<T>> type; > > > public GenericEvent(T resource, Type<GenericHandler<T>> type) { > > this.resource = resource; > > this.type = type; > > } > > > @Override > > public Type<GenericHandler<T>> getAssociatedType() { > > return this.type; > > } > > > public T getResource() { > > return this.resource; > > } > > > @Override > > protected void dispatch(GenericHandler<T> handler) { > > handler.onGenericEvent(this); > > } > > > } > > > public class GenericEventTest { > > > public static interface Foo { > > } > > > public static class FooImpl implements Foo { > > } > > > private boolean fired = false; > > > @Test > > public void staticTypeHandlerRegistrationTest() { > > HandlerManager manager = new HandlerManager(null); > > > manager.addHandler(MyTypes.FOO, new GenericHandler<Foo>() { > > @Override > > public void onGenericEvent(GenericEvent<Foo> event) { > > Assert.assertTrue(event.getResource() instanceof Foo); > > GenericEventTest.this.fired = true; > > } > > }); > > > manager.fireEvent(new GenericEvent<Foo>(new FooImpl(), > > MyTypes.FOO)); > > Assert.assertTrue(this.fired); > > > } > > > } > > > I don't know which of those two options is the "lesser" evil, if you > > will... but I am leaning toward the latter. Maintaining an interface > > like MyTypes seems messy, but properly managed, it beats the heck out > > of managing ({number of model classes} x (1 event type + 1 event > > handler)) classes! Although the following code still doesn't work (and > > maybe it shouldn't?) > > > public interface MyTypes { > > > public static Type<GenericHandler<Bar>> BAR = new > > Type<GenericHandler<Bar>>(); > > > public static Type<GenericHandler<Foo>> FOO = new > > Type<GenericHandler<Foo>>(); > > > } > > > public class GenericEventTest { > > > public static interface Bar extends Foo { > > } > > > public static class BarImpl implements Bar { > > > } > > > public static interface Foo { > > } > > > public static class FooImpl implements Foo { > > } > > > private boolean fired = false; > > > @Test > > public void complexTypeHandlerRegistrationTest() { > > HandlerManager manager = new HandlerManager(null); > > > manager.addHandler(MyTypes.FOO, new GenericHandler<Foo>() { > > @Override > > public void onGenericEvent(GenericEvent<Foo> event) { > > Assert.assertTrue(event.getResource() instanceof Foo); > > GenericEventTest.this.fired = true; > > } > > }); > > > manager.fireEvent(new GenericEvent<Bar>(new BarImpl(), > > MyTypes.BAR)); > > Assert.assertTrue(this.fired); > > > } > > > } > > > It's really too bad that HandlerManager relies on GwtEvent.Type > > instances as the HandlerRegistry#map key instead of comparing Type > > instances to see if they are equivalent! > > > On Jan 12, 12:20 pm, Thomas Broyer <t.bro...@gmail.com> wrote: > > > > On Jan 12, 5:42 pm, jarrod <jarrod.carl...@gmail.com> wrote: > > > > > It's amazing what fellow developers can come up with when you dangle a > > > > free lunch in their face! Below is a refactored GenericEvent and > > > > GenericEventTest that compiles and satisfies my desire to cut > > > > clutter... but now I owe my co-worker lunch... > > > > > public class GenericEvent<T> extends GwtEvent<GenericHandler<T>> { > > > > > private static Map<Class<?>, Type<GenericHandler<?>>> TYPES = new > > > > HashMap<Class<?>, Type<GenericHandler<?>>>(); > > > > > public static Type<GenericHandler<?>> getType(Class<?> clazz) { > > > > > if (!TYPES.containsKey(clazz)) { > > > > TYPES.put(clazz, new Type<GenericHandler<?>>()); > > > > } > > > > > return TYPES.get(clazz); > > > > } > > > > > private T resource; > > > > > public GenericEvent(T resource) { > > > > this.resource = resource; > > > > } > > > > > @SuppressWarnings("unchecked") > > > > @Override > > > > public Type<GenericHandler<T>> getAssociatedType() { > > > > return (Type) GenericEvent.getType(this.resource.getClass()); > > > > } > > > > Be careful not to send a subclass of what you actually want your > > > handlers to receive (could be an anonymous subclass!): > > > > class Foo { } > > > class Bar extends Foo { } > > > > bus.addHandler(GenericEvent.getType(Foo.class), myFooHandler); > > > > Foo foo = new Bar(); > > > bus.fireEvent(new GenericEvent(foo)); > > > // myFooHandler won't get called, because 'foo' is actually an > > > instance of Bar
-- http://groups.google.com/group/Google-Web-Toolkit-Contributors