package uk.co.webamoeba.mockito.collections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.internal.util.reflection.FieldReader;

/**
 * Factory that creates {@link InjectionDetails} from an {@link Object} based on the Mockito annotations
 * {@link InjectMocks} and {@link Mock}. Fields with the {@link InjectMocks} annotation are considered injectees. Fields
 * with the {@link Mock} annotation are considered injectables.
 */
public class MockitoInjectionDetailsFactory {

    public InjectionDetails createInjectionDetails(Object object) {
	Set<Object> injectees = getFieldValues(object, InjectMocks.class);
	Set<Object> injectables = getFieldValues(object, Mock.class);
	return new InjectionDetails(injectees, injectables);
    }

    private Set<Object> getFieldValues(Object object, Class<? extends Annotation> annotationClass) {
	Set<Field> fields = getAnnotatedFields(object, annotationClass);
	return getFieldValues(object, fields);
    }

    private Set<Object> getFieldValues(Object object, Set<Field> fields) {
	Set<Object> values = new HashSet<Object>();
	for (Field field : fields) {
	    values.add(new FieldReader(object, field).read());
	}
	return values;
    }

    private Set<Field> getAnnotatedFields(Object object, Class<? extends Annotation> annotationClass) {
	Set<Field> mockDependentFields = new HashSet<Field>();
	Class<?> clazz = object.getClass();
	while (clazz != Object.class) {
	    Field[] fields = clazz.getDeclaredFields();
	    for (Field field : fields) {
		if (null != field.getAnnotation(annotationClass)) {
		    mockDependentFields.add(field);
		}
	    }
	    clazz = clazz.getSuperclass();
	}
	return mockDependentFields;
    }
}

@@ -569,16 +569,45 @@
 tables.%0A
+ * %0A * @author James Kennard%0A
  */%0Apubl
@@ -649,16 +649,172 @@
 tory %7B%0A%0A
+    /**%0A     * @param object%0A     * @return %7B@link InjectionDetails%7D created based on the Mockito annotations %7B@link Mock%7D and %7B@link InjectMocks%7D.%0A     */%0A
     publ
@@ -1212,16 +1212,27 @@
 s(object
+.getClass()
 , annota
@@ -1298,235 +1298,906 @@
     
-private Set%3CObject%3E getFieldValues(Object object, Set%3CField%3E fields) %7B%0A%09Set%3CObject%3E values = new HashSet%3CObject%3E();%0A%09for (Field field : fields) %7B%0A%09    values.add(new FieldReader(object, field).read());%0A%09%7D%0A%09return values;%0A    %7D%0A
+/**%0A     * @param object%0A     *            %7B@link Object%7D from which to retrieve values%0A     * @param fields%0A     *            The %7B@link Field Fields%7D from which to retrieve values%0A     * @return Values of the %7B@link Field Fields%7D retrieved from the object%0A     */%0A    private Set%3CObject%3E getFieldValues(Object object, Set%3CField%3E fields) %7B%0A%09Set%3CObject%3E values = new HashSet%3CObject%3E();%0A%09for (Field field : fields) %7B%0A%09    values.add(new FieldReader(object, field).read());%0A%09%7D%0A%09return values;%0A    %7D%0A%0A    /**%0A     * @param clazz%0A     *            The %7B@link Class%7D from which we want to retrieve annotated %7B@link Field Fields%7D%0A     * @param annotationClass%0A     *            The annotation to use to identify the %7B@link Field Fields%7D we want to retrieve%0A     * @return The %7B@link Field Fields%7D from the %7B@link Class%7D that are annotated with the %7B@link Annotation%7D%0A     *         %7B@link Class%7D.%0A     */
 %0A   
@@ -2235,29 +2235,30 @@
 dFields(
-Object object
+Class%3C?%3E clazz
 , Class%3C
@@ -2359,76 +2359,266 @@
 );%0A%09
-Class%3C?%3E 
+while (
 clazz 
+!
 = 
-o
+O
 bject.
-getClass();%0A%09while (clazz != Object.class) %7B
+class) %7B%0A%09    // We use getDeclaredFields because we want to get fields that are not otherwise visible. This means we also%0A%09    // want to look through the inheritance hierarchy because getDeclaredFields will not do%0A%09    // this for us
 %0A%09  

Tidy up MockitoInjectionDetailsFactory