com.gwtplatform.mvp.rebind.ProxyEventMethod.java Source code

Java tutorial

Introduction

Here is the source code for com.gwtplatform.mvp.rebind.ProxyEventMethod.java

Source

/**
 * 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("}");
    }
}