001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util.function;
018
019import java.util.Objects;
020import java.util.Optional;
021import java.util.concurrent.atomic.AtomicReference;
022import java.util.concurrent.locks.Lock;
023import java.util.concurrent.locks.ReentrantLock;
024import java.util.function.Consumer;
025import java.util.function.Predicate;
026import java.util.function.Supplier;
027
028public final class Suppliers {
029    private Suppliers() {
030    }
031
032    /**
033     * Returns a supplier which caches the result of the first call to {@link Supplier#get()}and returns that value on
034     * subsequent calls.
035     *
036     * @param  supplier the delegate {@link Supplier}.
037     * @param  <T>      the type of results supplied by this supplier.
038     * @return          the result fo the first call to the delegate's {@link Supplier#get()} method.
039     */
040    public static <T> Supplier<T> memorize(Supplier<T> supplier) {
041        final AtomicReference<T> valueHolder = new AtomicReference<>();
042        final Lock lock = new ReentrantLock();
043        return new Supplier<>() {
044            @Override
045            public T get() {
046                T supplied = valueHolder.get();
047                if (supplied == null) {
048                    lock.lock();
049                    try {
050                        supplied = valueHolder.get();
051                        if (supplied == null) {
052                            supplied = Objects.requireNonNull(supplier.get(), "Supplier should not return null");
053                            valueHolder.lazySet(supplied);
054                        }
055                    } finally {
056                        lock.unlock();
057                    }
058                }
059                return supplied;
060            }
061        };
062    }
063
064    /**
065     * Returns a supplier which caches the result of the first call to {@link Supplier#get()} and returns that value on
066     * subsequent calls.
067     *
068     * @param  supplier the delegate {@link Supplier}.
069     * @param  consumer a consumer for any exception thrown by the {@link ThrowingSupplier#get()}.
070     * @param  <T>      the type of results supplied by this supplier.
071     * @return          the result fo the first call to the delegate's {@link Supplier#get()} method.
072     */
073    public static <T> Supplier<T> memorize(ThrowingSupplier<T, ? extends Exception> supplier, Consumer<Exception> consumer) {
074        final AtomicReference<T> valueHolder = new AtomicReference<>();
075        final Lock lock = new ReentrantLock();
076        return new Supplier<>() {
077            @Override
078            public T get() {
079                T supplied = valueHolder.get();
080                if (supplied == null) {
081                    lock.lock();
082                    try {
083                        supplied = valueHolder.get();
084                        if (supplied == null) {
085                            try {
086                                supplied = Objects.requireNonNull(supplier.get(), "Supplier should not return null");
087                                valueHolder.lazySet(supplied);
088                            } catch (Exception e) {
089                                consumer.accept(e);
090                            }
091                        }
092                    } finally {
093                        lock.unlock();
094                    }
095                }
096                return supplied;
097            }
098        };
099    }
100
101    /**
102     * Returns a supplier that return a constant value.
103     *
104     * @param  value the constant value to return.
105     * @param  <T>   the type of results supplied by this supplier.
106     * @return       the supplied {@code value}.
107     */
108    public static <T> Supplier<T> constant(T value) {
109        return new Supplier<>() {
110            @Override
111            public T get() {
112                return value;
113            }
114        };
115    }
116
117    /**
118     * Returns the first non null value provide by the given suppliers.
119     *
120     * @param  suppliers a list of supplier.
121     * @param  <T>       the type of results supplied by this supplier.
122     * @return           the optional computed value.
123     */
124    @SafeVarargs
125    public static <T> Optional<T> firstNotNull(ThrowingSupplier<T, Exception>... suppliers) throws Exception {
126        T answer = null;
127
128        for (ThrowingSupplier<T, Exception> supplier : suppliers) {
129            answer = supplier.get();
130            if (answer != null) {
131                break;
132            }
133        }
134
135        return Optional.ofNullable(answer);
136    }
137
138    /**
139     * Returns the first value provide by the given suppliers that matches the given predicate.
140     *
141     * @param  predicate the predicate used to evaluate the computed values.
142     * @param  suppliers a list fo supplier.
143     * @param  <T>       the type of results supplied by this supplier.
144     * @return           the optional matching value.
145     */
146    public static <T> Optional<T> firstMatching(Predicate<T> predicate, ThrowingSupplier<T, Exception>... suppliers)
147            throws Exception {
148        T answer = null;
149
150        for (ThrowingSupplier<T, Exception> supplier : suppliers) {
151            answer = supplier.get();
152            if (predicate.test(answer)) {
153                break;
154            }
155        }
156
157        return Optional.ofNullable(answer);
158    }
159}