Coverage Report - org.jbehave.core.steps.needle.NeedleStepsFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
NeedleStepsFactory
78%
41/52
64%
18/28
3.2
 
 1  
 package org.jbehave.core.steps.needle;
 2  
 
 3  
 import java.lang.annotation.Annotation;
 4  
 import java.lang.reflect.Method;
 5  
 import java.lang.reflect.Type;
 6  
 import java.util.ArrayList;
 7  
 import java.util.LinkedHashMap;
 8  
 import java.util.List;
 9  
 import java.util.Map;
 10  
 import java.util.Set;
 11  
 
 12  
 import org.jbehave.core.annotations.AsParameterConverter;
 13  
 import org.jbehave.core.configuration.Configuration;
 14  
 import org.jbehave.core.configuration.MostUsefulConfiguration;
 15  
 import org.jbehave.core.steps.AbstractStepsFactory;
 16  
 import org.jbehave.core.steps.CandidateSteps;
 17  
 import org.jbehave.core.steps.InjectableStepsFactory;
 18  
 import org.jbehave.core.steps.ParameterConverters.MethodReturningConverter;
 19  
 import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
 20  
 import org.jbehave.core.steps.Steps;
 21  
 import org.jbehave.core.steps.needle.configuration.CollectInjectionProvidersFromStepsInstance;
 22  
 import org.jbehave.core.steps.needle.configuration.CreateInstanceByDefaultConstructor;
 23  
 import org.jbehave.core.steps.needle.configuration.JBehaveNeedleConfiguration;
 24  
 import org.needle4j.NeedleTestcase;
 25  
 import org.needle4j.injection.InjectionProvider;
 26  
 import org.needle4j.reflection.ReflectionUtil;
 27  
 
 28  
 /**
 29  
  * An {@link InjectableStepsFactory} that uses a Needle {@link InjectionProvider} for the composition and instantiation
 30  
  * of all components that contain JBehave annotated methods.
 31  
  * @author Simon Zambrovski (simon.zambrovski@holisticon.de)
 32  
  * @author Jan Galinski (jan.galinski@holisticon.de)
 33  
  */
 34  
 public class NeedleStepsFactory extends NeedleTestcase implements InjectableStepsFactory {
 35  
 
 36  9
     private final Map<Class<?>, Object> cachedTypeInstances = new LinkedHashMap<Class<?>, Object>();
 37  
 
 38  
     private final Configuration configuration;
 39  
     private Class<?>[] steps;
 40  
 
 41  
     /**
 42  
      * Creates factory with given configuration and step instances.
 43  
      * @param configuration
 44  
      *        JBehave configuration
 45  
      * @param steps
 46  
      *        step classes
 47  
      */
 48  
     public NeedleStepsFactory(final Configuration configuration, final Class<?>... steps) {
 49  2
         this(configuration, null, steps);
 50  2
     }
 51  
 
 52  
     /**
 53  
      * Creates factory with given configuration, injection providers and step instances.
 54  
      * @param configuration
 55  
      *        JBehave configuration
 56  
      * @param steps
 57  
      *        step classes
 58  
      * @param providers
 59  
      *        injection providers.
 60  
      */
 61  
     public NeedleStepsFactory(final Configuration configuration, final Set<InjectionProvider<?>> injectionProviders, final Class<?>... steps) {
 62  9
         super(setUpInjectionProviders(JBehaveNeedleConfiguration.RESOURCE_JBEHAVE_NEEDLE));
 63  9
         if (injectionProviders != null) {
 64  7
             addInjectionProvider(toArray(injectionProviders));
 65  
         }
 66  9
         if (this.configuration == null) {
 67  9
             this.configuration = new MostUsefulConfiguration();
 68  
         } else {
 69  0
             this.configuration = configuration;
 70  
         }
 71  9
         this.steps = steps;
 72  9
     }
 73  
 
 74  
     /**
 75  
      * {@inheritDoc}
 76  
      */
 77  
     public List<CandidateSteps> createCandidateSteps() {
 78  9
         final List<CandidateSteps> result = new ArrayList<CandidateSteps>();
 79  16
         for (final Class<?> type : steps) {
 80  7
             if (hasAnnotatedMethods(type)) {
 81  7
                 configuration.parameterConverters().addConverters(methodReturningConverters(type));
 82  7
                 result.add(new Steps(configuration, type, this));
 83  
             }
 84  
         }
 85  9
         return result;
 86  
     }
 87  
 
 88  
     public Object createInstanceOfType(final Class<?> type) {
 89  8
         final Object instance = cachedTypeInstances.get(type);
 90  8
         if (instance == null) {
 91  
             try {
 92  7
                 final Object stepsInstance = createInstanceUsingNeedleTestCase(type);
 93  7
                 final InjectionProvider<?>[] foundProviders = CollectInjectionProvidersFromStepsInstance.INSTANCE.apply(stepsInstance);
 94  
 
 95  7
                 addInjectionProvider(foundProviders);
 96  
 
 97  7
                 initTestcase(stepsInstance);
 98  
 
 99  7
                 cachedTypeInstances.put(type, stepsInstance);
 100  7
                 return stepsInstance;
 101  0
             } catch (final Exception e) {
 102  0
                 throw new IllegalStateException(e);
 103  
             }
 104  
         }
 105  1
         return instance;
 106  
     }
 107  
 
 108  
     /**
 109  
      * Uses private instantiation methods of NeedleTestCase via {@link ReflectionUtil#invokeMethod(Object, String, Object...)}. First tries to create new
 110  
      * instance with constructor injection, then falls back to default constructor. If creation fails, an IllegalStateException is thrown.
 111  
      * @param type type of instance to create
 112  
      * @return new instance of type. Never <code>null</code>
 113  
      * @throws IllegalStateException when creation fails.
 114  
      */
 115  
     private Object createInstanceUsingNeedleTestCase(final Class<?> type) throws IllegalStateException {
 116  
         try {
 117  7
             Object instance = ReflectionUtil.invokeMethod(this, "getInstanceByConstructorInjection", type);
 118  7
             if (instance == null) {
 119  7
                 instance = CreateInstanceByDefaultConstructor.INSTANCE.apply(type);
 120  
             }
 121  7
             if (instance == null) {
 122  0
                 throw new IllegalStateException("failed to create instance of type " + type.getCanonicalName());
 123  
             }
 124  7
             return instance;
 125  0
         } catch (final Exception e) {
 126  0
             throw new IllegalStateException(e);
 127  
         }
 128  
 
 129  
     }
 130  
 
 131  
     /**
 132  
      * Create parameter converters from methods annotated with @AsParameterConverter
 133  
      * @see {@link AbstractStepsFactory}
 134  
      */
 135  
     private List<ParameterConverter> methodReturningConverters(final Class<?> type) {
 136  7
         final List<ParameterConverter> converters = new ArrayList<ParameterConverter>();
 137  80
         for (final Method method : type.getMethods()) {
 138  73
             if (method.isAnnotationPresent(AsParameterConverter.class)) {
 139  0
                 converters.add(new MethodReturningConverter(method, type, this));
 140  
             }
 141  
         }
 142  7
         return converters;
 143  
     }
 144  
 
 145  
     /**
 146  
      * Add injection providers.
 147  
      * @param providers
 148  
      *        add injection providers after factory construction.
 149  
      */
 150  
     public void addInjectionProviders(final Set<InjectionProvider<?>> providers) {
 151  0
         if (providers != null) {
 152  0
             addInjectionProvider(toArray(providers));
 153  
         }
 154  0
     }
 155  
 
 156  
     /**
 157  
      * Determines if the given type is a {@link Class} containing at least one method annotated with annotations from
 158  
      * package "org.jbehave.core.annotations".
 159  
      * @param type
 160  
      *        the Type of the steps instance
 161  
      * @return A boolean, <code>true</code> if at least one annotated method is found.
 162  
      * @see {@link AbstractStepsFactory}
 163  
      */
 164  
     static boolean hasAnnotatedMethods(final Type type) {
 165  7
         if (type instanceof Class<?>) {
 166  10
             for (final Method method : ((Class<?>)type).getMethods()) {
 167  10
                 for (final Annotation annotation : method.getAnnotations()) {
 168  7
                     if (annotation.annotationType().getName().startsWith("org.jbehave.core.annotations")) {
 169  7
                         return true;
 170  
                     }
 171  
                 }
 172  
             }
 173  
         }
 174  0
         return false;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Read injection providers configuration from a resource.
 179  
      * @param resourceName
 180  
      *        resource name
 181  
      * @return injection providers.
 182  
      */
 183  
     static InjectionProvider<?>[] setUpInjectionProviders(final String resourceName) {
 184  9
         return new JBehaveNeedleConfiguration(resourceName).getInjectionProviders();
 185  
     }
 186  
 
 187  
     /**
 188  
      * Set to array.
 189  
      * @param injectionProviders
 190  
      *        set of providers
 191  
      * @return array of providers
 192  
      */
 193  
     static InjectionProvider<?>[] toArray(final Set<InjectionProvider<?>> injectionProviders) {
 194  7
         return injectionProviders.toArray(new InjectionProvider<?>[injectionProviders.size()]);
 195  
     }
 196  
 
 197  
 }