/** * Copyright 2011 Marcin Mikosik * All rights reserved. */ package com.perunlabs.testinjector.bind; import static com.perunlabs.testinjector.util.Keys.keyOf; import static com.perunlabs.testinjector.util.Keys.keyProvidedBy; import static com.perunlabs.testinjector.util.Reflections.annotatedFields; import static com.perunlabs.testinjector.util.Reflections.getFieldValue; import static com.perunlabs.testinjector.util.Types.isProvider; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.mockito.Mock; import org.mockito.Spy; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.util.Providers; import com.perunlabs.testinjector.Bind; import com.perunlabs.testinjector.util.Collections; import com.perunlabs.testinjector.util.Types; public class FieldBindingsCollector { private final Map<Key<?>, Binding<?>> bindings = Collections.newHashMap(); public Set<Binding<?>> bindings() { return new HashSet<Binding<?>>(bindings.values()); } public void assertNotBound(Key<?> key, Field field) { Binding<?> existingBinding = bindings.get(key); if (existingBinding != null) { throw new DuplicateBindingException(existingBinding, field); } } public void collectBindings(Object test) { collectBindings(test, Mock.class); collectBindings(test, Spy.class); collectBindings(test, Bind.class); } private void collectBindings(Object test, Class<? extends Annotation> annotation) { for (Field field : annotatedFields(test.getClass(), annotation)) { if (isProvider(field)) { createProviderBinding(test, field); } else if (Types.isJavaxProvider(field)) { createJavaxProviderBinding(test, field); } else { createInstanceBinding(test, field); } } } /* * instance binding */ private void createInstanceBinding(Object test, Field field) { Key<?> key = keyOf(field); Object instance = getFieldValue(test, field); /** * It is safe to cast instance to type denoted by key as instance object has * been taken from field of that type. */ castAndAddInstanceBinding(key, instance, field); } private <T> void castAndAddInstanceBinding(Key<T> key, Object value, Field field) { @SuppressWarnings("unchecked") T castValue = (T) value; addInstanceBinding(key, castValue, field); } private <T> void addInstanceBinding(Key<T> key, T value, Field field) { assertNotBound(key, field); bindings.put(key, new ToInstanceBinding<T>(key, value, field)); } /* * provider binding */ private void createProviderBinding(Object test, Field field) { Key<?> key = keyProvidedBy(field); Object instance = getFieldValue(test, field); /** * It is safe to cast instance to type denoted by key as instance object has * been taken from field of that type. */ castAndAddProviderBinding(key, instance, field); } private <T> void castAndAddProviderBinding(Key<T> key, Object value, Field field) { @SuppressWarnings("unchecked") Provider<T> castValue = (Provider<T>) value; addProviderInstanceBinding(key, castValue, field); } private <T> void addProviderInstanceBinding(Key<T> key, Provider<T> value, Field field) { assertNotBound(key, field); bindings.put(key, new ToProviderBinding<T>(key, value, field)); } /* * javax provider binding */ private void createJavaxProviderBinding(Object test, Field field) { Key<?> key = keyProvidedBy(field); Object instance = getFieldValue(test, field); /** * It is safe to cast instance to type denoted by key as instance object has * been taken from field of that type. */ castAndAddJavaxProviderBinding(key, instance, field); } private <T> void castAndAddJavaxProviderBinding(Key<T> key, Object value, Field field) { @SuppressWarnings("unchecked") javax.inject.Provider<T> castValue = (javax.inject.Provider<T>) value; addProviderInstanceBinding(key, Providers.guicify(castValue), field); } }