ScanningStepsFactory.java
package org.jbehave.core.steps;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jbehave.core.annotations.AfterScenario;
import org.jbehave.core.annotations.AfterStories;
import org.jbehave.core.annotations.AfterStory;
import org.jbehave.core.annotations.BeforeScenario;
import org.jbehave.core.annotations.BeforeStories;
import org.jbehave.core.annotations.BeforeStory;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.configuration.Configuration;
import org.junit.After;
import org.junit.Before;
import org.reflections.Reflections;
import org.reflections.ReflectionsException;
import org.reflections.scanners.MethodAnnotationsScanner;
/**
* An {@link InjectableStepsFactory} that scans for classes in the classpath.
* The constructors allows the specification of the package names to scan or the
* root class from which the package name is derived. All classes that include
* any step method annotation ({@link Given}, {@link When}, {@link Then},
* {@link Before}, {@link After}, etc ... ) will be collected in the scan.
* Additional regex filters on the class names are provided via the
* {@link #matchingNames(String)} and {@link #notMatchingNames(String)} methods,
* which by default match all names.
*/
public class ScanningStepsFactory extends AbstractStepsFactory {
private final Set<Class<?>> types = new HashSet<>();
private String matchingRegex = ".*";
private String notMatchingRegex = "";
public ScanningStepsFactory(Configuration configuration, Class<?> root) {
this(configuration, root.getPackage().getName());
}
public ScanningStepsFactory(Configuration configuration,
String... packageNames) {
super(configuration);
for (String packageName : packageNames) {
types.addAll(scanTypes(packageName));
}
}
public ScanningStepsFactory matchingNames(String matchingRegex) {
this.matchingRegex = matchingRegex;
return this;
}
public ScanningStepsFactory notMatchingNames(String notMatchingRegex) {
this.notMatchingRegex = notMatchingRegex;
return this;
}
private Set<Class<?>> scanTypes(String packageName) {
Reflections reflections = new Reflections(packageName,
new MethodAnnotationsScanner());
Set<Class<?>> types = new HashSet<>();
types.addAll(typesAnnotatedWith(reflections, Given.class));
types.addAll(typesAnnotatedWith(reflections, When.class));
types.addAll(typesAnnotatedWith(reflections, Then.class));
types.addAll(typesAnnotatedWith(reflections, Before.class));
types.addAll(typesAnnotatedWith(reflections, After.class));
types.addAll(typesAnnotatedWith(reflections, BeforeScenario.class));
types.addAll(typesAnnotatedWith(reflections, AfterScenario.class));
types.addAll(typesAnnotatedWith(reflections, BeforeStory.class));
types.addAll(typesAnnotatedWith(reflections, AfterStory.class));
types.addAll(typesAnnotatedWith(reflections, BeforeStories.class));
types.addAll(typesAnnotatedWith(reflections, AfterStories.class));
return types;
}
private Set<Class<?>> typesAnnotatedWith(Reflections reflections,
Class<? extends Annotation> annotation) {
Set<Class<?>> types = new HashSet<>();
try {
Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(annotation);
for (Method method : methodsAnnotatedWith) {
types.add(method.getDeclaringClass());
}
} catch (ReflectionsException e) {
// https://github.com/ronmamo/reflections/issues/297
if (!"Scanner MethodAnnotationsScanner was not configured".equals(e.getMessage())) {
throw e;
}
}
return types;
}
@Override
protected List<Class<?>> stepsTypes() {
List<Class<?>> matchingTypes = new ArrayList<>();
for (Class<?> type : types) {
String name = type.getName();
if (name.matches(matchingRegex) && !name.matches(notMatchingRegex)) {
matchingTypes.add(type);
}
}
return matchingTypes;
}
@Override
public Object createInstanceOfType(Class<?> type) {
Object instance;
try {
instance = type.newInstance();
} catch (Exception e) {
throw new StepsInstanceNotFound(type, this);
}
return instance;
}
}