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

org.instancio.Select 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.exception.InstancioApiException;
import org.instancio.internal.ApiValidator;
import org.instancio.internal.selectors.FieldSelectorBuilderImpl;
import org.instancio.internal.selectors.PredicateScopeImpl;
import org.instancio.internal.selectors.PredicateSelectorImpl;
import org.instancio.internal.selectors.PrimitiveAndWrapperSelectorImpl;
import org.instancio.internal.selectors.ScopeImpl;
import org.instancio.internal.selectors.SelectorGroupImpl;
import org.instancio.internal.selectors.SelectorImpl;
import org.instancio.internal.selectors.TargetClass;
import org.instancio.internal.selectors.TargetFieldName;
import org.instancio.internal.selectors.TargetGetterReference;
import org.instancio.internal.selectors.TargetSetterName;
import org.instancio.internal.selectors.TargetSetterReference;
import org.instancio.internal.selectors.TypeSelectorBuilderImpl;
import org.instancio.settings.Keys;

import java.lang.reflect.Field;
import java.util.function.Predicate;

/**
 * Provides static factory methods for creating selectors and selector scopes.
 * Selectors are used for targeting fields and classes.
 * Instancio supports two types of selectors: regular and predicate-based.
 *
 * 

Regular selectors allow matching by exact class (not including subtypes) * and field:

* *
    *
  • {@link #all(Class)}
  • *
  • {@link #field(GetMethodSelector)}
  • *
  • {@link #field(Class, String)}
  • *
  • {@link #field(String)}
  • *
* *

Predicate selectors, as the name suggests, use a {@link Predicate} * for matching targets: * *

    *
  • {@link #fields()}
  • *
  • {@link #types()}
  • *
  • {@link #fields(Predicate)}
  • *
  • {@link #types(Predicate)}
  • *
* *

The first two allow constructing predicates using convenience builder * methods. The last two methods can be used with arbitrary predicates * where the builders are not sufficient.

* *

It should be noted that regular selectors have higher precedence than * predicate selectors. See the * Selectors * section of the User Guide for more details. * * @see Selector * @see SelectorGroup * @see PredicateSelector * @see TargetSelector * @see GroupableSelector * @since 1.2.0 */ public final class Select { /** * Provides a builder for selecting fields based on {@link Predicate Predicates}. * This method can be used for selecting multiple fields in different classes. * The returned builder offers convenience methods for constructing the predicate. * *

The following example will match all fields named {@code lastModified} * declared in the {@code Person} and other classes referenced from {@code Person}.

* *
{@code
     * Person person = Instancio.of(Person.class)
     *     .supply(fields().named("lastModified"), () -> LocalDateTime.now())
     *     .create();
     * }
* *

Specifying only {@code fields()} (without further predicates) * will match all fields declared across the class tree.

* *

The alternative method {@link #fields(Predicate)} can be used to specify * a predicate directly.

* * @return predicate selector builder for matching fields * @see #fields(Predicate) * @see #types(Predicate) * @see #types() * @since 1.6.0 */ public static FieldSelectorBuilder fields() { return new FieldSelectorBuilderImpl(); } /** * Provides a builder for selecting types based on {@link Predicate Predicates}. * This method can be used for selecting multiple types. * The returned builder offers convenience methods for constructing the predicate. * *

The following example will match all types annotated {@code @Embeddable}.

* *
{@code
     * Person person = Instancio.of(Person.class)
     *     .set(types().annotated(Embeddable.class), null)
     *     .create();
     * }
* *

Specifying only {@code types()} (without further predicates) * will match all types referenced in the class tree.

* *

The alternative method {@link #types(Predicate)} can be used to specify * a predicate directly.

* * @return predicate selector builder for matching types * @see #types(Predicate) * @see #fields(Predicate) * @see #fields() * @since 1.6.0 */ public static TypeSelectorBuilder types() { return new TypeSelectorBuilderImpl(); } /** * Select all fields matching the specified predicate. * * @param predicate for matching fields * @return a predicate selector * @see #fields() * @see #types() * @see #types(Predicate) * @since 1.6.0 */ public static PredicateSelector fields(final Predicate predicate) { ApiValidator.notNull(predicate, "Field predicate must not be null"); return PredicateSelectorImpl.builder().fieldPredicate(predicate).build(); } /** * Select all types matching the specified predicate. * * @param predicate for matching types * @return a predicate selector * @see #types() * @see #fields() * @see #fields(Predicate) * @since 1.6.0 */ public static PredicateSelector types(final Predicate> predicate) { ApiValidator.notNull(predicate, "Type predicate must not be null"); return PredicateSelectorImpl.builder().typePredicate(predicate).build(); } /** * Select all instances of the given type, not including subtypes. *

* If the type is a primitive or wrapper, this method only selects the specified type. For example: *

    *
  • {@code all(int.class)} - selects primitive {@code int} but not {@code Integer}
  • *
  • {@code all(Integer.class)} - selects {@code Integer} wrapper but not primitive {@code int}
  • *
*

* In order to select both, primitive {@code int} and wrapper, use the {@link #allInts()}. * * @param type to select * @return a selector for given class * @since 1.2.0 */ public static Selector all(final Class type) { ApiValidator.notNull(type, "Class must not be null"); return SelectorImpl.builder() .target(new TargetClass(type)) .build(); } /** * A convenience method for combining multiple selectors. * *

Example: *

{@code
     * Person person = Instancio.of(Person.class)
     *     .withNullable(all(
     *         all(Gender.class),
     *         all(Phone.class),
     *         field(Person::getDateOfBirth)
     *     ))
     *     .create()
     * }
* * @param selectors to combine * @return a group containing given selectors * @since 1.3.0 */ public static SelectorGroup all(final GroupableSelector... selectors) { ApiValidator.notEmpty(selectors, "Selector group must contain at least one selector"); return new SelectorGroupImpl(selectors); } /** * Selects a field by name in the specified class. The name must match exactly. * * @param declaringClass class declaring the field * @param fieldName field name to select * @return a selector for given field * @throws InstancioApiException if the class has no field with the specified name * @see #field(String) * @see #fields() * @see #fields(Predicate) * @since 1.2.0 */ public static Selector field(final Class declaringClass, final String fieldName) { ApiValidator.notNull(declaringClass, "declaring class must not be null"); ApiValidator.notNull(fieldName, "field name must not be null"); return SelectorImpl.builder() .target(new TargetFieldName(declaringClass, fieldName)) .build(); } /** * Selects a field by name declared in the class being created. * *

Example: *

{@code
     * Person person = Instancio.of(Person.class)
     *     .ignore(field("fullName")) // Person.fullName
     *     .create();
     * }
* * @param fieldName field name to select * @return a selector for given field * @throws InstancioApiException if the class being created has no field with the specified name * @see #field(Class, String) * @see #fields() * @see #fields(Predicate) * @since 1.2.0 */ public static Selector field(final String fieldName) { ApiValidator.notNull(fieldName, "field name must not be null"); return SelectorImpl.builder() .target(new TargetFieldName(null, fieldName)) .build(); } /** * Selects a field based on the given method reference. *

* Internally, the method reference is mapped to a regular field selector * as returned by {@link #field(Class, String)}. Therefore, this * method only works for classes with expected field and method naming * conventions: * *

    *
  • Java beans convention with "get" and "is" prefixes.
  • *
  • Java records convention where method names match property names.
  • *
*

* Examples: * *

{@code
     * // Java beans naming convention
     * field(Person::getName)  -> field(Person.class, "name")
     * field(Person::isActive) -> field(Person.class, "active")
     *
     * // Property-style naming convention (e.g. Java records)
     * field(Person::name)     -> field(Person.class, "name")
     * field(Person::isActive) -> field(Person.class, "isActive")
     * }
* *

Note: for a method reference with a generic return type, * the type must be specified explicitly, for example: * *

{@code
     * class Item {
     *     private T value;
     *
     *     T getValue() { // generic return type
     *         return value;
     *     }
     * }
     *
     * // Option 1:
     * Select.field(Item::getValue)
     *
     * // Option 2:
     * GetMethodSelector, String> getterSelector = Item::getValue;
     * Select.field(getterSelector)
     * }
* * @param methodReference method reference from which field name will be resolved * @param type declaring the method * @param return type of the method * @return a field selector matching the given method reference * @see #field(Class, String) * @since 2.3.0 */ public static Selector field(final GetMethodSelector methodReference) { ApiValidator.notNull(methodReference, "getter method reference must not be null"); return SelectorImpl.builder() .target(new TargetGetterReference(methodReference)) .build(); } /** * Selects a setter by name declared in the class being created. * The setter method must have exactly one parameter. Since this method * resolves the setter by name only (ignoring the parameter type) * it should only be used if there are no overloaded setters. * *

Example *

{@code
     * Person person = Instancio.of(Person.class)
     *     .ignore(setter("setFullName")) // Person.setFullName
     *     .create();
     * }
* *

Note: setter selectors can only be used if * {@link Keys#ASSIGNMENT_TYPE} is set to {@code AssignmentType.METHOD}. * * @param methodName the name of the setter method to select * @return a selector for given method * @throws InstancioApiException if the root class has no method with the specified name * @see #setter(Class, String) * @see #setter(Class, String, Class) * @since 4.0.0 */ @ExperimentalApi public static Selector setter(final String methodName) { return setter(null, methodName, null); } /** * Selects a setter method by name in the specified class. * The method must have exactly one parameter. Since this method resolves * the setter by name only (ignoring the parameter type) * it should only be used if there are no overloaded setters. * *

Note: setter selectors can only be used if * {@link Keys#ASSIGNMENT_TYPE} is set to {@code AssignmentType.METHOD}. * * @param declaringClass class declaring the method * @param methodName method name to select * @return a selector for given method * @throws InstancioApiException if the class has no method with the specified name * @see #setter(String) * @see #setter(SetMethodSelector) * @since 4.0.0 */ @ExperimentalApi public static Selector setter(final Class declaringClass, final String methodName) { return setter(declaringClass, methodName, null); } /** * Selects a setter method by name and parameter type in the specified class. * *

Note: setter selectors can only be used if * {@link Keys#ASSIGNMENT_TYPE} is set to {@code AssignmentType.METHOD}. * * @param declaringClass class declaring the method * @param methodName method name to select * @param parameterType the parameter type of the setter * @return a selector for given method * @throws InstancioApiException if the class has no method with * the specified name and parameter type * @since 4.0.0 */ @ExperimentalApi public static Selector setter(final Class declaringClass, final String methodName, final Class parameterType) { ApiValidator.notNull(methodName, "method name must not be null"); return SelectorImpl.builder() .target(new TargetSetterName(declaringClass, methodName, parameterType)) .build(); } /** * Selects a setter method based on the given method reference. * *

This selector resolves the class that declares the setter and * the method name from the method reference. Since this method resolves * the setter by name only (ignoring the parameter type) * it should only be used if there are no overloaded setters. * *

Note: setter selectors can only be used if * {@link Keys#ASSIGNMENT_TYPE} is set to {@code AssignmentType.METHOD}. * * @param methodReference method reference from which the method will be resolved * @param type declaring the method * @param the argument type of the method * @return a method selector matching the given method reference * @see #setter(Class, String, Class) * @since 4.0.0 */ @ExperimentalApi public static Selector setter(final SetMethodSelector methodReference) { ApiValidator.notNull(methodReference, "setter method reference must not be null"); return SelectorImpl.builder() .target(new TargetSetterReference(methodReference)) .build(); } /** * Selects the root object. * * @return the selector for the root object * @since 2.0.0 */ public static TargetSelector root() { return SelectorImpl.getRootSelector(); } /** * Shorthand for {@code all(String.class)}. * * @return selector for all Strings * @since 1.2.0 */ public static Selector allStrings() { return all(String.class); } /** * Selects all bytes, primitive and wrapper. * * @return selector for all bytes * @since 1.2.0 */ public static Selector allBytes() { return new PrimitiveAndWrapperSelectorImpl(byte.class, Byte.class); } /** * Selects all floats, primitive and wrapper. * * @return selector for all floats * @since 1.2.0 */ public static Selector allFloats() { return new PrimitiveAndWrapperSelectorImpl(float.class, Float.class); } /** * Selects all shorts, primitive and wrapper. * * @return selector for all shorts * @since 1.2.0 */ public static Selector allShorts() { return new PrimitiveAndWrapperSelectorImpl(short.class, Short.class); } /** * Selects all integers, primitive and wrapper. * * @return selector for all integers * @since 1.2.0 */ public static Selector allInts() { return new PrimitiveAndWrapperSelectorImpl(int.class, Integer.class); } /** * Selects all longs, primitive and wrapper. * * @return selector for all longs * @since 1.2.0 */ public static Selector allLongs() { return new PrimitiveAndWrapperSelectorImpl(long.class, Long.class); } /** * Selects all doubles, primitive and wrapper. * * @return selector for all doubles * @since 1.2.0 */ public static Selector allDoubles() { return new PrimitiveAndWrapperSelectorImpl(double.class, Double.class); } /** * Selects all booleans, primitive and wrapper. * * @return selector for all booleans * @since 1.2.0 */ public static Selector allBooleans() { return new PrimitiveAndWrapperSelectorImpl(boolean.class, Boolean.class); } /** * Selects all characters, primitive and wrapper. * * @return selector for all characters * @since 1.2.0 */ public static Selector allChars() { return new PrimitiveAndWrapperSelectorImpl(char.class, Character.class); } /** * Creates a scope for narrowing down a selector's target to a field of the specified class. *

* For example, the following will set all lists within {@code Person.address} object to an empty list. * *

{@code
     * Person person = Instancio.of(Person.class)
     *     .set(all(List.class).within(scope(Person.class, "address")), Collections.emptyList())
     *     .create();
     * }
* * @param targetClass of the scope * @param fieldName declared by the target class * @return a scope for fine-tuning a selector * @see #scope(GetMethodSelector) * @since 1.3.0 */ public static Scope scope(final Class targetClass, final String fieldName) { return new ScopeImpl(new TargetFieldName(targetClass, fieldName), null); } /** * Creates a selector scope for narrowing down a selector's target to the specified class. *

* For example, assuming a {@code Customer} class that has a {@code CustomerConsent} class. * the following will set all booleans within {@code CustomerConsent} to {@code true}. * *

{@code
     * Customer customer = Instancio.of(Customer.class)
     *     .set(allBooleans().within(scope(CustomerConsent.class)), true)
     *     .create();
     * }
* * @param targetClass of the scope * @return a scope for fine-tuning a selector * @since 1.3.0 */ public static Scope scope(final Class targetClass) { ApiValidator.notNull(targetClass, "Scope class must not be null"); return new ScopeImpl(new TargetClass(targetClass), null); } /** * Creates a scope for narrowing down a selector's target to a * matching the specified method reference. * *

This is a convenience method for {@link #scope(Class, String)} * that avoids referring to a field by its name. * * @param methodReference method reference from which field name will be resolved * @param type declaring the method * @param return type of the method * @return a scope for fine-tuning a selector * @see #scope(Class, String) * @since 3.0.0 */ public static Scope scope(final GetMethodSelector methodReference) { ApiValidator.notNull(methodReference, "getter method reference must not be null"); return new ScopeImpl(new TargetGetterReference(methodReference), null); } /** * Creates a scope from the given predicate selector. * * @param selector a predicate selector to use as the scope * @return a scope for fine-tuning a selector * @since 4.2.0 */ @ExperimentalApi public static Scope scope(final PredicateSelector selector) { return new PredicateScopeImpl(selector); } private Select() { // non-instantiable } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy