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

com.github.ykrasik.jaci.util.function.MoreSuppliers Maven / Gradle / Ivy

/******************************************************************************
 * Copyright (C) 2015 Yevgeny Krasik                                          *
 *                                                                            *
 * 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 com.github.ykrasik.jaci.util.function;

import com.github.ykrasik.jaci.util.reflection.ReflectionUtils;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * Supplier related utilities.
 *
 * @author Yevgeny Krasik
 */
public final class MoreSuppliers {
    private MoreSuppliers() { }

    /**
     * Create a supplier that will return the given value.
     *
     * @param value Value to return.
     * @param  Value type.
     * @return A {@link Spplr} that will return the given constant value.
     */
    public static  Spplr of(T value) {
        return new ConstSupplier<>(value);
    }

    /**
     * A {@link Spplr} that returns a constant value.
     */
    @ToString
    @RequiredArgsConstructor
    private static class ConstSupplier implements Spplr {
        private final T value;

        @Override
        public T get() {
            return value;
        }
    }

    /**
     * Create a supplier that will transform the value returned by the given supplier by invoking the given function on it.
     * This operation is also known as 'map'.
     * If the given supplier is a const supplier type (supplies a constant or cache value), the returned supplier
     * will also cache the result of the transformation and not re-calculate it on every call.
     *
     * @param supplier Supplier to transform.
     * @param function Function to transform the supplier with.
     * @param  Supplied value type.
     * @param  Transformed value type.
     * @return A {@link Spplr} that will apply the given function to the value supplied by the given supplier.
     */
    public static  Spplr map(@NonNull Spplr supplier, @NonNull Func function) {
        if (supplier instanceof ConstSupplier || supplier instanceof CachingSupplier) {
            return of(function.apply(supplier.get()));
        }
        return new TransformingSupplier<>(supplier, function);
    }

    /**
     * A {@link Spplr} that returns a the result of applying a function to the value supplied by the wrapped supplier.
     */
    @ToString(of = "supplier")
    @RequiredArgsConstructor
    private static class TransformingSupplier implements Spplr {
        private final Spplr supplier;
        private final Func function;

        @Override
        public R get() {
            return function.apply(supplier.get());
        }
    }

    // TODO: JavaDoc - Returns a supplier that only invokes the supplier once and then returns the cached result.

    /**
     * Wrap the given supplier in a supplier that will only call the underlying supplier once,
     * and then cache the returned value. The following calls will returned the cached value.
     * A 'lazy supplier' of sorts.
     *
     * @param supplier Supplier to cache.
     * @param  Supplied value type.
     * @return A {@link Spplr} that will call the wrapped supplier once and only once and cache the value for future
     *         calls.
     */
    public static  Spplr cache(@NonNull Spplr supplier) {
        return new CachingSupplier<>(supplier);
    }

    /**
     * A {@link Spplr} that calls the underlying supplier once and only once and caches the returned value for future calls.
     */
    @ToString
    private static class CachingSupplier implements Spplr {
        private Spplr supplier;
        private volatile T value;

        private CachingSupplier(Spplr supplier) {
            this.supplier = supplier;
        }

        @Override
        public T get() {
            if (value == null) {
                // Double-checked locking.
                synchronized (this) {
                    if (value == null) {
                        value = supplier.get();
                        supplier = null;    // Release reference to gc.
                    }
                }
            }
            return value;
        }
    }

    /**
     * Create a supplier that will invoke the method specified by the give method name
     * on the given object instance through reflection.
     * The method must be no-args and must return a value of the specified type.
     * The method may be private, in which case it will be made accessible outside it's class.
     *
     * @param instance Instance to invoke the method one.
     * @param methodName Method name to invoke. Method must be no-args and return a value of the specified type.
     * @param suppliedClass Expected return type of the method.
     * @param  Supplier return type.
     * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance.
     */
    public static  Spplr reflectionSupplier(@NonNull Object instance,
                                                  @NonNull String methodName,
                                                  @NonNull Class suppliedClass) {
        final Method method = ReflectionUtils.getNoArgsMethod(instance.getClass(), methodName);
        ReflectionUtils.assertReturnValue(method, suppliedClass);
        return new ReflectionSupplier<>(instance, method);
    }

    /**
     * Similar to {@link #reflectionSupplier(Object, String, Class)}, except allows for an alternative return type.
     * Will first try to create a reflection supplier using the primary class, but if that fails (the method doesn't
     * return the primary class), will re-try with the secondary class.
     * Useful for cases where the return type could be either a primitive or the primitive's boxed version
     * (i.e. Integer.type or Integer.class).
     *
     * @param instance Instance to invoke the method one.
     * @param methodName Method name to invoke. Method must be no-args and return a value of
     *                   either the primary class or the secondary type.
     * @param primaryClass Primary return type of the method. Will be tried first.
     * @param secondaryClass Secondary return type of the method.
     *                       Will be tried if the method doesn't return the primary type.
     * @param  Supplier return type.
     * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance.
     */
    public static  Spplr reflectionSupplier(Object instance,
                                                  String methodName,
                                                  Class primaryClass,
                                                  Class secondaryClass) {
        try {
            return reflectionSupplier(instance, methodName, primaryClass);
        } catch (IllegalArgumentException e) {
            // Try the alternative return value.
            return reflectionSupplier(instance, methodName, secondaryClass);
        }
    }

    /**
     * A {@link Spplr} that invokes a (possibly private) no-args method through reflection.
     *
     * @author Yevgeny Krasik
     */
    @ToString
    @RequiredArgsConstructor
    private static class ReflectionSupplier implements Spplr {
        private final Object instance;
        private final Method method;

        @Override
        public T get() {
            return ReflectionUtils.invokeNoArgs(instance, method);
        }
    }

    /**
     * Create a supplier that will invoke the method specified by the give method name
     * on the given object instance through reflection.
     * The created supplier will supply a {@link List} of values.
     * The method must be no-args and must return an array of the specified type.
     * The method may be private, in which case it will be made accessible outside it's class.
     *
     * @param instance Instance to invoke the method one.
     * @param methodName Method name to invoke. Method must be no-args and return an array of the specified type.
     * @param suppliedClass Expected array return type of the method.
     * @param  Supplier return type.
     * @return A {@link Spplr} that will invoke the no-args method specified by the given name on the given instance.
     */
    public static  Spplr> reflectionListSupplier(Object instance, String methodName, Class suppliedClass) {
        final Spplr supplier = reflectionSupplier(instance, methodName, suppliedClass);
        return new ReflectionListSupplier<>(supplier);
    }

    /**
     * A {@link Spplr} that invokes a (possibly private) no-args method that returns an array of {@code T}
     * through reflection, and wraps the returned array in a {@link List}.
     *
     * @author Yevgeny Krasik
     */
    @ToString
    @RequiredArgsConstructor
    private static class ReflectionListSupplier implements Spplr> {
        private final Spplr supplier;

        @Override
        public List get() {
            return Arrays.asList(supplier.get());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy