com.gwtplatform.mvp.rebind.ProxyEventMethod Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2011 ArcBees 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.gwtplatform.mvp.rebind;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.gwtplatform.mvp.client.annotations.ProxyEvent;
/**
* Represents a method, in the presenter, that is responsible of handling an event.
*/
public class ProxyEventMethod {
private final TreeLogger logger;
private final ClassCollection classCollection;
private final PresenterInspector presenterInspector;
private String functionName;
private String eventTypeName;
private String handlerTypeName;
private String handlerMethodName;
private ClassInspector eventInspector;
public ProxyEventMethod(TreeLogger logger,
ClassCollection classCollection,
PresenterInspector presenterInspector) {
this.logger = logger;
this.classCollection = classCollection;
this.presenterInspector = presenterInspector;
}
public void init(JMethod method) throws UnableToCompleteException {
ProxyEvent annotation = method.getAnnotation(ProxyEvent.class);
assert annotation != null;
functionName = method.getName();
if (method.getReturnType().isPrimitive() != JPrimitiveType.VOID) {
logger.log(TreeLogger.WARN, getErrorPrefix()
+ " returns something else than void. Return value will be ignored.");
}
if (method.getParameters().length != 1) {
logger.log(TreeLogger.ERROR, getErrorPrefix()
+ " needs to have exactly 1 parameter of a type derived from GwtEvent.");
throw new UnableToCompleteException();
}
JClassType eventType = method.getParameters()[0].getType().isClassOrInterface();
if (eventType == null || !eventType.isAssignableTo(classCollection.gwtEventClass)) {
logger.log(TreeLogger.ERROR, getErrorPrefix()
+ " must take a parameter extending " + ClassCollection.gwtEventClassName);
throw new UnableToCompleteException();
}
eventInspector = new ClassInspector(logger, eventType);
eventTypeName = eventType.getQualifiedSourceName();
ensureStaticGetTypeMethodExists(eventType);
JClassType handlerType = findHandlerType(eventType);
handlerTypeName = handlerType.getQualifiedSourceName();
JMethod handlerMethod = findHandlerMethod(handlerType);
handlerMethodName = handlerMethod.getName();
// Warn if handlerMethodName is different
if (!handlerMethodName.equals(functionName)) {
logger.log(TreeLogger.WARN, getErrorPrefix(eventTypeName, handlerTypeName)
+ ". The handler method '" + handlerMethodName + "' differs from the annotated method '"
+ functionName + ". You should use the same method name for easier reference.");
}
}
private JMethod findHandlerMethod(JClassType handlerType)
throws UnableToCompleteException {
if (handlerType.getMethods().length != 1) {
logger.log(TreeLogger.ERROR, getErrorPrefix(eventTypeName, handlerTypeName)
+ ", but the handler interface has more than one method.");
throw new UnableToCompleteException();
}
JMethod handlerMethod = handlerType.getMethods()[0];
if (handlerMethod.getReturnType().isPrimitive() != JPrimitiveType.VOID) {
logger.log(TreeLogger.WARN, getErrorPrefix(eventTypeName, handlerTypeName)
+ ", but the handler's method does not return void. Return value will be ignored.");
}
return handlerMethod;
}
private JClassType findHandlerType(JClassType eventType)
throws UnableToCompleteException {
JMethod eventMethod = eventInspector.findMethod("dispatch",
classCollection.eventHandlerClass);
if (eventMethod == null) {
logger.log(TreeLogger.ERROR, getErrorPrefix(eventType.getName())
+ ", but the event class has no valid 'dispatch' method.");
throw new UnableToCompleteException();
}
return eventMethod.getParameters()[0].getType().isClassOrInterface();
}
private void ensureStaticGetTypeMethodExists(JClassType eventType)
throws UnableToCompleteException {
JMethod getTypeMethod = eventType.findMethod("getType", new JType[0]);
if (getTypeMethod == null
|| !getTypeMethod.isStatic()
|| getTypeMethod.getParameters().length != 0) {
logger.log(TreeLogger.ERROR, getErrorPrefix(eventType.getName())
+ ", but this event class does not have a static getType method with no parameters.");
throw new UnableToCompleteException();
}
JClassType getTypeReturnType = getTypeMethod.getReturnType().isClassOrInterface();
if (getTypeReturnType == null
|| !classCollection.gwtEventTypeClass.isAssignableFrom(getTypeReturnType)) {
logger.log(TreeLogger.ERROR, getErrorPrefix(eventType.getName())
+ ", but this event class getType() method does not return on object of type "
+ ClassCollection.gwtEventTypeClassName);
throw new UnableToCompleteException();
}
}
private String getErrorPrefix() {
return "In presenter " + presenterInspector.getPresenterClassName()
+ ", method " + functionName + " annotated with @" + ProxyEvent.class.getSimpleName();
}
private String getErrorPrefix(String eventTypeName) {
return getErrorPrefix() + " refers to event " + eventTypeName;
}
private String getErrorPrefix(String eventTypeName, String handlerTypeName) {
return getErrorPrefix(eventTypeName) + " with handler " + handlerTypeName;
}
/**
* Ensures that this method does not clash with the one passed as parameters.
* Logs an error and throws {@link UnableToCompleteException} if a clash occurs.
*
* @param previousMethod The method to check against.
* @throws UnableToCompleteException If a clash is observed.
*/
public void ensureNoClashWith(ProxyEventMethod previousMethod)
throws UnableToCompleteException {
if (previousMethod.handlerMethodName.equals(handlerMethodName)
&& previousMethod.eventTypeName.equals(eventTypeName)) {
logger.log(TreeLogger.ERROR, getErrorPrefix()
+ ". The handler method " + handlerMethodName + " is already used by method "
+ previousMethod.functionName + ".");
throw new UnableToCompleteException();
}
}
/**
* Ensures the class being built by the {@link ClassSourceFileComposerFactory} implements the
* interface required to handle this event.
*
* @param composerFactory The composer factory.
*/
public void addImplementedInterface(
ClassSourceFileComposerFactory composerFactory) {
composerFactory.addImplementedInterface(handlerTypeName);
}
/**
* Writes the call required to ensure the proxy handles this event.
*
* @param writer The {@link SourceWriter}.
*/
public void writeAddHandler(SourceWriter writer) {
writer.println("getEventBus().addHandler( " + eventTypeName + ".getType(), this );");
}
/**
* Writes the definition of the method responsible of handling this event.
*
* @param writer The {@link SourceWriter}.
*/
public void writeHandlerMethod(SourceWriter writer) {
writer.println("");
writer.println("@Override");
writer.println("public final void " + handlerMethodName + "( final " + eventTypeName
+ " event ) {");
writer.indent();
writer.println("getPresenter( new NotifyingAsyncCallback<" + presenterInspector.getPresenterClassName()
+ ">(getEventBus()) {");
writer.indent();
writer.println("@Override");
writer.println("public void success(final " + presenterInspector.getPresenterClassName()
+ " presenter) {");
writer.indent();
writer.println("Scheduler.get().scheduleDeferred( new Command() {");
writer.indent();
writer.println("@Override");
writer.println("public void execute() {");
writer.indent();
writer.println("presenter." + functionName + "( event );");
writer.outdent();
writer.println("}");
writer.outdent();
writer.println("} );");
writer.outdent();
writer.println("}");
writer.outdent();
writer.println("} );");
writer.outdent();
writer.println("}");
}
}