##### Coverage Report - org.jbehave.core.configuration.AnnotationBuilder

Classes in this File | Line Coverage | Branch Coverage | Complexity
AnnotationBuilder |
99% | 111/112
100% | 24/24
| 1.9565217391304348;1.957
AnnotationBuilder$InstantiationFailed |
100% | 4/4
N/A | N/A
| 1.9565217391304348;1.957

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