GuiceStepsFactory.java

package org.jbehave.core.steps.guice;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.steps.AbstractStepsFactory;
import org.jbehave.core.steps.InjectableStepsFactory;

/**
 * An {@link InjectableStepsFactory} that uses a Guice {@link Injector} for the
 * composition and instantiation of all components that contain JBehave
 * annotated methods.
 * 
 * @author Cristiano GaviĆ£o
 * @author Paul Hammant
 * @author Mauro Talevi
 */
public class GuiceStepsFactory extends AbstractStepsFactory {

    private final Injector injector;

    public GuiceStepsFactory(Configuration configuration, Injector injector) {
        super(configuration);
        this.injector = injector;
    }

    @Override
    protected List<Class<?>> stepsTypes() {
        List<Class<?>> types = new ArrayList<>();
        addTypes(injector, types);
        return types;
    }

    /**
     * Adds steps types from given injector and recursively its parent
     * 
     * @param injector the current Inject
     * @param types the List of steps types
     */
    private void addTypes(Injector injector, List<Class<?>> types) {
        for (Binding<?> binding : injector.getBindings().values()) {
            Key<?> key = binding.getKey();
            Type type = key.getTypeLiteral().getType();
            if (hasAnnotatedMethods(type)) {
                types.add(((Class<?>)type));
            }
        }
        if (injector.getParent() != null) {
            addTypes(injector.getParent(), types);
        }
    }

    @Override
    public Object createInstanceOfType(Class<?> type) {
        List<Object> instances = new ArrayList<>();
        addInstances(injector, type, instances);
        if (!instances.isEmpty()) {
            return instances.iterator().next();
        }
        return new StepsInstanceNotFound(type, this);
    }

    private void addInstances(Injector injector, Class<?> type, List<Object> instances) {
        for (Binding<?> binding : injector.getBindings().values()) {
            Key<?> key = binding.getKey();
            if (type.equals(key.getTypeLiteral().getType())) {
                instances.add(injector.getInstance(key));
            }
        }
        if (injector.getParent() != null) {
            addInstances(injector.getParent(), type, instances);
        }
    }
}