All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.gwt.test.uibinder.UiBinderInvocationHandler Maven / Gradle / Ivy

There is a newer version: 0.63
Show newest version
package com.googlecode.gwt.test.uibinder;

import com.google.gwt.dev.Link;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.gwt.test.exceptions.GwtTestUiBinderException;
import com.googlecode.gwt.test.exceptions.ReflectionException;
import com.googlecode.gwt.test.utils.GwtReflectionUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

class UiBinderInvocationHandler implements InvocationHandler {

    private static final Map, GwtEvent> EVENT_PROTOTYPES = new HashMap, GwtEvent>();

    private final Class> proxiedClass;

    UiBinderInvocationHandler(Class> proxiedClass) {
        this.proxiedClass = proxiedClass;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("createAndBindUi")) {
            return createAndBindUi(args[0]);
        } else {
            throw new GwtTestUiBinderException("Not managed method for UiBinder : " + method.getName());
        }
    }

    @SuppressWarnings("unchecked")
    private  void addHandlers(Object owner) {
        Map uiHandlerMethods = GwtReflectionUtils.getAnnotatedMethod(
                owner.getClass(), UiHandler.class);

        for (Map.Entry entry : uiHandlerMethods.entrySet()) {
            for (String uiFieldName : entry.getValue().value()) {
                Widget uiField = GwtReflectionUtils.getPrivateFieldValue(owner, uiFieldName);
                GwtEvent.Type eventType = (GwtEvent.Type) getEventType(entry.getKey());

                H handler = (H) createHandler(uiField, entry.getKey(), owner);

                if (eventType instanceof DomEvent.Type) {
                    uiField.addDomHandler(handler, (DomEvent.Type) eventType);
                } else {

                    // special case for ValueChangeEvent and HasValueChangeHandlers

                    if (uiField instanceof HasValueChangeHandlers
                            && handler instanceof ValueChangeHandler) {
                        ((HasValueChangeHandlers) uiField).addValueChangeHandler((ValueChangeHandler) handler);
                    } else {
                        uiField.addHandler(handler, eventType);
                    }

                }
            }

        }
    }

    /**
     * This method is in charge of the instanciation of DOM object / GWT widget and their binding
     * with @UiField in the owner
     *
     * @param owner The owner UiBinder subclass instance.
     * @return The root component, initially returned by {@link Link
     * UiBinder#createAndBindUi(Object)}.
     */
    private Object createAndBindUi(Object owner) {
        UiBinderParser parser = new UiBinderParser();

        // parse .ui.xml file and inject @UiField
        Object rootComponent = parser.createUiComponent(proxiedClass, owner);
        // handle @UiHandlers
        addHandlers(owner);

        return rootComponent;
    }

    private InvocationHandler createEventHandlerInvocationHandler(final Method uiHandlerMethod,
                                                                  final Object uiHandlerOwner) {

        return new InvocationHandler() {

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return GwtReflectionUtils.callPrivateMethod(uiHandlerOwner, uiHandlerMethod, args);
            }
        };
    }

    private EventHandler createHandler(Widget uiField, Method uiHandlerMethod, Object uiHandlerOwner) {
        Class eventHandlerClass = findHandlerClass(uiHandlerMethod);
        InvocationHandler eventHandlerInvocationHandler = createEventHandlerInvocationHandler(
                uiHandlerMethod, uiHandlerOwner);

        return (EventHandler) Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{eventHandlerClass}, eventHandlerInvocationHandler);
    }

    @SuppressWarnings("unchecked")
    private Class findHandlerClass(Method uiHandlerMethod) {
        Class eventTypeClass = uiHandlerMethod.getParameterTypes()[0];
        String eventHandlerClassName = eventTypeClass.getName().substring(0,
                eventTypeClass.getName().lastIndexOf("Event"))
                + "Handler";

        try {
            return (Class) GwtReflectionUtils.getClass(eventHandlerClassName);
        } catch (ClassNotFoundException e) {
        }

        String anotherEventHandlerClassName = eventTypeClass.getName() + ".Handler";
        try {
            return (Class) GwtReflectionUtils.getClass(anotherEventHandlerClassName);
        } catch (ClassNotFoundException e) {
            // should never happen
            throw new GwtTestUiBinderException("Cannot find handler class for event type '"
                    + eventTypeClass.getName() + "'. By convention, it should be '"
                    + eventHandlerClassName + "' or '" + anotherEventHandlerClassName + "'");
        }
    }

    private GwtEvent.Type getEventType(Method method) {
        Class eventTypeClass = method.getParameterTypes()[0];
        try {
            return GwtReflectionUtils.callStaticMethod(eventTypeClass, "getType");
        } catch (ReflectionException e) {
            // custom event does not respect the "TYPE" private static field
            // 'standard', we have to try to instanciate it to call
            // 'getAssociatedType()' method on it
            GwtEvent eventPrototype = EVENT_PROTOTYPES.get(eventTypeClass);
            if (eventPrototype == null) {
                eventPrototype = (GwtEvent) GwtReflectionUtils.instantiateClass(eventTypeClass);
                EVENT_PROTOTYPES.put(eventTypeClass, eventPrototype);
            }

            return eventPrototype.getAssociatedType();
        }

    }

}