All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.instancio.Instancio Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
/*
 * 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.feed.Feed;
import org.instancio.internal.ApiImpl;
import org.instancio.internal.ApiValidator;
import org.instancio.internal.CartesianProductApiImpl;
import org.instancio.internal.ClassApiImpl;
import org.instancio.internal.CollectionsApiImpl;
import org.instancio.internal.FeedApiImpl;
import org.instancio.internal.GenApiImpl;
import org.instancio.internal.MapApiImpl;
import org.instancio.settings.Keys;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import static org.instancio.internal.util.TypeUtils.cast;

/**
 * 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 blank object of the specified class. * *

The created object will have the following properties: * *

    *
  • value fields (strings, numbers, dates, etc) are {@code null}
  • *
  • arrays, collections, and maps are empty
  • *
  • nested POJOs are blank
  • *
* *

For example, assuming the following POJO: * *

{@code
     * class Person {
     *     String name;
     *     LocalDate dateOfBirth;
     *     List phoneNumbers;
     *     Address address;
     * }
     * }
* *

Creating a blank {@code Person} object will produce: * *

{@code
     * Person person = Instancio.createBlank(Person.class);
     *
     * // Output:
     * // Person[
     * //   name=null,
     * //   dateOfBirth=null,
     * //   phoneNumbers=[] // empty List
     * //   address=Address[street=null, city=null, country=null] // blank nested POJO
     * // ]
     * }
* * @param type the type of blank object to create * @param the type of object * @return a blank object of the specified type * @see BaseApi#setBlank(TargetSelector) * @see #ofBlank(Class) * @since 4.7.0 */ @ExperimentalApi public static T createBlank(final Class type) { return ofBlank(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 InstancioClassApi of(final Class type) { return new ClassApiImpl<>(type); } /** * Builder version of the {@link #createBlank(Class)} method * that allows customisation of generated values. * *

For example, assuming the following POJO: * *

{@code
     * class Person {
     *     String name;
     *     LocalDate dateOfBirth;
     *     List phoneNumbers;
     *     Address address;
     * }
     * }
* *

The snippet below will create a blank {@code Person} object * with two initialised fields: * *

{@code
     * Person person = Instancio.ofBlank(Person.class)
     *     .set(field(Address::getCountry), "Canada")
     *     .generate(field(Person::getDateOfBirth), gen -> gen.temporal().localDate().past())
     *     .create()
     *
     * // Sample output:
     * // Person[
     * //   name=null,
     * //   dateOfBirth=1990-12-29,
     * //   phoneNumbers=[]
     * //   address=Address[street=null, city=null, country=Canada]
     * //]
     * }
* * @param type the type of blank object to create * @param the type of object * @return API builder reference * @see BaseApi#setBlank(TargetSelector) * @since 4.7.0 */ @ExperimentalApi public static InstancioClassApi ofBlank(final Class type) { final InstancioClassApi api = new ClassApiImpl<>(type); api.setBlank(Select.root()); return api; } /** * 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)
     *     .create();
     * }
* *

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 InstancioCartesianProductApi 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 InstancioCartesianProductApi 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 InstancioCartesianProductApi 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 InstancioCollectionsApi> ofList(final Class elementType) { return new CollectionsApiImpl(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 InstancioCollectionsApi> ofList(final TypeTokenSupplier elementTypeToken) { return new CollectionsApiImpl(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 InstancioCollectionsApi> ofList(final Model elementModel) { return CollectionsApiImpl.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 InstancioCollectionsApi> ofSet(final Class elementType) { return new CollectionsApiImpl(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 InstancioCollectionsApi> ofSet(final TypeTokenSupplier elementTypeToken) { return new CollectionsApiImpl(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 InstancioCollectionsApi> ofSet(final Model elementModel) { return CollectionsApiImpl.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 InstancioCollectionsApi> ofMap( final Class keyType, final Class valueType) { return new MapApiImpl(Map.class, ApiValidator.validateOfMapKeyOrValueType(keyType), ApiValidator.validateOfMapKeyOrValueType(valueType)); } /** * Builder API for generating a {@link Map} using type tokens. * * @param keyTypeToken specifying the key type * @param valueTypeToken specifying the value type * @param key type * @param value type * @return API builder reference * @since 2.16.0 */ @SuppressWarnings("all") public static InstancioCollectionsApi> ofMap( final TypeTokenSupplier keyTypeToken, final TypeTokenSupplier valueTypeToken) { return new MapApiImpl(Map.class, ApiValidator.validateTypeToken(keyTypeToken), ApiValidator.validateTypeToken(valueTypeToken)); } /** * A shorthand API for generating simple value types, * such as strings, numbers, dates, etc. * *

This API supports generating a single value * using the {@code get()} method: * *

{@code
     * URL url = Instancio.gen().net().url().get();
     *
     * String randomChoice = Instancio.gen().oneOf("foo", "bar", "baz").get();
     * }
* *

as well as generating a list of values * using the {@code list(int size)} method: * *

{@code
     * List pastDates = Instancio.gen().temporal().localDate().past().list(5);
     *
     * List uuids = Instancio.gen().text().uuid().upperCase().withoutDashes().list(5);
     * }
* *

Additionally, the API can generate an infinite stream of values, * for example a stream of strings in the {@code "ABC-123"} format: * *

{@code
     * Stream pastDates = Instancio.gen().text().pattern("#C#C#C-#d#d#d")
     *   .stream()
     *   .limit(100); // limit must be called to avoid an infinite loop
     * }
* * @return API builder reference * @since 5.0.0 */ @ExperimentalApi public static InstancioGenApi gen() { return new GenApiImpl(); } /** * Creates a feed of the specified type. * * @param type the class that defines a feed * @param the type of feed * @return API builder reference * @see #ofFeed(Class) * @since 5.0.0 */ @ExperimentalApi public static F createFeed(final Class type) { return new FeedApiImpl<>(type).create(); } /** * Builder version of {@link #createFeed(Class)} * that allows customising the feed's properties. * * @param type the class that defines a feed * @param the type of feed * @return API builder reference * @see #createFeed(Class) * @since 5.0.0 */ @ExperimentalApi public static InstancioFeedApi ofFeed(final Class type) { return new FeedApiImpl<>(type); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy