// Path: src/main/java/no/saua/remock/internal/AnnotationVisitor.java
// public interface AnnotationVisitor<T extends Annotation> {
//     AnnotationVisitorResult visitClass(T annotation);
// 
//     AnnotationVisitorResult visitField(T annotation, Field field);
// 
//     public static class AnnotationVisitorResult {
//         private Set<SpringBeanDefiner> definers = new HashSet<>();
//         private Set<SpyDefinition> spies = new HashSet<>();
//         private Set<Rejecter> rejecters = new HashSet<>();
// 
//         public AnnotationVisitorResult addSpringBeanDefiner(SpringBeanDefiner springBeanDefiner) {
//             definers.add(springBeanDefiner);
//             return this;
//         }
// 
//         public AnnotationVisitorResult addSpy(SpyDefinition spyDefinition) {
//             spies.add(spyDefinition);
//             return this;
//         }
// 
//         public AnnotationVisitorResult addRejecter(Rejecter rejecter) {
//             rejecters.add(rejecter);
//             return this;
//         }
// 
//         Set<SpringBeanDefiner> getDefiners() {
//             return definers;
//         }
// 
//         Set<SpyDefinition> getSpies() {
//             return spies;
//         }
// 
//         Set<Rejecter> getRejecters() {
//             return rejecters;
//         }
//     }
// }
// 
// Path: src/main/java/no/saua/remock/internal/SpyDefinition.java
// public class SpyDefinition extends Entity<SpyDefinition> {
// 
//     private String beanNameToSpy;
//     private Class<?> classToSpy;
// 
//     public SpyDefinition(Class<?> beanClass, String beanName) {
//         classToSpy = beanClass;
//         beanNameToSpy = beanName;
//     }
// 
//     public boolean matches(Class<?> beanClass, String beanName) {
//         return (classToSpy != null && classToSpy.isAssignableFrom(beanClass)) || beanName.equals(beanNameToSpy);
//     }
// 
//     @Override
//     public boolean equals(SpyDefinition other) {
//         return Objects.equals(beanNameToSpy, other.beanNameToSpy) && Objects.equals(classToSpy, other.classToSpy);
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(beanNameToSpy, classToSpy);
//     }
// 
//     public static class SpyInitializer implements BeanPostProcessor, Resettable {
// 
//         private Set<SpyDefinition> spyDefinitions = new HashSet<>();
//         private List<Object> spies = new ArrayList<>();
// 
//         public SpyInitializer(Set<SpyDefinition> spyDefinitions) {
//             this.spyDefinitions = spyDefinitions;
//         }
// 
//         @Override
//         public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//             return bean;
//         }
// 
//         @Override
//         public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//             for (SpyDefinition spyDefinition : spyDefinitions) {
//                 if (spyDefinition.matches(bean.getClass(), beanName)) {
//                     Object spy = Mockito.spy(bean);
//                     spies.add(spy);
//                     return spy;
//                 }
//             }
//             return bean;
//         }
// 
//         @Override
//         public void reset() {
//             for (Object spy: spies) {
//                 Mockito.reset(spy);
//             }
//         }
//     }
// }


import no.saua.remock.internal.AnnotationVisitor;
import no.saua.remock.internal.SpyDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.annotation.*;
import java.lang.reflect.Field;
package no.saua.remock;



/**
 * Wraps the original spring bean with a Mockito spy. Allowing you to verify calls made on that
 * bean. Note that this annotation also is a meta-annotation for {@link Autowired} causing the spied
 * field to also be injected. Usage:
 * <p>
 * <pre>
 * &#064;RunWith(SpringJUnit4ClassRunner.class)
 * &#064;BootstrapWith(RemockBootstrapper.class)
 * &#064;ContextConfiguration(classes = SomeService.class)
 * public class ReplaceWithMockTest {
 *
 *     &#064;WrapWithSpy
 *     public SomeDependency someDependency;
 *
 *     &#064;Inject
 *     public SomeService someService;
 *
 *     &#064;Test
 *     public void test() {
 *         someService.getHalf()
 *         verify(someDependency).method();
 *     }
 * }
 *
 * </pre>
 */
@Autowired
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(WrapWithSpy.WrapWithSpies.class)
public @interface WrapWithSpy {
    Class<?>[] value() default {};

    String[] beanNames() default {};

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface WrapWithSpies {
        WrapWithSpy[] value();
    }

    public static class ReplaceWithSpyAnnotationVisitor implements AnnotationVisitor<WrapWithSpy> {

        @Override
        public AnnotationVisitorResult visitClass(WrapWithSpy annotation) {
            AnnotationVisitorResult result = new AnnotationVisitorResult();
            if (annotation.value().length > 0) {
                for (Class<?> spyClass : annotation.value()) {
// Path: src/main/java/no/saua/remock/internal/AnnotationVisitor.java
// public interface AnnotationVisitor<T extends Annotation> {
//     AnnotationVisitorResult visitClass(T annotation);
// 
//     AnnotationVisitorResult visitField(T annotation, Field field);
// 
//     public static class AnnotationVisitorResult {
//         private Set<SpringBeanDefiner> definers = new HashSet<>();
//         private Set<SpyDefinition> spies = new HashSet<>();
//         private Set<Rejecter> rejecters = new HashSet<>();
// 
//         public AnnotationVisitorResult addSpringBeanDefiner(SpringBeanDefiner springBeanDefiner) {
//             definers.add(springBeanDefiner);
//             return this;
//         }
// 
//         public AnnotationVisitorResult addSpy(SpyDefinition spyDefinition) {
//             spies.add(spyDefinition);
//             return this;
//         }
// 
//         public AnnotationVisitorResult addRejecter(Rejecter rejecter) {
//             rejecters.add(rejecter);
//             return this;
//         }
// 
//         Set<SpringBeanDefiner> getDefiners() {
//             return definers;
//         }
// 
//         Set<SpyDefinition> getSpies() {
//             return spies;
//         }
// 
//         Set<Rejecter> getRejecters() {
//             return rejecters;
//         }
//     }
// }
// 
// Path: src/main/java/no/saua/remock/internal/SpyDefinition.java
// public class SpyDefinition extends Entity<SpyDefinition> {
// 
//     private String beanNameToSpy;
//     private Class<?> classToSpy;
// 
//     public SpyDefinition(Class<?> beanClass, String beanName) {
//         classToSpy = beanClass;
//         beanNameToSpy = beanName;
//     }
// 
//     public boolean matches(Class<?> beanClass, String beanName) {
//         return (classToSpy != null && classToSpy.isAssignableFrom(beanClass)) || beanName.equals(beanNameToSpy);
//     }
// 
//     @Override
//     public boolean equals(SpyDefinition other) {
//         return Objects.equals(beanNameToSpy, other.beanNameToSpy) && Objects.equals(classToSpy, other.classToSpy);
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(beanNameToSpy, classToSpy);
//     }
// 
//     public static class SpyInitializer implements BeanPostProcessor, Resettable {
// 
//         private Set<SpyDefinition> spyDefinitions = new HashSet<>();
//         private List<Object> spies = new ArrayList<>();
// 
//         public SpyInitializer(Set<SpyDefinition> spyDefinitions) {
//             this.spyDefinitions = spyDefinitions;
//         }
// 
//         @Override
//         public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//             return bean;
//         }
// 
//         @Override
//         public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//             for (SpyDefinition spyDefinition : spyDefinitions) {
//                 if (spyDefinition.matches(bean.getClass(), beanName)) {
//                     Object spy = Mockito.spy(bean);
//                     spies.add(spy);
//                     return spy;
//                 }
//             }
//             return bean;
//         }
// 
//         @Override
//         public void reset() {
//             for (Object spy: spies) {
//                 Mockito.reset(spy);
//             }
//         }
//     }
// }


// Path: src/main/java/no/saua/remock/WrapWithSpy.java
import no.saua.remock.internal.AnnotationVisitor;
import no.saua.remock.internal.SpyDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.annotation.*;
import java.lang.reflect.Field;

package no.saua.remock;



/**
 * Wraps the original spring bean with a Mockito spy. Allowing you to verify calls made on that
 * bean. Note that this annotation also is a meta-annotation for {@link Autowired} causing the spied
 * field to also be injected. Usage:
 * <p>
 * <pre>
 * &#064;RunWith(SpringJUnit4ClassRunner.class)
 * &#064;BootstrapWith(RemockBootstrapper.class)
 * &#064;ContextConfiguration(classes = SomeService.class)
 * public class ReplaceWithMockTest {
 *
 *     &#064;WrapWithSpy
 *     public SomeDependency someDependency;
 *
 *     &#064;Inject
 *     public SomeService someService;
 *
 *     &#064;Test
 *     public void test() {
 *         someService.getHalf()
 *         verify(someDependency).method();
 *     }
 * }
 *
 * </pre>
 */
@Autowired
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(WrapWithSpy.WrapWithSpies.class)
public @interface WrapWithSpy {
    Class<?>[] value() default {};

    String[] beanNames() default {};

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface WrapWithSpies {
        WrapWithSpy[] value();
    }

    public static class ReplaceWithSpyAnnotationVisitor implements AnnotationVisitor<WrapWithSpy> {

        @Override
        public AnnotationVisitorResult visitClass(WrapWithSpy annotation) {
            AnnotationVisitorResult result = new AnnotationVisitorResult();
            if (annotation.value().length > 0) {
                for (Class<?> spyClass : annotation.value()) {
                    result.addSpy(new SpyDefinition(spyClass, null));