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 }