001    /*
002     * Copyright 2013 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwtmockito.fakes;
017    
018    import com.google.gwt.core.client.GWT;
019    import com.google.gwt.uibinder.client.UiBinder;
020    import com.google.gwt.uibinder.client.UiField;
021    
022    import java.lang.reflect.Field;
023    import java.lang.reflect.InvocationHandler;
024    import java.lang.reflect.Method;
025    import java.lang.reflect.ParameterizedType;
026    import java.lang.reflect.Proxy;
027    import java.util.Arrays;
028    import java.util.LinkedList;
029    import java.util.List;
030    
031    /**
032     * Provides fake implementations of {@link UiBinder}. The fake implementation
033     * populates all (non-provided) {@link UiField} in the target type with objects
034     * obtained from GWT.create. {@link com.google.gwtmockito.GwtMockito} can be
035     * used to control values returned from GWT.create and hence affect how fields
036     * are populated.
037     *
038     * @author ekuefler@google.com (Erik Kuefler)
039     */
040    public class FakeUiBinderProvider implements FakeProvider<UiBinder<?, ?>>{
041    
042      /**
043       * Returns a new instance of FakeUiBinder that implements the given interface.
044       * This is accomplished by returning a dynamic proxy object that delegates
045       * calls to a backing FakeUiBinder.
046       *
047       * @param type interface to be implemented by the returned type. This must
048       *        represent an interface that directly extends {@link UiBinder}.
049       */
050      @Override
051      public UiBinder<?, ?> getFake(final Class<?> type) {
052        return (UiBinder<?, ?>) Proxy.newProxyInstance(
053            FakeUiBinderProvider.class.getClassLoader(),
054            new Class<?>[] {type},
055            new InvocationHandler() {
056              @Override
057              public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
058                // createAndBindUi is the only method defined by UiBinder
059                for (Field field : getAllFields(args[0].getClass())) {
060                  if (field.isAnnotationPresent(UiField.class)
061                      && !field.getAnnotation(UiField.class).provided()) {
062                    field.setAccessible(true);
063                    field.set(args[0], GWT.create(field.getType()));
064                  }
065                }
066                return GWT.create(getUiRootType(type));
067              }
068            });
069      }
070    
071      private <T> Class<?> getUiRootType(Class<T> type) {
072        // The UI root type is the first generic type parameter of the UiBinder
073        ParameterizedType parameterizedType = (ParameterizedType) type.getGenericInterfaces()[0];
074        Class<?> uiRootType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
075        return uiRootType;
076      }
077    
078      private List<Field> getAllFields(Class<?> type) {
079        List<Field> fields = new LinkedList<Field>();
080        fields.addAll(Arrays.asList(type.getDeclaredFields()));
081        if (type.getSuperclass() != null) {
082          fields.addAll(getAllFields(type.getSuperclass()));
083        }
084        return fields;
085      }
086    }