Injections.java

/**
 * Copyright (C) 2022 Christopher J. Stehno
 *
 * 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
 *
 *         http://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 io.github.cjstehno.testthings.inject;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Defines some standard injection utilities.
 *
 * <strong>Random Value Objects.</strong> If the <code>value</code> object passed into the injection methods is an
 * instance of a <code>Randomizer</code>, that randomizer will be used to randomly generate the value.
 */
public interface Injections {

    /**
     * Injects a value directly into an object field, if it exists, it not, an error is thrown.
     *
     * @param name  the name of the field to be injected
     * @param value the value (may be a Randomizer).
     * @return the configured Injections instance
     */
    default Injections setField(final String name, final Object value) {
        return set(name, value, false);
    }

    /**
     * If <code>preferSetter</code> is <code>true</code>, an attempt will be made to inject the value using a setter
     * method for the named property, otherwise the field itself will be injected. If the <code>preferSetter</code>
     * value is <code>false</code> the field will be directly injected.
     *
     * @param name         the name of the property or field
     * @param value        the value (may be a Randomizer).
     * @param preferSetter whether to prefer the setter over direct injection
     * @return the configured Injections instance
     */
    Injections set(final String name, final Object value, final boolean preferSetter);

    /**
     * Trys to inject the value using a setter, if it exists, otherwise direct field injection is performed.
     *
     * @param name the property name
     * @param value the value
     * @return the configured injections instance
     */
    default Injections setProperty(final String name, final Object value) {
        return set(name, value, true);
    }

    /**
     * Injects a value directly into all of the fields of the specified type.
     *
     * @param type the field type
     * @param value the value to inject
     * @return the configured injections instance
     */
    default Injections setField(final Class<?> type, final Object value) {
        return set(type, value, false);
    }

    /**
     * Injects a value into the setters setting the specified type - if a setter does not exist, it will try to inject
     * directly into a field of the type.
     *
     * If a setter and a field both match, it will only inject one of them, not both.
     *
     * @param type the field type
     * @param value the value to inject
     * @return the configured injections instance
     */
    default Injections setProperty(final Class<?> type, final Object value) {
        return set(type, value, true);
    }

    /**
     * Injects a value into the setters setting the specified type - if a setter does not exist, it will try to inject
     * directly into a field of the type.
     *
     * If a setter and a field both match, it will only inject one of them, not both.
     *
     * @param type the field type
     * @param value the value to inject
     * @param setter prefers using the setter if it exists
     * @return the configured injections instance
     */
    Injections set(final Class<?> type, final Object value, final boolean setter);

    /**
     * Updates a field value by applying given function to the current value.
     * <p>
     * If the updater function returns a Randomizer, it will be used to generate the updated value.
     *
     * @param name    the property or field name
     * @param updater the updater function
     * @return the instance to the Injections implementation
     */
    default Injections updateField(final String name, final Function<Object, Object> updater) {
        return update(name, updater, false);
    }

    /**
     * Updates a field value by applying given function to the current value of the field. The getter and setter method
     * will be used, if they exist.
     * <p>
     * If the updater function returns a Randomizer, it will be used to generate the updated value.
     *
     * @param name    the property or field name
     * @param updater the updater function
     * @return the instance to the Injections implementation
     */
    default Injections updateProperty(final String name, final Function<Object, Object> updater) {
        return update(name, updater, true);
    }

    /**
     * Updates a field value by applying given function to the current value. If the <code>preferProps</code>
     * parameter is <code>true</code>, the injector will first attempt to use the named property before
     * defaulting update the field directly.
     * <p>
     * If the updater function returns a Randomizer, it will be used to generate the updated value.
     *
     * @param name    the property or field name
     * @param updater the updater function
     * @param preferProps prefer the use of the getter and setter methods over direct field access
     * @return the instance to the Injections implementation
     */
    default Injections update(final String name, final Function<Object, Object> updater, boolean preferProps) {
        return update(name, updater, preferProps, preferProps);
    }

    /**
     * Updates a field value by applying given function to the current value. If the <code>preferProps</code>
     * parameter is <code>true</code>, the injector will first attempt to use the named property before
     * defaulting update the field directly.
     * <p>
     * If the updater function returns a Randomizer, it will be used to generate the updated value.
     *
     * @param name    the property or field name
     * @param updater the updater function
     * @param preferSetter will try to use the setter rather than go directly to the field
     * @param preferGetter will try to use the getter rather than go directly to the field
     * @return the instance to the Injections implementation
     */
    Injections update(final String name, final Function<Object, Object> updater, final boolean preferSetter, final boolean preferGetter);

    /**
     * Allows in-place modification of a value reference from the target instance. The target instance will be passed
     * to the provided consumer for modification.
     *
     * @param name     the name
     * @param modifier the modifier
     * @return the instance of the Injections implementation
     */
    default Injections modifyField(final String name, final Consumer<Object> modifier) {
        return modify(name, modifier, false);
    }

    /**
     * Allows in-place modification of a value reference from the target instance. The target instance will be passed
     * to the provided consumer for modification. The getter will be used, if it exists.
     *
     * @param name     the name
     * @param modifier the modifier
     * @return the instance of the Injections implementation
     */
    default Injections modifyProperty(final String name, final Consumer<Object> modifier) {
        return modify(name, modifier, true);
    }

    /**
     * Allows in-place modification of a value reference from the target instance. The target instance will be passed
     * to the provided consumer for modification.
     *
     * @param name     the name
     * @param modifier the modifier
     * @param preferGetter will try to use the getter rather than going directly to the field
     * @return the instance of the Injections implementation
     */
    Injections modify(final String name, final Consumer<Object> modifier, final boolean preferGetter);
}