Coverage Report - org.jbehave.core.configuration.AnnotationBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
AnnotationBuilder
97%
144/147
94%
32/34
2.24
AnnotationBuilder$InstantiationFailed
100%
4/4
N/A
2.24
 
 1  
 package org.jbehave.core.configuration;
 2  
 
 3  
 import java.io.ByteArrayInputStream;
 4  
 import java.io.IOException;
 5  
 import java.lang.reflect.Constructor;
 6  
 import java.util.ArrayList;
 7  
 import java.util.List;
 8  
 import java.util.Properties;
 9  
 
 10  
 import org.apache.commons.lang.StringUtils;
 11  
 import org.jbehave.core.ConfigurableEmbedder;
 12  
 import org.jbehave.core.Embeddable;
 13  
 import org.jbehave.core.annotations.Configure;
 14  
 import org.jbehave.core.annotations.UsingEmbedder;
 15  
 import org.jbehave.core.annotations.UsingPaths;
 16  
 import org.jbehave.core.annotations.UsingSteps;
 17  
 import org.jbehave.core.embedder.Embedder;
 18  
 import org.jbehave.core.embedder.EmbedderControls;
 19  
 import org.jbehave.core.embedder.StoryControls;
 20  
 import org.jbehave.core.failures.FailureStrategy;
 21  
 import org.jbehave.core.failures.PendingStepStrategy;
 22  
 import org.jbehave.core.io.PathCalculator;
 23  
 import org.jbehave.core.io.StoryFinder;
 24  
 import org.jbehave.core.io.StoryLoader;
 25  
 import org.jbehave.core.io.StoryPathResolver;
 26  
 import org.jbehave.core.parsers.StepPatternParser;
 27  
 import org.jbehave.core.parsers.StoryParser;
 28  
 import org.jbehave.core.reporters.StepdocReporter;
 29  
 import org.jbehave.core.reporters.StoryReporter;
 30  
 import org.jbehave.core.reporters.StoryReporterBuilder;
 31  
 import org.jbehave.core.reporters.ViewGenerator;
 32  
 import org.jbehave.core.steps.CandidateSteps;
 33  
 import org.jbehave.core.steps.InjectableStepsFactory;
 34  
 import org.jbehave.core.steps.InstanceStepsFactory;
 35  
 import org.jbehave.core.steps.ParameterControls;
 36  
 import org.jbehave.core.steps.ParameterConverters;
 37  
 import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
 38  
 import org.jbehave.core.steps.ScanningStepsFactory;
 39  
 import org.jbehave.core.steps.StepCollector;
 40  
 import org.jbehave.core.steps.StepFinder;
 41  
 import org.jbehave.core.steps.StepMonitor;
 42  
 
 43  
 import com.thoughtworks.paranamer.Paranamer;
 44  
 
 45  
 /**
 46  
  * Allows the building of {@link Configuration}, {@link CandidateSteps} and
 47  
  * {@link Embedder} from an annotated class.
 48  
  * 
 49  
  * @author Cristiano GaviĆ£o
 50  
  * @author Mauro Talevi
 51  
  */
 52  
 public class AnnotationBuilder {
 53  
 
 54  
     private final AnnotationMonitor annotationMonitor;
 55  
 
 56  
     private final Class<?> annotatedClass;
 57  
     private final AnnotationFinder finder;
 58  
 
 59  
     public AnnotationBuilder(Class<?> annotatedClass) {
 60  18
         this(annotatedClass, new PrintStreamAnnotationMonitor());
 61  18
     }
 62  
 
 63  21
     public AnnotationBuilder(Class<?> annotatedClass, AnnotationMonitor annotationMonitor) {
 64  21
         this.annotationMonitor = annotationMonitor;
 65  21
         this.annotatedClass = annotatedClass;
 66  21
         this.finder = new AnnotationFinder(annotatedClass);
 67  21
     }
 68  
 
 69  
     public Class<?> annotatedClass() {
 70  1
         return annotatedClass;
 71  
     }
 72  
 
 73  
     /**
 74  
      * Builds a Configuration instance based on annotation {@link Configure}
 75  
      * found in the annotated object instance
 76  
      * 
 77  
      * @return A Configuration instance
 78  
      */
 79  
     public Configuration buildConfiguration() throws AnnotationRequired {
 80  
 
 81  17
         if (!finder.isAnnotationPresent(Configure.class)) {
 82  
             // not using annotation configuration, default to most useful
 83  
             // configuration
 84  4
             return new MostUsefulConfiguration();
 85  
         }
 86  
 
 87  13
         Configuration configuration = configurationElement(finder, "using", Configuration.class);
 88  13
         configuration.useKeywords(configurationElement(finder, "keywords", Keywords.class));
 89  13
         configuration.useFailureStrategy(configurationElement(finder, "failureStrategy", FailureStrategy.class));
 90  13
         configuration.usePendingStepStrategy(configurationElement(finder, "pendingStepStrategy",
 91  
                 PendingStepStrategy.class));
 92  13
         configuration.useParanamer(configurationElement(finder, "paranamer", Paranamer.class));
 93  13
         configuration.useStoryControls(configurationElement(finder, "storyControls", StoryControls.class));
 94  13
         configuration.useStepCollector(configurationElement(finder, "stepCollector", StepCollector.class));
 95  13
         configuration.useStepdocReporter(configurationElement(finder, "stepdocReporter", StepdocReporter.class));
 96  13
         configuration.useStepFinder(configurationElement(finder, "stepFinder", StepFinder.class));
 97  13
         configuration.useStepMonitor(configurationElement(finder, "stepMonitor", StepMonitor.class));
 98  13
         configuration.useStepPatternParser(configurationElement(finder, "stepPatternParser", StepPatternParser.class));
 99  13
         configuration.useStoryLoader(configurationElement(finder, "storyLoader", StoryLoader.class));
 100  13
         configuration.useStoryParser(configurationElement(finder, "storyParser", StoryParser.class));
 101  13
         configuration.useStoryPathResolver(configurationElement(finder, "storyPathResolver", StoryPathResolver.class));
 102  13
         configuration
 103  13
                 .useDefaultStoryReporter(configurationElement(finder, "defaultStoryReporter", StoryReporter.class));
 104  13
         configuration.useStoryReporterBuilder(configurationElement(finder, "storyReporterBuilder",
 105  
                 StoryReporterBuilder.class));
 106  13
         configuration.useViewGenerator(configurationElement(finder, "viewGenerator", ViewGenerator.class));
 107  13
         configuration.useParameterConverters(parameterConverters(finder));
 108  13
         configuration.useParameterControls(configurationElement(finder, "parameterControls", ParameterControls.class));
 109  13
         configuration.usePathCalculator(configurationElement(finder, "pathCalculator", PathCalculator.class));
 110  13
         return configuration;
 111  
     }
 112  
 
 113  
     /**
 114  
      * Builds CandidateSteps using annotation {@link UsingSteps} found in the
 115  
      * annotated object instance and using the configuration build by
 116  
      * {@link #buildConfiguration()}
 117  
      * 
 118  
      * @return A List of CandidateSteps instances
 119  
      */
 120  
     public List<CandidateSteps> buildCandidateSteps() {
 121  7
         return buildCandidateSteps(buildConfiguration());
 122  
     }
 123  
 
 124  
     /**
 125  
      * Builds CandidateSteps using annotation {@link UsingSteps} found in the
 126  
      * annotated object instance and the configuration provided
 127  
      * 
 128  
      * @param configuration the Configuration
 129  
      * @return A List of CandidateSteps instances
 130  
      */
 131  
     public List<CandidateSteps> buildCandidateSteps(Configuration configuration) {
 132  9
         return buildStepsFactory(configuration).createCandidateSteps();
 133  
     }
 134  
 
 135  
     /**
 136  
      * Builds the {@link InjectableStepsFactory} using annotation
 137  
      * {@link UsingSteps} found in the annotated object instance and the
 138  
      * configuration provided
 139  
      * 
 140  
      * @param configuration the Configuration
 141  
      * @return A {@link InjectableStepsFactory}
 142  
      */
 143  
     public InjectableStepsFactory buildStepsFactory(Configuration configuration) {
 144  16
         List<Object> stepsInstances = new ArrayList<Object>();
 145  16
         InjectableStepsFactory factory = null;
 146  16
         if (finder.isAnnotationPresent(UsingSteps.class)) {
 147  12
                         List<Class<Object>> stepsClasses = finder.getAnnotatedClasses(
 148  
                                         UsingSteps.class, Object.class, "instances");
 149  12
                         if (!stepsClasses.isEmpty()) {
 150  10
                                 for (Class<Object> stepsClass : stepsClasses) {
 151  14
                                         stepsInstances.add(instanceOf(Object.class, stepsClass));
 152  13
                                 }
 153  9
                                 factory = new InstanceStepsFactory(configuration,
 154  
                                                 stepsInstances);
 155  
                         }
 156  11
                         List<String> packages = finder.getAnnotatedValues(UsingSteps.class,
 157  
                                         String.class, "packages");
 158  11
                         if (!packages.isEmpty()) {
 159  1
                                 String matchingNames = finder.getAnnotatedValue(UsingSteps.class,
 160  
                                                 String.class, "matchingNames");
 161  1
                                 String notMatchingNames = finder.getAnnotatedValue(UsingSteps.class,
 162  
                                                 String.class, "notMatchingNames");
 163  1
                                 factory = new ScanningStepsFactory(configuration,
 164  1
                                                 packages.toArray(new String[packages.size()]))
 165  1
                                                 .matchingNames(matchingNames).notMatchingNames(notMatchingNames);
 166  
                         }
 167  11
         } else {
 168  4
             annotationMonitor.annotationNotFound(UsingSteps.class, annotatedClass);
 169  
         }
 170  
 
 171  15
         if (factory == null) {
 172  5
             factory = new InstanceStepsFactory(configuration);
 173  
         }
 174  15
         return factory;
 175  
     }
 176  
 
 177  
     public Embedder buildEmbedder() {
 178  10
         if (!finder.isAnnotationPresent(UsingEmbedder.class)) {
 179  1
             return defaultEmbedder();
 180  
         }
 181  
 
 182  9
         boolean batch = control(finder, "batch");
 183  9
         boolean skip = control(finder, "skip");
 184  9
         boolean generateViewAfterStories = control(finder, "generateViewAfterStories");
 185  9
         boolean ignoreFailureInStories = control(finder, "ignoreFailureInStories");
 186  9
         boolean ignoreFailureInView = control(finder, "ignoreFailureInView");
 187  9
         boolean verboseFailures = control(finder, "verboseFailures");
 188  9
         boolean verboseFiltering = control(finder, "verboseFiltering");
 189  9
         String storyTimeouts = finder.getAnnotatedValue(UsingEmbedder.class, String.class, "storyTimeouts");
 190  9
         long storyTimeoutInSecs = finder.getAnnotatedValue(UsingEmbedder.class, Long.class, "storyTimeoutInSecs");
 191  9
         String storyTimeoutInSecsByPath = finder.getAnnotatedValue(UsingEmbedder.class, String.class, "storyTimeoutInSecsByPath");
 192  9
         boolean failOnStoryTimeout = control(finder, "failOnStoryTimeout");
 193  9
         int threads = finder.getAnnotatedValue(UsingEmbedder.class, Integer.class, "threads");
 194  9
         Embedder embedder = embedder();
 195  9
                 EmbedderControls embedderControls = embedder.embedderControls();
 196  9
                 embedderControls.doBatch(batch).doSkip(skip).doGenerateViewAfterStories(generateViewAfterStories)
 197  9
                 .doIgnoreFailureInStories(ignoreFailureInStories).doIgnoreFailureInView(ignoreFailureInView)
 198  9
                 .doVerboseFailures(verboseFailures).doVerboseFiltering(verboseFiltering)
 199  9
                 .doFailOnStoryTimeout(failOnStoryTimeout).useThreads(threads);
 200  9
                 if ( storyTimeoutInSecs != 0 ){
 201  9
                         embedderControls.useStoryTimeoutInSecs(storyTimeoutInSecs);
 202  
                 }
 203  9
                 if ( StringUtils.isNotBlank(storyTimeoutInSecsByPath) ){
 204  0
                         embedderControls.useStoryTimeoutInSecsByPath(storyTimeoutInSecsByPath);
 205  
                 }
 206  9
                 if ( StringUtils.isNotBlank(storyTimeouts) ){
 207  1
                         embedderControls.useStoryTimeouts(storyTimeouts);
 208  
                 }
 209  9
         Configuration configuration = buildConfiguration();
 210  9
         embedder.useConfiguration(configuration);
 211  9
         boolean useStepsFactory = finder.getAnnotatedValue(UsingEmbedder.class, Boolean.class, "stepsFactory");
 212  9
         if (useStepsFactory) {
 213  7
             embedder.useStepsFactory(buildStepsFactory(configuration));
 214  
         } else {
 215  2
             embedder.useCandidateSteps(buildCandidateSteps(configuration));
 216  
         }
 217  9
         List<String> metaFilters = finder.getAnnotatedValues(UsingEmbedder.class, String.class, "metaFilters");
 218  9
         if (!metaFilters.isEmpty()) {
 219  4
             embedder.useMetaFilters(metaFilters);
 220  
         }
 221  9
         Properties systemProperties = loadProperties(finder.getAnnotatedValue(UsingEmbedder.class, String.class,
 222  
                 "systemProperties"));
 223  9
         if (!systemProperties.isEmpty()) {
 224  1
             embedder.useSystemProperties(systemProperties);
 225  
         }
 226  9
         return embedder;
 227  
     }
 228  
 
 229  
     @SuppressWarnings("unchecked")
 230  
     protected Embedder embedder() {
 231  18
         return instanceOf(Embedder.class,
 232  9
                 (Class<? extends Embedder>) finder.getAnnotatedValue(UsingEmbedder.class, Class.class, "embedder"));
 233  
     }
 234  
     
 235  
     protected Embedder defaultEmbedder() {
 236  1
             return new Embedder();
 237  
     }
 238  
 
 239  
     public AnnotationFinder finder() {
 240  0
             return finder;
 241  
     }
 242  
     
 243  
     public List<String> findPaths() {
 244  2
         if (!finder.isAnnotationPresent(UsingPaths.class)) {
 245  1
             return new ArrayList<String>();
 246  
         }
 247  
 
 248  1
         String searchIn = finder.getAnnotatedValue(UsingPaths.class, String.class, "searchIn");
 249  1
         List<String> includes = finder.getAnnotatedValues(UsingPaths.class, String.class, "includes");
 250  1
         List<String> excludes = finder.getAnnotatedValues(UsingPaths.class, String.class, "excludes");
 251  1
         return storyFinder().findPaths(searchIn, includes, excludes);
 252  
     }
 253  
 
 254  
     @SuppressWarnings("unchecked")
 255  
     private StoryFinder storyFinder() {
 256  1
         return instanceOf(StoryFinder.class, (Class<? extends StoryFinder>)finder.getAnnotatedValue(UsingPaths.class, Class.class, "storyFinder"));
 257  
     }
 258  
 
 259  
     private boolean control(AnnotationFinder finder, String name) {
 260  72
         return finder.getAnnotatedValue(UsingEmbedder.class, Boolean.class, name);
 261  
     }
 262  
 
 263  
     private <T> T configurationElement(AnnotationFinder finder, String name, Class<T> type) {
 264  247
         Class<T> implementation = elementImplementation(finder, name);
 265  247
         return instanceOf(type, implementation);
 266  
     }
 267  
 
 268  
     @SuppressWarnings("unchecked")
 269  
         protected <T> Class<T> elementImplementation(AnnotationFinder finder, String name) {
 270  247
         return finder.getAnnotatedValue(Configure.class, Class.class, name);
 271  
     }
 272  
 
 273  
     private Properties loadProperties(String systemPropertiesCSV) {
 274  9
         Properties properties = new Properties();
 275  
         try {
 276  9
             properties.load(new ByteArrayInputStream(systemPropertiesCSV.replace(",", "\n").getBytes()));
 277  0
         } catch (IOException e) {
 278  
             // return empty map
 279  9
         }
 280  9
         return properties;
 281  
     }
 282  
 
 283  
     protected ParameterConverters parameterConverters(AnnotationFinder annotationFinder) {
 284  13
         List<ParameterConverter> converters = new ArrayList<ParameterConverter>();
 285  13
         for (Class<ParameterConverter> converterClass : annotationFinder.getAnnotatedClasses(Configure.class,
 286  
                 ParameterConverter.class, "parameterConverters")) {
 287  3
             converters.add(instanceOf(ParameterConverter.class, converterClass));
 288  3
         }
 289  13
         return new ParameterConverters().addConverters(converters);
 290  
     }
 291  
 
 292  
     protected <T, V extends T> T instanceOf(Class<T> type, Class<V> ofClass) {
 293  
             try { 
 294  
                 // by classloader constructor
 295  
                     try {
 296  274
                             Constructor<V> constructor =
 297  274
                                             ofClass.getConstructor(new Class<?>[]{ClassLoader.class});
 298  13
                             return constructor.newInstance(annotatedClass.getClassLoader());
 299  
                     }
 300  261
                     catch(NoSuchMethodException ns){
 301  
                     }
 302  
                     // by class constructor
 303  
                     try {
 304  261
                             Constructor<V> constructor =
 305  261
                                             ofClass.getConstructor(new Class<?>[]{Class.class});
 306  13
                             return constructor.newInstance(annotatedClass);
 307  
                     }
 308  248
                     catch(NoSuchMethodException ns){
 309  
                     }                         
 310  
                     // by class instance
 311  248
             return ofClass.newInstance();
 312  
             }
 313  1
             catch (Exception e) {
 314  1
             annotationMonitor.elementCreationFailed(ofClass, e);
 315  1
             throw new InstantiationFailed(ofClass, type, e);
 316  
         }
 317  
     }
 318  
 
 319  
     protected AnnotationMonitor annotationMonitor() {
 320  1
         return annotationMonitor;
 321  
     }
 322  
 
 323  
     protected AnnotationFinder annotationFinder() {
 324  1
         return finder;
 325  
     }
 326  
 
 327  
     public Object embeddableInstance() {
 328  8
         return injectEmbedder(buildEmbedder(), annotatedClass);
 329  
     }
 330  
 
 331  
     protected Object injectEmbedder(Embedder embedder, Class<?> annotatedClass) {
 332  
         try {
 333  8
             Object instance = annotatedClass.newInstance();
 334  7
             if (instance instanceof Embeddable) {
 335  6
                 Embeddable embeddable = (Embeddable) instance;
 336  6
                 embeddable.useEmbedder(embedder);
 337  
             }
 338  7
             if (instance instanceof ConfigurableEmbedder) {
 339  2
                 ConfigurableEmbedder configurableEmbedder = (ConfigurableEmbedder) instance;
 340  2
                 configurableEmbedder.useConfiguration(embedder.configuration());
 341  2
                 configurableEmbedder.addSteps(embedder.candidateSteps());
 342  2
                 configurableEmbedder.useStepsFactory(embedder.stepsFactory());
 343  
             }
 344  7
             return instance;
 345  1
         } catch (Exception e) {
 346  1
             annotationMonitor.elementCreationFailed(annotatedClass, e);
 347  1
             throw new InstantiationFailed(annotatedClass, e);
 348  
         }
 349  
     }
 350  
 
 351  
     @SuppressWarnings("serial")
 352  
     public static class InstantiationFailed extends RuntimeException {
 353  
 
 354  
         public InstantiationFailed(Class<?> ofClass, Class<?> type, Throwable cause) {
 355  1
             super("Failed to instantiate " + ofClass + " of type " + type, cause);
 356  1
         }
 357  
 
 358  
         public InstantiationFailed(Class<?> ofClass, Throwable cause) {
 359  1
             super("Failed to instantiate " + ofClass, cause);
 360  1
         }
 361  
 
 362  
     }
 363  
 
 364  
 }