org.instancio.spi.InstancioServiceProvider Maven / Gradle / Ivy
Show all versions of instancio-core Show documentation
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.instancio.spi;
import org.instancio.InstancioApi;
import org.instancio.Node;
import org.instancio.TargetSelector;
import org.instancio.documentation.ExperimentalApi;
import org.instancio.generator.Generator;
import org.instancio.generator.GeneratorSpec;
import org.instancio.generators.Generators;
import org.instancio.settings.AssignmentType;
import org.instancio.settings.Keys;
import org.instancio.settings.Settings;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ServiceLoader;
/**
* Instancio Service Provider Interface for providing custom:
*
*
* - generator mappings via {@link #getGeneratorProvider()}
* - subtype mappings via {@link #getTypeResolver()}
* - class instantiation logic via {@link #getTypeInstantiator()}
* - setter resolution via {@link #getSetterMethodResolver()}
* - annotation processing via {@link #getAnnotationProcessor()}
*
*
* All of the above are {@code default} methods that return {@code null}.
* Implementations of this class can override one or more methods as needed.
* Instancio will invoke each of the above methods only once, therefore,
* these methods may contain initialisation logic.
*
*
This class uses the {@link ServiceLoader} mechanism. Therefore,
* implementations must be registered explicitly.
*
* -
* for a module using the Java module system by adding
* {@code provides org.instancio.spi.InstancioServiceProvider with fully.qualified.ImplementationName;}
*
* -
* otherwise in non-modular code or when read by non-modular code,
* by placing a file named {@code org.instancio.spi.InstancioServiceProvider} under
* {@code /META-INF/services}; The file must contain the fully-qualified name
* of the implementing class.
*
*
*
* Note: only {@link InstancioServiceProvider} (and not the interfaces
* defined within it) can be registered via the {@code ServiceLoader}.
*
* @since 2.11.0
*/
public interface InstancioServiceProvider {
/**
* An optional method that can be used by implementations
* that require access to the context information.
*
* @param context containing additional information
* @since 2.12.0
*/
@ExperimentalApi
default void init(ServiceProviderContext context) {
}
/**
* Returns a {@code GeneratorProvider} implementation.
*
*
Instancio will automatically use generators configured using
* the provider. As an example, consider the following use-case where
* {@code Person} objects need to be created with valid phone numbers.
* To achieve this, a custom {@code Phone} generator is supplied:
*
*
{@code
* Generator phoneGenerator = createPhoneGenerator();
*
* Person person = Instancio.of(Person.class)
* .supply(all(Phone.class), phoneGenerator)
* .create();
* }
*
* To avoid specifying the generator manually, the {@code Phone}
* generator can be registered using the {@code GeneratorProvider}.
* Once registered, the {@code Person} can be created without specifying
* the generator explicitly:
*
*
{@code
* Person person = Instancio.create(Person.class); // uses Phone generator
* }
*
* @return a custom generator provider, or {@code null} if not required
* @since 2.11.0
*/
default GeneratorProvider getGeneratorProvider() {
return null;
}
/**
* Returns a {@code TypeResolver} implementation.
*
* @return a custom type resolver, or {@code null} if not required
* @since 2.11.0
*/
default TypeResolver getTypeResolver() {
return null;
}
/**
* Returns a {@code TypeInstantiator} implementation.
*
* @return a custom type instantiator, or {@code null} if not required
* @since 2.11.0
*/
default TypeInstantiator getTypeInstantiator() {
return null;
}
/**
* Returns a {@code SetterMethodResolver} implementation.
*
* @return a custom setter method resolver, or {@code null} if not required
* @since 3.2.0
*/
@ExperimentalApi
default SetterMethodResolver getSetterMethodResolver() {
return null;
}
/**
* Returns an {@code AnnotationProcessor} implementation.
*
* @return a custom annotation processor, or {@code null} if not required
* @since 4.5.0
*/
@ExperimentalApi
default AnnotationProcessor getAnnotationProcessor() {
return null;
}
/**
* Provides custom {@link Generator} classes.
*
* An implementation of this interface can be returned
* via the {@link #getGeneratorProvider()} method.
*
* @since 2.11.0
*/
interface GeneratorProvider {
/**
* Returns a generator spec for the specified {@code node}.
* The returned spec must also implement the {@link Generator} interface.
*
*
If the implementation does not define a generator for the given
* node, then a {@code null} can be returned.
*
* @param generators provides access to built-in generators
* @param node for which to return a generator
* @return generator spec for the given {@code node}, or {@code null}
* @throws InstancioSpiException if the returned generator spec
* does not implement {@link Generator}
* @since 2.11.0
*/
GeneratorSpec> getGenerator(Node node, Generators generators);
}
/**
* Resolves subtype based on a given class.
*
*
An implementation of this interface can be returned
* via the {@link #getTypeResolver()} method.
*
* @since 2.11.0
*/
interface TypeResolver {
/**
* Returns a subtype for the given {@code type}.
*
*
Similar functionality can be achieved using:
*
*
* - {@link InstancioApi#subtype(TargetSelector, Class)}
* - {@link Settings#mapType(Class, Class)}
*
*
* However, the methods above require specifying the subtypes
* manually. This class allows subtype resolution to be done
* automatically. For example, if this method maps an abstract
* {@code Animal} class to a concrete {@code Dog} class, then
* Instancio will generate objects based on this mapping:
*
*
{@code
* Animal animal = Instancio.create(Animal.class);
* assertThat(animal).isExactlyInstanceOf(Dog.class);
* }
*
* @param type the type to map to a subtype
* @return the subtype class, or {@code null} if no subtype is defined
* @throws InstancioSpiException if the returned class is not
* a subtype of the argument class
* @since 2.11.0
*/
Class> getSubtype(Class> type);
}
/**
* Provides custom instantiation logic.
*
* An implementation of this interface can be returned
* via the {@link #getTypeInstantiator()} method.
*
* @since 2.11.0
*/
interface TypeInstantiator {
/**
* Instantiates an instance of the specified type.
*
*
The main use-case for implementing this interface is to provide
* custom instantiation logic. Instancio will attempt to instantiate
* classes using this method. If the object returned by this method
* is {@code null}, then Instancio will attempt creating an instance
* using internal instantiators.
*
*
Implementations of this interface are expected only to
* instantiate, but not populate objects. By default, Instancio will
* overwrite initialised values unless
* {@link Keys#OVERWRITE_EXISTING_VALUES} is set to {@code false}.
*
* @param type the type to instantiate
* @return an instance of the specified {@code type},
* or {@code null} to delegate instantiation to Instancio
* @throws InstancioSpiException if the returned object is not an
* instance of the specified {@code type}
* @since 2.11.0
*/
Object instantiate(Class> type);
}
/**
* Resolves setter method based on a given node when
* {@link Keys#ASSIGNMENT_TYPE} is set to {@link AssignmentType#METHOD}.
*
*
An implementation of this interface can be returned
* via the {@link #getSetterMethodResolver()} method.
*
* @since 3.2.0
*/
@ExperimentalApi
interface SetterMethodResolver {
/**
* Returns the setter method for the given {@code node}.
* If {@code null} is returned, Instancio will attempt to resolve
* the method using built-in resolvers based on the value of
* {@link Keys#SETTER_STYLE}.
*
* @param node to resolve the setter method for
* @return setter method or {@code null} if method was not resolved
* @since 3.2.0
*/
@ExperimentalApi
Method getSetter(Node node);
}
/**
* Allows customising generated values based on annotations.
*
*
This interface has no methods to implement. Instead, it relies on
* user-defined methods marked with the {@code @AnnotationHandler} annotation
* (see the {@link AnnotationHandler} Javadoc for an example).
*
* @see AnnotationHandler
* @since 4.5.0
*/
@ExperimentalApi
interface AnnotationProcessor {
/**
* Denotes a method for handling annotations.
*
*
The accepted signatures for {@code @AnnotationHandler} methods are:
*
*
* - {@code void example(Annotation annotation, GeneratorSpec> spec, Node node)}
* - {@code void example(Annotation annotation, GeneratorSpec> spec)}
*
*
* where:
*
*
* - the {@code annotation} and {@code spec} parameters can be subtypes
* of {@link Annotation} and {@link GeneratorSpec}, respectively
* - the {@code node} parameter is optional, and can be omitted
* if it's not needed
*
*
* Example:
*
*
{@code
* @Retention(RetentionPolicy.RUNTIME)
* public @interface HexString {
* int length();
* }
*
* public class SampleAnnotationProcessor implements AnnotationProcessor {
*
* @AnnotationHandler
* void handleZipCode(HexString annotation, StringGeneratorSpec spec) {
* spec.hex().length(annotation.length());
* }
* }
* }
*
* @since 4.5.0
*/
@ExperimentalApi
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationHandler {}
}
}