for (final MetaMethod method : declaringClass.getMethodsAnnotatedWith(EventHandler.class)) {
final String[] targetDataFieldNames = method.getAnnotation(EventHandler.class).value();
if (targetDataFieldNames.length == 0) {
throw new GenerationException("@EventHandler annotation on method ["
+ declaringClass.getFullyQualifiedName()
+ "." + method.getName() + "] must specify at least one data-field target.");
}
final MetaClass eventType = (method.getParameters().length == 1) ? method.getParameters()[0].getType() : null;
if (eventType == null || (!eventType.isAssignableTo(Event.class)) && !eventType.isAssignableTo(DomEvent.class)) {
throw new GenerationException("@EventHandler method [" + method.getName() + "] in class ["
+ declaringClass.getFullyQualifiedName()
+ "] must have exactly one parameter of a type extending either ["
+ DomEvent.class.getName() + "] or [" + NativeEvent.class.getName() + "].");
}
if (eventType.isAssignableTo(Event.class)) {
/*
* Generate native DOM event handlers.
*/
final MetaClass handlerType = MetaClassFactory.get(EventListener.class);
final BlockBuilder<AnonymousClassStructureBuilder> listenerBuiler = ObjectBuilder.newInstanceOf(handlerType)
.extend()
.publicOverridesMethod(handlerType.getMethods()[0].getName(), Parameter.of(eventType, "event"));
listenerBuiler.append(InjectUtil.invokePublicOrPrivateMethod(ctx.getInjectionContext(), component,
method, Stmt.loadVariable("event")));
final ObjectBuilder listenerInstance = listenerBuiler.finish().finish();
int eventsToSink =
Event.FOCUSEVENTS | Event.GESTUREEVENTS | Event.KEYEVENTS | Event.MOUSEEVENTS | Event.TOUCHEVENTS;
if (method.isAnnotationPresent(SinkNative.class)) {
eventsToSink = method.getAnnotation(SinkNative.class).value();
}
for (final String name : targetDataFieldNames) {
if (processedNativeHandlers.contains(name) || processedEventHandlers.contains(name)) {
throw new GenerationException(
"Cannot specify more than one @EventHandler method when @SyncNative is used for data-field ["
+ name + "] in class ["
+ declaringClass.getFullyQualifiedName()
+ "].");
}
else {
processedNativeHandlers.add(name);
}
if (dataFieldTypes.containsKey(name)) {
final MetaClass dataFieldType = dataFieldTypes.get(name);
if (!dataFieldType.isAssignableTo(Element.class)) {
/*
* We have a GWT or other Widget type.
*/
throw new GenerationException("@DataField [" + name + "] of type [" + dataFieldType.getName()
+ "] in class [" + declaringClass.getFullyQualifiedName() + "] is not assignable to ["
+ Element.class.getName() + "] specified by @EventHandler method " + method.getName()
+ "("
+ eventType.getName() + ")]");
}
else {
/*
* We have a wrapped native Element reference
*/
throw new GenerationException("Cannot attach native DOM events to @DataField [" + name
+ "] of type ["
+ dataFieldType.getName() + "] in class [" + declaringClass.getFullyQualifiedName()
+ "] specified by @EventHandler method " + method.getName() + "(" + eventType.getName()
+ ")] - Use the corresponding GWT 'EventHandler' types instead.");
}
}
else {
/*
* We are completely native and have no reference to this data-field
* Element in Java
*/
initStmts.add(Stmt.invokeStatic(TemplateUtil.class, "setupNativeEventListener", component,
Stmt.loadVariable(dataFieldElementsVarName).invoke("get", name), listenerInstance,
eventsToSink));
}
}
}
else {
/*
* We have a GWT Widget type
*/
final MetaClass handlerType;
try {
handlerType = getHandlerForEvent(eventType);
}
catch (GenerationException e) {
/*
* see ERRAI-373 for details on this crazy inference (without this message, the cause of the
* problem is nearly impossible to diagnose)
*/
if (declaringClass.getClass() == JavaReflectionClass.class) {
throw new GenerationException(
"The type " + declaringClass.getFullyQualifiedName() + " looks like a client-side" +
" @Templated class, but it is not known to GWT. This probably means that " +
declaringClass.getName() + " or one of its supertypes contains non-translatable code." +
" Run the GWT compiler with logLevel=DEBUG to pinpoint the problem.", e);
}
throw e;
}
final BlockBuilder<AnonymousClassStructureBuilder> listenerBuiler = ObjectBuilder.newInstanceOf(handlerType)
.extend()
.publicOverridesMethod(handlerType.getMethods()[0].getName(), Parameter.of(eventType, "event"));
listenerBuiler.append(InjectUtil.invokePublicOrPrivateMethod(ctx.getInjectionContext(),
component, method, Stmt.loadVariable("event")));
final ObjectBuilder listenerInstance = listenerBuiler.finish().finish();
final MetaClass hasHandlerType = MetaClassFactory.get("com.google.gwt.event.dom.client.Has"
+ handlerType.getName()
+ "s");
for (final String name : targetDataFieldNames) {
final MetaClass dataFieldType = dataFieldTypes.get(name);
if (dataFieldType == null) {
throw new GenerationException("@EventHandler method [" + method.getName() + "] in class ["
+ declaringClass.getFullyQualifiedName()
+ "] handles a GWT event type but the specified @DataField [" + name + "] was not found.");
}
if (processedNativeHandlers.contains(name)) {
throw new GenerationException(
"Cannot specify more than one @EventHandler method when @SinkNative is used for data-field ["
+ name + "] in class [" + declaringClass.getFullyQualifiedName()
+ "].");
}
processedEventHandlers.add(name);
// Where will the event come from? It could be a @DataField member, or it could be the templated widget itself!
final Statement eventSource;
if ("this".equals(name)) {
eventSource = Stmt.loadVariable("obj");
}
else {
eventSource = Stmt.nestedCall(fieldsMap).invoke("get", name);
}
if (dataFieldType.isAssignableTo(Element.class)) {
initStmts.add(Stmt.invokeStatic(TemplateUtil.class, "setupWrappedElementEventHandler", component,
eventSource, listenerInstance,
Stmt.invokeStatic(eventType, "getType")));
}
else if (dataFieldType.isAssignableTo(hasHandlerType)) {
final Statement widget = Cast.to(hasHandlerType, eventSource);
initStmts.add(Stmt.nestedCall(widget).invoke("add" + handlerType.getName(),
Cast.to(handlerType, listenerInstance)));
}
else if (dataFieldType.isAssignableTo(Widget.class)) {
final Statement widget = Cast.to(Widget.class, eventSource);
initStmts.add(Stmt.nestedCall(widget).invoke("addDomHandler",
listenerInstance, Stmt.invokeStatic(eventType, "getType")));
}
else {
throw new GenerationException("@DataField [" + name + "] of type [" + dataFieldType.getName()
+ "] in class [" + declaringClass.getFullyQualifiedName()
+ "] does not implement required interface [" + hasHandlerType.getName()
+ "] specified by @EventHandler method " + method.getName() + "(" + eventType.getName()
+ ")]");
}