com.alwa.spread.Spreader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spread Show documentation
Show all versions of spread Show documentation
Spread. Its an ergonomic helper library for building Java Test Objects
package com.alwa.spread;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Spreader {
private static final Logger LOGGER = Logger.getLogger(Spreader.class.getName());
private Integer steps;
private Callable factoryTemplate;
private List> mutatorTemplateAndParameters;
private List> factoryParameters;
private Boolean debug;
public Spreader() {
}
public Spreader factory(Callable factoryTemplate) {
captureFactorySpreads(factoryTemplate);
this.factoryTemplate = factoryTemplate;
return this;
}
public Spreader steps(int steps) {
this.steps = steps;
return this;
}
public Stream spread() {
if (isDebugMode()) {
printSplash();
printSummary();
}
validateSpread();
initialiseFactorySpreads();
initialiseMutatorSpreads();
if (isDebugMode()) {
printUnmappedValueMatrix();
}
Object[] dataObjects =
(isCollection() || isMap()) ?
getCollectionDataObjects() :
getNonCollectionDataObjects();
return (Stream)Arrays.stream(dataObjects);
}
public Integer getSteps() {
return steps;
}
private boolean isCollection() {
String factoryTemplateClassName = factoryTemplate.getClass().getName();
return factoryTemplateClassName.startsWith("com.alwa.spread.ListSpread") ||
factoryTemplateClassName.startsWith("com.alwa.spread.SetSpread");
}
private boolean isMap() {
String factoryTemplateClassName = factoryTemplate.getClass().getName();
return factoryTemplateClassName.startsWith("com.alwa.spread.MapSpread");
}
private Object[] getNonCollectionDataObjects() {
Object[] dataObjects = new Object[steps];
for (int i = 0; i < steps; i++) {
dataObjects[i] = createNextObject();
}
return dataObjects;
}
private Object[] getCollectionDataObjects() {
try {
Object[] dataObjects = new Object[1];
Callable factoryTemplate = this.factoryTemplate;
T collectionObject = factoryTemplate.call();
for (int i = 0; i < steps; i++) {
applyMutators(collectionObject);
}
dataObjects[0] = collectionObject;
return dataObjects;
} catch (Exception e) {
throw new SpreaderException("Exception thrown whilst creating next object", e);
}
}
private void printSplash() {
String message ="\n" +
" ____ __\n" +
" / __/ ___ ____ ___ ___ _ ___/ /\n" +
" _\\ \\ / _ \\ / __// -_)/ _ `// _ / \n" +
" /___/ / .__//_/ \\__/ \\_,_/ \\_,_/ \n" +
" /_/ \n";
LOGGER.info(message);
}
private void printSummary() {
LOGGER.info("\n");
LOGGER.info(String.format("Factory template :[%s]\n", factoryTemplate));
if (factoryParameters == null || factoryParameters.size() == 0) {
LOGGER.info("No Factory injector.s\n");
} else {
LOGGER.info(String.format("Factory injectors :[\n%s\n]\n", factoryParameters.stream().map(Spread::toString).collect(Collectors.joining(" \n"))));
}
if (mutatorTemplateAndParameters == null) {
LOGGER.info("No Mutator injectors.\n");
} else {
LOGGER.info(String.format("Mutator injectors :[\n%s\n]\n", mutatorTemplateAndParameters.stream().map(MutatorTemplateAndParameters::toString).collect(Collectors.joining(" \n"))));
}
}
private void printUnmappedValueMatrix() {
LOGGER.info("\n");
LOGGER.info("Spread will use these data arrays to feed though factory injectors....\n");
StringBuilder factoryValueMatrix = new StringBuilder();
factoryValueMatrix.append("\n");
if (factoryParameters.size() > 0) {
factoryValueMatrix.append(
factoryParameters.stream().map(param -> " |----------------Spread----------------| ").collect(Collectors.joining())
);
factoryValueMatrix.append("\n");
IntStream.range(0, steps).forEach(i -> factoryValueMatrix.append(centeredRow(factoryParameters.stream().filter(spread -> spread.getValues().length >= steps).collect(Collectors.toList()), i)));
LOGGER.info(factoryValueMatrix.toString());
} else {
LOGGER.info("Empty factory injectors, no Spreads....\n");
}
LOGGER.info("\n");
LOGGER.info("Spread will use these data arrays to feed though mutator methods....\n");
StringBuilder mutatorValueMatrix = new StringBuilder();
mutatorValueMatrix.append("\n");
if (mutatorTemplateAndParameters != null && !mutatorTemplateAndParameters.isEmpty()) {
mutatorTemplateAndParameters.forEach(mutatorTemplateAndParameter -> {
LOGGER.info(String.format("Mutator....[{%s}]\n", mutatorTemplateAndParameter.getMutatorTemplate()));
mutatorValueMatrix.append(
mutatorTemplateAndParameter.getParameters().stream().map(param -> " |----------------Spread----------------| ").collect(Collectors.joining())
);
mutatorValueMatrix.append("\n");
IntStream.range(0, steps).forEach(i -> mutatorValueMatrix.append(centeredRow(mutatorTemplateAndParameter.getParameters(), i)));
});
LOGGER.info(mutatorValueMatrix.toString());
}
}
private String centeredRow(List> factoryParameters, int rowNumber) {
String centeredRow = factoryParameters.stream()
.map(spread -> centerString(40, spread.getValues()[rowNumber].toString()))
.collect(Collectors.joining(" "));
return centeredRow + "\n";
}
public static String centerString (int width, String s) {
return String.format("%-" + width + "s", String.format("%" + (s.length() + (width - s.length()) / 2) + "s", s));
}
public Spreader mutator(Consumer setterTemplate) {
captureMutatorTemplateAndParameters(setterTemplate.getClass().getDeclaredFields(), setterTemplate);
return this;
}
public Spreader debug() {
this.debug = true;
return this;
}
private boolean isDebugMode() {
return debug != null && debug;
}
private void validateSpread() {
validateFactoryTemplate();
validateSteps();
}
private Object createNextObject() {
Callable factoryTemplate = this.factoryTemplate;
try {
T nextObject = factoryTemplate.call();
return applyMutators(nextObject);
} catch (Exception e) {
throw new SpreaderException("Exception thrown whilst creating next object", e);
}
}
private void captureFactorySpreads(Callable factoryTemplate) {
this.factoryTemplate = factoryTemplate;
captureFactoryParameters(factoryTemplate.getClass().getDeclaredFields(), factoryTemplate);
}
@SafeVarargs
public final Spreader mutators(Consumer... setterTemplates) {
for (Consumer setterTemplate: setterTemplates) {
captureMutatorTemplateAndParameters(setterTemplate.getClass().getDeclaredFields(), setterTemplate);
}
return this;
}
private void captureFactoryParameters(Field[] factoryArguments, Callable factoryTemplate) {
List> factoryParameters = new ArrayList<>();
for (int i = 0; i < factoryArguments.length; i++) {
factoryArguments[i].setAccessible(true);
try {
Object factoryParameter = factoryArguments[i].get(factoryTemplate);
factoryParameters.addAll(spreadsFromParameter(factoryParameter, factoryTemplate));
} catch (IllegalAccessException e) {
throw new SpreaderException("IllegalAccessException when trying to capture Spread parameters", e);
}
}
this.factoryParameters = factoryParameters;
}
private List> spreadsFromParameter(Object parameter, Callable factoryTemplate) {
List> spreadList = new ArrayList<>();
if (parameter instanceof Spread) {
spreadList.add((Spread)parameter);
} else {
spreadList.addAll(getNestedSpreads(parameter));
}
return filterNulls(spreadList);
}
private List> filterNulls(List> spreadList) {
return spreadList.stream().filter(Objects::nonNull).collect(Collectors.toList());
}
private List> spreadsFromParameter(Object parameter, Object factoryTemplate) {
List> spreadList = new ArrayList<>();
if (parameter instanceof Spread) {
spreadList.add((Spread)parameter);
} else {
getNestedSpreads(parameter);
}
return filterNulls(spreadList);
}
private List> getNestedSpreads(Object parameter) {
List> spreadList = new ArrayList<>();
Field[] attributes = parameter.getClass().getDeclaredFields();
for (Field field: attributes) {
if (field.getType().isAssignableFrom(Spread.class)) {
spreadList.add(getSpreadField(field, parameter));
}
}
return spreadList;
}
private Spread getSpreadField(Field field, Object parameter) {
try {
field.setAccessible(true);
return (Spread)field.get(parameter);
} catch (IllegalAccessException e) {
throw new SpreaderException("There was a problem getting a Spread field when capturing params");
}
}
private void captureMutatorTemplateAndParameters(Field[] mutatorArguments, Consumer mutatorTemplate) {
List> mutatorParameters = new ArrayList<>();
for (int i = 0; i < mutatorArguments.length; i++) {
mutatorArguments[i].setAccessible(true);
try {
Object factoryParameter = mutatorArguments[i].get(mutatorTemplate);
mutatorParameters.addAll(spreadsFromParameter(factoryParameter, mutatorTemplate));
} catch (IllegalAccessException e) {
throw new SpreaderException("IllegalAccessException when trying to capture Spread parameters", e);
}
}
if (mutatorTemplateAndParameters == null) {
mutatorTemplateAndParameters = new ArrayList<>();
}
mutatorTemplateAndParameters.add(new MutatorTemplateAndParameters<>(mutatorTemplate, mutatorParameters));
}
private void initialiseFactorySpreads() {
this.factoryParameters
.stream()
.forEach(spread -> spread.init(steps));
}
private void initialiseMutatorSpreads() {
if (mutatorTemplateAndParameters != null) {
mutatorTemplateAndParameters.stream()
.map(MutatorTemplateAndParameters::getParameters)
.forEach(this::initialiseMutatorParams);
}
}
private void initialiseMutatorParams(List> mutatorParams) {
mutatorParams
.stream()
.forEach(spread -> spread.init(steps));
}
private Object applyMutators(T nextObject) {
if (mutatorTemplateAndParameters != null) {
for (MutatorTemplateAndParameters mutatorTemplateAndParameters: this.mutatorTemplateAndParameters) {
mutatorTemplateAndParameters.getMutatorTemplate().accept(nextObject);
}
}
return nextObject;
}
private void validateFactoryTemplate() {
if (factoryTemplate == null) {
String message = "Spreader spread() failure, missing factory. You may need to add a factory to call a constructor, or a factory method, to create instances.";
throw new SpreaderException(message);
}
}
private void validateSteps() {
if (steps == null) {
String message = "Spreader spread() failure, missing steps. You may need to add a step definition to define how many objects to spread.";
throw new SpreaderException(message);
} else if (steps <= 0) {
String message = "Spreader spread() failure, steps Invalid. Steps must be defined as a positive integer and defines how many objects to spread. Invalid Steps: [%s]";
throw new SpreaderException(String.format(message, steps));
}
}
}