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