Provide support for generation of additional types
Generators implementation
For simple type generators use the dev.orne.test.rnd.AbstractTypedGenerator
class:
public class MyGenerator
extends AbstractTypedGenerator<MyType> {
@Override
public @NotNull MyType defaultValue() {
return new MyType();
}
@Override
public @NotNull MyType randomValue() {
final MyType instance = new MyType();
// Generate random values for bean properties
instance.setName(Generators.randomValue(String.class));
return instance;
}
}
If the generator must be used for more than one class or interface simply
override the supports
method checking additional types:
interface MyInterface {}
abstract class MyAbstractType {}
class MyType extends MyAbstractType implements MyInterface {}
public class MyGenerator
extends AbstractTypedGenerator<MyType> {
/**
* Generates MyInterface, MyAbstractType and MyType instances.
*/
@Override
public boolean supports(
final @NotNull Class<?> type) {
return super.supports(type)
|| MyAbstractType.class.equals(type)
|| MyInterface.class.equals(type);
}
@Override
public @NotNull MyType defaultValue() {
return new MyType();
}
@Override
public @NotNull MyType randomValue() {
final MyType instance = new MyType();
// Generate random values for bean properties
instance.setName(Generators.randomValue(String.class));
return instance;
}
}
Generator priority
Registered generators are ordered based on their optional
dev.orne.test.rnd.Priority
annotation. To override a registered generator
simply register a new generator implementation with a higher priority than the
existing generator.
@Priority(Priority.DEFAULT)
public class MyImprovedGenerator ... {
...
}
All the built-in generators have a priority lower than the default priority, so any registered generator without a priority annotation will have higher priority than the built-in ones.
Register new generators
To add support for new types simply implement the dev.orne.test.rnd.Generator
interface and register the new implementations through SPI in a
META-INF/services/dev.orne.test.rnd.Generator
file:
org.example.MyNewGenerator
org.example.AnotherNewGenerator
Parameterizable generators
The generators can accept parameters passed by the user or extracted
from the target (field, argument or return type).
The simplest way to implement a parameterizable generator is to extend the
AbstractTypedParameterizableGenerator
abstract class:
class MyParameters implements GenerationParameters {
...
}
public class MyGenerator
extends AbstractTypedParameterizableGenerator<MyType, MyParameters> {
/**
* {@inheritDoc}
*/
@Override
public @NotNull MyType defaultValue(
@NotNull MyParameters parameters) {
final MyType instance = new MyType();
// Adjust default value to parameters if needed
return instance;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull MyType randomValue(
@NotNull MyParameters parameters) {
final MyType instance = new MyType();
// Adjust random value to parameters
return instance;
}
}
The AbstractParameterizableGenerator
and
AbstractTypedParameterizableGenerator
already detect if the generation
parameters bean implements NullableParameters
and honor it's nullable
property on calls to nullableDefaultValue(...)
and
nullableRandomValue(...)
methods.
See next section for generation parameters implementation details.
Generation parameters
To implement parameterizable generators first choose or create a generation parameters bean. Some interfaces are provided for common parameters:
classDiagram
direction TB
class GenerationParameters {
<<interface>>
}
class NullableParameters {
<<interface>>
isNullable() boolean
setNullable(boolean nullable)
}
GenerationParameters <|-- NullableParameters
class NumberParameters {
<<interface>>
getMin() Number
setMin(Number value)
getMax() Number
setMax(Number value)
}
GenerationParameters <|-- NumberParameters
class SizeParameters {
<<interface>>
getMinSize() int
setMinSize(int value)
getMaxSize() int
setMaxSize(int value)
}
GenerationParameters <|-- SizeParameters
class SimpleGenericParameters {
<<interface>>
getType() Type
setType(Type type)
}
GenerationParameters <|-- SimpleGenericParameters
class KeyValueGenericParameters {
<<interface>>
getKeysType() Type
setKeysType(Type type)
getValuesType() Type
setValuesType(Type type)
}
GenerationParameters <|-- KeyValueGenericParameters
Default implementations of each interface are provided and used in
GenerationParameters
factory methods:
classDiagram
direction TB
class GenerationParameters {
<<interface>>
forNullables() NullableParameters.Builder$
forNumbers() NumberParameters.Builder$
forSizes() SizeParameters.Builder$
forSimpleGenerics() SimpleGenericParameters.Builder$
forKeyValueGenerics() KeyValueGenericParameters.KeysTypeBuilder$
}
class NullableParameters {
<<interface>>
}
GenerationParameters <|-- NullableParameters
class NullableParametersImpl
NullableParameters <|.. NullableParametersImpl
class NumberParameters {
<<interface>>
}
GenerationParameters <|-- NumberParameters
class NumberParametersImpl
NumberParameters <|.. NumberParametersImpl
class SizeParameters {
<<interface>>
}
GenerationParameters <|-- SizeParameters
class SizeParametersImpl
SizeParameters <|.. SizeParametersImpl
class SimpleGenericParameters {
<<interface>>
}
GenerationParameters <|-- SimpleGenericParameters
class SimpleGenericParametersImpl
SimpleGenericParameters <|.. SimpleGenericParametersImpl
class KeyValueGenericParameters {
<<interface>>
}
GenerationParameters <|-- KeyValueGenericParameters
class KeyValueGenericParametersImpl
KeyValueGenericParameters <|.. KeyValueGenericParametersImpl
For example, String
generator uses the StringGenerationParameters
bean,
with the following class hierarchy:
classDiagram
direction TB
class GenerationParameters {
<<interface>>
}
class NullableParameters {
<<interface>>
isNullable() boolean
setNullable(boolean nullable)
}
GenerationParameters <|-- NullableParameters
class SizeParameters {
<<interface>>
getMinSize() int
setMinSize(int value)
getMaxSize() int
setMaxSize(int value)
}
GenerationParameters <|-- SizeParameters
class NullableParametersImpl {
-boolean nullable
}
NullableParameters <|.. NullableParametersImpl
class StringGenerationParameters {
-int minSize
-int maxSize
}
NullableParametersImpl <|-- StringGenerationParameters
SizeParameters <|.. StringGenerationParameters
Generation parameter sources
Generation parameter sources can be of any type.
They can be passed to the Generators
methods
with parameter sources argument or extracted from the targeted generation
targets (fields, parameters, return types).
In addition to aforementioned interfaces the class TypeDeclaration
is used to contain desired type declaration. This is specially useful for
generation of parameterized types (ParameterizedType
) and generic array
types (GenericArrayType
).
classDiagram
direction TB
class NullableParameters {
<<interface>>
-boolean nullable
}
class NumberParameters {
<<interface>>
-Number min
-Number max
}
class SizeParameters {
<<interface>>
-int minSize
-int maxSize
}
class SimpleGenericParameters {
<<interface>>
-Type type
}
class KeyValueGenericParameters {
<<interface>>
-Type keysType
-Type valuesType
}
class TypeDeclaration {
-Type type
}
Targeted generators add Java Validation constraint annotations to generation parameter sources passed to target type generators.
Warning: Actually constraint annotations of static members are ignored.
Generation parameters extraction
When a generation request is received by a parameterizable generator it tries
to extract generation parameters from the received parameter sources.
This is performed by an interface based modular system which main API is
ParametersExtractor
:
classDiagram
direction LR
class ParametersExtractors {
getExtractor(Class~P~ parametersType) ParametersExtractor~P~$
}
class ParametersExtractor~P~ {
<<interface>>
extractParameters(P params, Object[] sources)
extractParameters(P params, Collection~?~ sources)
}
ParametersExtractors ..> ParametersExtractor
class DefaultParametersExtractor~P~ {
- List~ParametersSourceExtractor~ extractors
}
ParametersExtractor <|.. DefaultParametersExtractor
class ParametersSourceExtractor~P, S~ {
<<interface>>
extractParameters(S from, T target)
}
DefaultParametersExtractor ..> ParametersSourceExtractor
Implementations of ParametersSourceExtractor
for the following source-parameters
are provided out-of-the-box:
javax.validation.constraints.NotNull -> NullableParameters
NullableParameters -> NullableParameters
javax.validation.constraints.Min -> NumberParameters
javax.validation.constraints.Max -> NumberParameters
javax.validation.constraints.Positive -> NumberParameters
javax.validation.constraints.PositiveOrZero -> NumberParameters
NumberParameters -> NumberParameters
javax.validation.constraints.Size -> SizeParameters
SizeParameters -> SizeParameters
TypeDeclaration -> SimpleGenericParameters
SimpleGenericParameters -> SimpleGenericParameters
TypeDeclaration -> KeyValueGenericParameters
KeyValueGenericParameters -> KeyValueGenericParameters
To extend the generation parameters extraction capabilities new parameter source extractors can be provided by third party libraries.
For simple generation parameters extractors use the
dev.orne.test.rnd.params.AbstractParametersSourceExtractor
class:
public class MyExtractor
extends AbstractParametersSourceExtractor<MyParameters, MySource> {
/**
* {@inheritDoc}
*/
@Override
public void extractParameters(
final @NotNull MySource from,
final @NotNull MyParameters target) {
// Populate parameters from source
}
}
To add support for new parameter sources register the new
dev.orne.test.rnd.params.ParametersSourceExtractor
interface implementations
SPI in a META-INF/services/dev.orne.test.rnd.params.ParametersSourceExtractor
file:
org.example.MyNewParametersSourceExtractor
org.example.AnotherNewParametersSourceExtractor