PicoAnnotationBuilder.java

package org.jbehave.core.configuration.pico;

import java.util.ArrayList;
import java.util.List;

import org.jbehave.core.annotations.pico.UsingPico;
import org.jbehave.core.configuration.AnnotationBuilder;
import org.jbehave.core.configuration.AnnotationFinder;
import org.jbehave.core.configuration.AnnotationMonitor;
import org.jbehave.core.configuration.AnnotationRequired;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.io.ResourceLoader;
import org.jbehave.core.model.TableTransformers;
import org.jbehave.core.steps.CompositeStepsFactory;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.ParameterConverters;
import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
import org.jbehave.core.steps.pico.PicoStepsFactory;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.AbstractInjector.AmbiguousComponentResolutionException;

/**
 * Extends {@link AnnotationBuilder} to provide PicoContainer-based 
 * dependency injection if {@link UsingPico} annotation is present.
 * 
 * @author Cristiano GaviĆ£o
 * @author Mauro Talevi
 */
public class PicoAnnotationBuilder extends AnnotationBuilder {

    private PicoContainer container;

    public PicoAnnotationBuilder(Class<?> annotatedClass) {
        super(annotatedClass);
    }

    public PicoAnnotationBuilder(Class<?> annotatedClass, AnnotationMonitor annotationMonitor) {
        super(annotatedClass, annotationMonitor);
    }

    @Override
    public Configuration buildConfiguration() throws AnnotationRequired {
        AnnotationFinder finder = annotationFinder();
        if (finder.isAnnotationPresent(UsingPico.class)) {
            @SuppressWarnings("rawtypes")
            List<Class> moduleClasses = finder.getAnnotatedValues(UsingPico.class, Class.class, "modules");
            List<PicoModule> modules = new ArrayList<>();
            for (Class<PicoModule> moduleClass : moduleClasses) {
                try {
                    modules.add(moduleClass.newInstance());
                } catch (Exception e) {
                    annotationMonitor().elementCreationFailed(moduleClass, e);
                }
            }
            if (modules.size() > 0) {
                container = createPicoContainer(modules);                
            }
        } else {
            annotationMonitor().annotationNotFound(UsingPico.class, annotatedClass());
        }
        return super.buildConfiguration();
    }

    @Override
    public InjectableStepsFactory buildStepsFactory(Configuration configuration) {
        InjectableStepsFactory factoryUsingSteps = super.buildStepsFactory(configuration);
        if (container != null) {
            return new CompositeStepsFactory(new PicoStepsFactory(configuration, container), factoryUsingSteps);
        }
        return factoryUsingSteps;
    }

    @Override
    protected ParameterConverters parameterConverters(AnnotationFinder annotationFinder, ResourceLoader resourceLoader,
            TableTransformers tableTransformers) {
        ParameterConverters converters = super.parameterConverters(annotationFinder, resourceLoader, tableTransformers);
        if (container != null) {
            return converters.addConverters(container.getComponents(ParameterConverter.class));
        }
        return converters;
    }

    @Override
    protected <T, V extends T> T instanceOf(final Class<T> type, final Class<V> ofClass) {
        if (container != null) {
            T instance = null;            
            try {
                instance = container.getComponent(type);
            } catch (AmbiguousComponentResolutionException e) {
                instance = container.getComponent(ofClass);
            }
            if (instance != null) {
                return instance;
            }
        }
        return super.instanceOf(type, ofClass);
    }

    @SuppressWarnings("unchecked")
    protected PicoContainer createPicoContainer(List<PicoModule> modules) {
        if (container != null) {
            return container;
        }
        @SuppressWarnings("rawtypes")
        Class containerClass = annotationFinder().getAnnotatedValue(UsingPico.class, Class.class, "container");
        MutablePicoContainer container = instanceOf(MutablePicoContainer.class, containerClass);
        for (PicoModule module : modules) {
            module.configure(container);
        }
        return container;
    }

    protected PicoContainer picoContainer() {
        return container;
    }

}