org.instancio.Instancio Maven / Gradle / Ivy
/*
* 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;
import org.instancio.documentation.ExperimentalApi;
import org.instancio.internal.ApiImpl;
import org.instancio.internal.ApiValidator;
import org.instancio.internal.CartesianProductApiImpl;
import org.instancio.internal.OfClassApiImpl;
import org.instancio.internal.OfCollectionApiImpl;
import org.instancio.internal.OfMapApiImpl;
import org.instancio.settings.Keys;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
/**
* Instancio API for creating instances of a class.
*
* Usage
*
* Create and populate an instance of a class
* Returns an object fully populated with random data with non-null values.
*
* {@code
* Person person = Instancio.create(Person.class);
* }
*
* Customise object's values
* Returns an object populated with random data and some specified fields' values customised.
*
* {@code
* Person person = Instancio.of(Person.class)
* .set(field(Person::getFullName), "Homer Simpson")
* .supply(all(LocalDateTime.class), () -> LocalDateTime.now())
* .generate(field(Phone::getNumber), gen -> gen.text().pattern("(#d#d#d) #d#d#d-#d#d#d#d"))
* .create();
* }
*
* Allow {@code null} values to be generated
* Default behaviour is to populate every field with a non-null value.
* Specifying nullable will randomly generate either {@code null} or non-null value.
*
* {@code
* Person person = Instancio.of(Person.class)
* .withNullable(field(Person::getDateOfBirth))
* .withNullable(all(Gender.class))
* .withNullable(allStrings())
* .create();
* }
*
* Ignore certain fields or classes
* Ignored fields will not be populated. Their values will be {@code null}
* (unless they have a default value assigned).
*
* {@code
* Person person = Instancio.of(Person.class)
* .ignore(fields().named("id")) // Person.id, Address.id, etc.
* .ignore(all(LocalDateTime.class))
* .create();
* }
*
* Creating instances of a class from a {@code Model}
* Instancio builder API parameters can be saved as a {@link Model} object using
* the {@link InstancioApi#toModel()} method. Objects can then be generated based
* on the model. Models can be useful:
*
*
* - as a prototype for creating customised instances of a class
* by overriding model parameters
* - reducing code duplication by re-using the same model in different
* parts of the code
*
*
* {@code
* // Create a model
* Model simpsons = Instancio.of(Person.class)
* .supply(all(Address.class), () -> new Address("742 Evergreen Terrace", "Springfield", "US"))
* .supply(field(Person::getPets), () -> List.of(
* new Pet(PetType.CAT, "Snowball"),
* new Pet(PetType.DOG, "Santa's Little Helper"))
* //... other specs
* .toModel();
*
* // Use the above model as is
* Person person = Instancio.create(simpsons);
*
* // Use the model but override the name
* Person homer = Instancio.of(simpsons).set(field(Person::getName), "Homer").create();
* Person marge = Instancio.of(simpsons).set(field(Person::getName), "Marge").create();
*
* // A model can also used to create another model.
* // This snippet creates a new model from the original model to include a new pet.
* Model withNewPet = Instancio.of(simpsons)
* .supply(field(Person::getPets), () -> List.of(
* new Pet(PetType.PIG, "Plopper"),
* new Pet(PetType.CAT, "Snowball"),
* new Pet(PetType.DOG, "Santa's Little Helper"))
* .toModel();
* }
*
* Creating generic classes
* There are two options for creating instances of a generic type.
*
* Option 1: using a {@link TypeToken}
* {@code
* Pair pairOfFruits = Instancio.create(new TypeToken>() {}); // note the empty '{}' braces
* }
*
* Option 2: using {@code withTypeParameters} to specify the type arguments
* {@code
* Pair pairOfFruits = Instancio.of(Pair.class)
* .withTypeParameters(Apple.class, Banana.class)
* .create();
* }
*
* The second approach allows specifying arbitrary type parameters at runtime,
* however using this method will produce an "unchecked assignment" warning.
*
* @see InstancioApi
* @see Select
* @since 1.0.1
*/
public final class Instancio {
private Instancio() {
// non-instantiable
}
/**
* Creates an instance of the specified class.
*
* @param type to create
* @param the type of object
* @return an object of the specified type
* @since 1.0.1
*/
public static T create(final Class type) {
return of(type).create();
}
/**
* Creates a {@link List} of random size.
*
* Unless configured otherwise, the generated size will be between
* {@link Keys#COLLECTION_MIN_SIZE} and {@link Keys#COLLECTION_MAX_SIZE},
* inclusive.
*
*
To create a list of a specific size, use {@link #ofList(Class)}.
*
* @param elementType class to generate as list elements
* @param element type
* @return API builder reference
* @since 3.0.1
*/
public static List createList(final Class elementType) {
return ofList(elementType).create();
}
/**
* Creates a {@link Set} of random size.
*
* Unless configured otherwise, the generated size will be between
* {@link Keys#COLLECTION_MIN_SIZE} and {@link Keys#COLLECTION_MAX_SIZE},
* inclusive.
*
*
To create a {@code Set} of a specific size, use {@link #ofSet(Class)}.
*
* @param elementType class to generate as set elements
* @param element type
* @return API builder reference
* @since 3.0.1
*/
public static Set createSet(final Class elementType) {
return ofSet(elementType).create();
}
/**
* Creates a {@link Map} of random size.
*
* Unless configured otherwise, the generated size will be between
* {@link Keys#MAP_MIN_SIZE} and {@link Keys#MAP_MAX_SIZE}, inclusive.
*
*
To create a {@code Map} of a specific size, use {@link #ofMap(Class, Class)}.
*
* @param keyType class to generate as map keys
* @param valueType class to generate as map values
* @param key type
* @param value type
* @return API builder reference
* @since 3.0.1
*/
public static Map createMap(final Class keyType, final Class valueType) {
return ofMap(keyType, valueType).create();
}
/**
* Creates an infinite stream of instances of the specified class.
*
* Example:
*
{@code
* List persons = Instancio.stream(Person.class)
* .limit(5)
* .collect(Collectors.toList());
* }
*
* @param type to create
* @param the type of object
* @return an infinite stream of objects of the specified type
* @since 1.1.9
*/
public static Stream stream(final Class type) {
return of(type).stream();
}
/**
* Creates an object of type specified by the type token.
* This method can be used for creating instances of generic types.
*
* Example:
*
{@code
* Pair pair = Instancio.create(new TypeToken>(){});
* }
*
* @param typeToken specifying the type to create
* @param the type of object
* @return an object of the specified type
*/
public static T create(final TypeTokenSupplier typeToken) {
return of(typeToken).create();
}
/**
* Creates an infinite stream of objects of type specified by the type token.
* This method can be used for creating streams of generic types.
*
* Example:
*
{@code
* List> pairs = Instancio.stream(new TypeToken>() {})
* .limit(5)
* .collect(Collectors.toList());
* }
*
* @param typeToken specifying the type to create
* @param the type of object
* @return an infinite stream of objects of the specified type
* @since 1.1.9
*/
public static Stream stream(final TypeTokenSupplier typeToken) {
return of(typeToken).stream();
}
/**
* Creates an object populated using the given model.
* If the object needs to be customised, use the {@link #of(Model)} method.
*
* For an example of how to create a model, see {@link InstancioApi#toModel()}.
*
* @param model a model that will be used as a template for creating the object
* @param the type of object
* @return an object created based on the model
* @see InstancioApi#toModel()
* @see #of(Model)
* @see #stream(Model)
*/
public static T create(final Model model) {
return of(model).create();
}
/**
* Creates an infinite stream of objects populated using the given model.
*
* Example:
*
{@code
* Model model = Instancio.of(Person.class)
* .ignore(field(Person::getId))
* .generate(field(Person::dateOfBirth), gen -> gen.temporal().localDate().past())
* .toModel();
*
* List persons = Instancio.stream(model)
* .limit(5)
* .collect(Collectors.toList());
* }
*
* @param model that will be used to generate the objects
* @param the type of object
* @return an infinite stream of objects created based on the model
* @see #create(Model)
* @see #of(Model)
* @since 2.4.0
*/
public static Stream stream(final Model model) {
return of(model).stream();
}
/**
* Builder version of {@link #create(Class)} that allows customisation of generated values.
*
* {@code
* Person person = Instancio.of(Person.class)
* .generate(allInts(), gen -> gen.ints().min(1).max(99))
* .supply(all(Address.class), () -> new Address("742 Evergreen Terrace", "Springfield", "US"))
* .supply(field("pets"), () -> List.of(
* new Pet(PetType.CAT, "Snowball"),
* new Pet(PetType.DOG, "Santa's Little Helper")))
* .create();
* }
*
* @param type to create
* @param the type of object
* @return API builder reference
*/
public static InstancioOfClassApi of(final Class type) {
return new OfClassApiImpl<>(type);
}
/**
* Builder version of {@link #create(TypeTokenSupplier)} that allows customisation of generated values.
*
* {@code
* List persons = Instancio.of(new TypeToken>(){})
* .generate(allInts(), gen -> gen.ints().min(1).max(99))
* .supply(all(Address.class), () -> new Address("742 Evergreen Terrace", "Springfield", "US"))
* .supply(field("pets"), () -> List.of(
* new Pet(PetType.CAT, "Snowball"),
* new Pet(PetType.DOG, "Santa's Little Helper")))
* .create();
* }
*
* @param typeToken specifying the type to create
* @param the type of object
* @return API builder reference
*/
public static InstancioApi of(final TypeTokenSupplier typeToken) {
return new ApiImpl<>(typeToken);
}
/**
* Builder version of {@link #create(Model)} that allows overriding of generation
* parameters of an existing model.
*
* {@code
* Model personModel = Instancio.of(Person.class)
* .generate(allInts(), gen -> gen.ints().min(1).max(99))
* .supply(all(Address.class), () -> new Address("742 Evergreen Terrace", "Springfield", "US"))
* .supply(field("pets"), () -> List.of(
* new Pet(PetType.CAT, "Snowball"),
* new Pet(PetType.DOG, "Santa's Little Helper")))
* .toModel();
*
* // Use the existing model and add/override generation parameters
* Person simpsonKid = Instancio.of(personModel)
* .generate(field("fullName"), gen -> gen.oneOf("Lisa Simpson", "Bart Simpson"))
* .create();
* }
*
* @param model specifying generation parameters of the object to create
* @param the type of object
* @return API builder reference
*/
public static InstancioApi of(final Model model) {
return new ApiImpl<>(model);
}
/**
* Generates the Cartesian product based on the values specified via the
* {@code with()} method. The Cartesian product is returned as
* a {@link List} in lexicographical order.
*
* Example:
*
{@code
* record Widget(String type, int num) {}
*
* List results = Instancio.ofCartesianProduct(Widget.class)
* .with(field(Widget::type), "FOO", "BAR", "BAZ")
* .with(field(Widget::num), 1, 2, 3)
* .list();
* }
*
* This will produce the following list of {@code Widget} objects:
*
* [Widget[type=FOO, num=1],
* Widget[type=FOO, num=2],
* Widget[type=FOO, num=3],
* Widget[type=BAR, num=1],
* Widget[type=BAR, num=2],
* Widget[type=BAR, num=3],
* Widget[type=BAZ, num=1],
* Widget[type=BAZ, num=2],
* Widget[type=BAZ, num=3]]
*
*
* @param type to create
* @param the type of object
* @return API builder reference
* @see #ofCartesianProduct(Model)
* @see #ofCartesianProduct(TypeTokenSupplier)
* @since 4.0.0
*/
@ExperimentalApi
public static CartesianProductApi ofCartesianProduct(final Class type) {
return new CartesianProductApiImpl<>(ApiValidator.validateOfCartesianProductElementType(type));
}
/**
* Generates the Cartesian product based on the values specified via the
* {@code with()} method. The Cartesian product is returned as
* a {@link List} in lexicographical order.
*
* See {@link #ofCartesianProduct(Class)} for an example.
*
* @param typeToken specifying the type to create
* @param the type of object
* @return API builder reference
* @see #ofCartesianProduct(Class)
* @see #ofCartesianProduct(Model)
* @since 4.0.0
*/
@ExperimentalApi
public static CartesianProductApi ofCartesianProduct(final TypeTokenSupplier typeToken) {
return new CartesianProductApiImpl<>(typeToken);
}
/**
* Generates the Cartesian product based on the values specified via the
* {@code with()} method. The Cartesian product is returned as
* a {@link List} in lexicographical order.
*
* See {@link #ofCartesianProduct(Class)} for an example.
*
* @param model specifying generation parameters of the object to create
* @param the type of object
* @return API builder reference
* @see #ofCartesianProduct(Class)
* @see #ofCartesianProduct(TypeTokenSupplier)
* @since 4.0.0
*/
@ExperimentalApi
public static CartesianProductApi ofCartesianProduct(final Model model) {
return new CartesianProductApiImpl<>(model);
}
/**
* Builder API for generating a {@link List} that allows customising generated values.
*
* @param elementType class to generate as list elements
* @param element type
* @return API builder reference
* @since 2.0.0
*/
@SuppressWarnings("all")
public static InstancioOfCollectionApi> ofList(final Class elementType) {
return new OfCollectionApiImpl(List.class, ApiValidator.validateOfListElementType(elementType));
}
/**
* Builder API for generating a {@link List} using a type token.
*
* @param elementTypeToken specifying the element type
* @param element type
* @return API builder reference
* @since 2.16.0
*/
@SuppressWarnings("all")
public static InstancioOfCollectionApi> ofList(final TypeTokenSupplier elementTypeToken) {
return new OfCollectionApiImpl(List.class, ApiValidator.validateTypeToken(elementTypeToken));
}
/**
* Builder API for generating a {@link List} using the specified model for list elements.
*
* @param elementModel a model for creating list elements
* @param element type
* @return API builder reference
* @since 2.5.0
*/
public static InstancioOfCollectionApi> ofList(final Model elementModel) {
return OfCollectionApiImpl.fromElementModel(cast(List.class), elementModel);
}
/**
* Builder API for generating a {@link Set} that allows customisation of generated values.
*
* @param elementType class to generate as set elements
* @param element type
* @return API builder reference
* @since 2.0.0
*/
@SuppressWarnings("all")
public static InstancioOfCollectionApi> ofSet(final Class elementType) {
return new OfCollectionApiImpl(Set.class, ApiValidator.validateOfSetElementType(elementType));
}
/**
* Builder API for generating a {@link Set} using a type token.
*
* @param elementTypeToken specifying the element type
* @param element type
* @return API builder reference
* @since 2.16.0
*/
@SuppressWarnings("all")
public static InstancioOfCollectionApi> ofSet(final TypeTokenSupplier elementTypeToken) {
return new OfCollectionApiImpl(Set.class, ApiValidator.validateTypeToken(elementTypeToken));
}
/**
* Builder API for generating a {@link Set} using the specified model for list elements.
*
* @param elementModel a model for creating set elements
* @param element type
* @return API builder reference
* @since 2.5.0
*/
public static InstancioOfCollectionApi> ofSet(final Model elementModel) {
return OfCollectionApiImpl.fromElementModel(cast(Set.class), elementModel);
}
/**
* Builder API for generating a {@link Map} that allowss customisation of generated values.
*
* @param keyType class to generate as map keys
* @param valueType class to generate as map values
* @param key type
* @param value type
* @return API builder reference
* @since 2.0.0
*/
@SuppressWarnings("all")
public static InstancioOfCollectionApi