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