/******************************************************************************
* 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 java.util.Objects;
/**
* 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 <T> Value type.
* @return A {@link Spplr} that will return the given constant value.
*/
public static <T> Spplr<T> of(T value) {
return new ConstSupplier<>(value);
}
/**
* A {@link Spplr} that returns a constant value.
*/
private static class ConstSupplier<T> implements Spplr<T> {
private final T value;
private ConstSupplier(T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ConstSupplier{");
sb.append("value=").append(value);
sb.append('}');
return sb.toString();
}
}
/**
* 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 <T> Supplied value type.
* @param <R> Transformed value type.
* @return A {@link Spplr} that will apply the given function to the value supplied by the given supplier.
*/
public static <T, R> Spplr<R> map(Spplr<T> supplier, Func<T, R> 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.
*/
private static class TransformingSupplier<T, R> implements Spplr<R> {
private final Spplr<T> supplier;
private final Func<T, R> function;
private TransformingSupplier(Spplr<T> supplier, Func<T, R> function) {
this.supplier = Objects.requireNonNull(supplier, "supplier");
this.function = Objects.requireNonNull(function, "function");
}
@Override
public R get() {
return function.apply(supplier.get());
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TransformingSupplier{");
sb.append("supplier=").append(supplier);
sb.append('}');
return sb.toString();
}
}
// 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 <T> 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 <T> Spplr<T> cache(Spplr<T> 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.
*/
private static class CachingSupplier<T> implements Spplr<T> {
private Spplr<T> supplier;
private volatile T value;
private CachingSupplier(Spplr<T> supplier) {
this.supplier = Objects.requireNonNull(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;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("CachingSupplier{");
sb.append("supplier=").append(supplier);
sb.append(", value=").append(value);
sb.append('}');
return sb.toString();
}
}
}