package org.camunda.bpm.extension.mockito;
import static java.lang.String.format;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.camunda.bpm.extension.mockito.function.CreateInstance.mockInstance;
import static org.camunda.bpm.extension.mockito.function.CreateInstance.newInstanceByDefaultConstructor;
import static org.camunda.bpm.extension.mockito.function.NameForType.juelNameFor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.camunda.bpm.engine.test.mock.Mocks;
import org.camunda.bpm.extension.mockito.function.NameForType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Util class that delegates to
* {@link org.camunda.bpm.engine.test.mock.Mocks#register(String, Object)} and
* {@link org.camunda.bpm.engine.test.mock.Mocks#get(Object)} in a type-safe
* way.
* <p/>
* When mocking JavaDelegate, ExecutionListener or TaskListener, use
* {@link DelegateExpressions} instead.
*/
@SuppressWarnings("unused")
public final class Expressions {
private static final Logger LOG = LoggerFactory.getLogger(Expressions.class);
/**
* Util class, do not instantiate!
*/
private Expressions() {
// hide constructor
}
/**
* Registers mock instances for every public static nested class found in
* parentClass.
*
* @param parentClass the parentClass to scan for nested public static types
*/
public static void registerMockInstancesForNestedTypes(final Class<?> parentClass) {
findNestedClasses(parentClass).forEach(Expressions::registerMockInstance);
}
private static Collection<Class<?>> findNestedClasses(final Class<?> parentClass) {
final List<Class<?>> result = new ArrayList<>();
for (final Class<?> nestedClass : parentClass.getDeclaredClasses()) {
final int modifiers = nestedClass.getModifiers();
if (isPublic(modifiers) && isStatic(modifiers)) {
result.add(nestedClass);
}
}
return result;
}
/**
* Registers mocks via
* {@link org.camunda.bpm.engine.test.mock.Mocks#register(String, Object)} for
* all attributes with Named-types.
*
* @param instance instance who's fields are registered (maybe Junit test or jbehave
* steps).
*/
public static void registerInstancesForFields(final Object instance) {
if (instance == null) {
throw new IllegalArgumentException("instance must not be null!");
}
for (final Field field : instance.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
final Object value = field.get(instance);
if (value != null) {
registerInstance(juelNameFor(field.getType()), value);
}
} catch (final Exception e) {
// fallThrough
}
}
}
/**
* Registers new instances for every public static nested class found in
* parentClass.
*
* @param parentClass the parentClass to scan for nested public static types
*/
public static void registerNewInstancesForNestedTypes(final Class<?> parentClass) {
findNestedClasses(parentClass).forEach(Expressions::registerNewInstance);
}
/**
* Creates and registers mock instance for every given type.
*
* @param types collection of types to mock and register
* @see #registerMockInstances(java.util.Collection)
*/
public static void registerMockInstances(final Class<?>... types) {
if (types == null) {
throw new IllegalArgumentException("types must not be null!");
}
registerMockInstances(Arrays.asList(types));
}
/**
* Creates and registers mock instance for every given type.
*
* @param types collection of types to mock and register
*/
public static void registerMockInstances(final Collection<Class<?>> types) {
types.forEach(Expressions::registerMockInstance);
}
/**
* Creates a mock for the given type and registers it.
*
* @param name the juel name under which the mock is registered
* @param type the type of the mock to create
* @return the registered mock instance
*/
public static <T> T registerMockInstance(final String name, final Class<T> type) {
return registerInstance(name, mockInstance(type));
}
/**
* Creates a mock for the given type and registers it.
*
* @param type the type of the mock to create
* @return the registered mock instance
*/
public static <T> T registerMockInstance(final Class<T> type) {
return registerMockInstance(juelNameFor(type), type);
}
/**
* Creates a new instance for the given type and registers it under the given
* name.
*
* @param name the name for the registered instance
* @param type the type of the instance to create
* @return the registered instance
*/
public static <T> T registerNewInstance(final String name, final Class<T> type) {
return registerInstance(name, newInstanceByDefaultConstructor(type));
}
/**
* Creates a new instance for the given type using the default constructor and
* registers it.
*
* @param type the type of the instance to create
* @return the registered instance
* @see #registerNewInstance(String, Class)
*/
public static <T> T registerNewInstance(final Class<T> type) {
return registerNewInstance(juelNameFor(type), type);
}
/**
* If you already have the instance, register it directly. Name is guessed via
* {@link NameForType}.
*
* @param instance the instance or mock to register
* @return the registered instance
*/
public static <T> T registerInstance(final T instance) {
return registerInstance(juelNameFor(instance), instance);
}
/**
* Delegates to
* {@link org.camunda.bpm.engine.test.mock.Mocks#register(String, Object)}
*
* @param name the juel name for the registered instance
* @param instance the instance to register
* @return the registered instance
*/
public static <T> T registerInstance(final String name, final T instance) {
if (isBlank(name)) {
throw new IllegalArgumentException("name must not be blank!");
}
if (instance == null) {
throw new IllegalArgumentException("instance must not be null!");
}
LOG.debug(format("registered instance: name=%s, value=%s", name, instance));
Mocks.register(name, instance);
return instance;
}
/**
* @param name juel name of the registered instance or mock
* @return registered instance or mock of type
*/
@SuppressWarnings("unchecked")
public static <T> T getRegistered(final String name) {
final T mock = (T) Mocks.get(name);
if (mock == null) {
throw new IllegalStateException("no instance registered for name=" + name);
}
return mock;
}
/**
* @param type the type of the registered instance or mock
* @return registered instance or mock for type
*/
public static <T> T getRegistered(final Class<?> type) {
return getRegistered(juelNameFor(type));
}
/**
* @see org.camunda.bpm.engine.test.mock.Mocks#reset()
*/
public static void reset() {
Mocks.reset();
}
}