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