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