001/*
002 * MIT License
003 *
004 * Copyright (c) 2023 Michael Cowan
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in all
014 * copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024
025package io.blt.util;
026
027import io.blt.util.functional.ThrowingConsumer;
028import io.blt.util.functional.ThrowingSupplier;
029import java.util.function.Supplier;
030
031import static java.util.Objects.nonNull;
032
033/**
034 * Static utility methods for operating on {@code Object}.
035 */
036public final class Obj {
037
038    private Obj() {
039        throw new IllegalAccessError("Utility class should be accessed statically and never constructed");
040    }
041
042    /**
043     * Passes the {@code instance} to the {@code consumer}, then returns the {@code instance}.
044     * e.g.
045     * <pre>{@code
046     * var user = Obj.poke(new User(), u -> {
047     *     u.setName("Greg");
048     *     u.setAge(15);
049     * });
050     * }</pre>
051     * <p>
052     * Optionally, the {@code consumer} may throw which will bubble up.
053     * </p>
054     *
055     * @param instance instance to consume and return
056     * @param consumer operation to perform on {@code instance}
057     * @param <T>      type of {@code instance}
058     * @param <E>      type of {@code consumer} throwable
059     * @return {@code instance} after accepting side effects via {@code consumer}.
060     */
061    public static <T, E extends Throwable> T poke(T instance, ThrowingConsumer<T, E> consumer) throws E {
062        consumer.accept(instance);
063        return instance;
064    }
065
066    /**
067     * Calls the {@code supplier} to retrieve an instance which is mutated by the {@code consumer} then returned.
068     * e.g.
069     * <pre>{@code
070     * var user = Obj.tap(User::new, u -> {
071     *     u.setName("Greg");
072     *     u.setAge(15);
073     * });
074     * }</pre>
075     * <p>
076     * Optionally, the {@code consumer} may throw which will bubble up.
077     * </p>
078     *
079     * @param supplier supplies an instance to consume and return
080     * @param consumer operation to perform on supplied instance
081     * @param <T>      type of instance
082     * @param <E>      type of {@code consumer} throwable
083     * @return Supplied instance after applying side effects via {@code consumer}.
084     */
085    public static <T, E extends Throwable> T tap(Supplier<T> supplier, ThrowingConsumer<T, E> consumer) throws E {
086        return poke(supplier.get(), consumer);
087    }
088
089    /**
090     * Returns {@code value} if non-null, else invokes and returns the result of {@code supplier}.
091     * <p>
092     * Optionally, the {@code supplier} may throw which will bubble up.
093     * </p>
094     * e.g.
095     * <pre>{@code
096     * private URL homepageOrDefault(URL homepage) throws MalformedURLException {
097     *     return orElseGet(homepage, () -> new URL("https://google.com"));
098     * }
099     * }</pre>
100     *
101     * @param value    returned if non-null
102     * @param supplier called and returned if {@code value} is null
103     * @param <T>      type of the returned value
104     * @param <E>      type of {@code supplier} throwable
105     * @return {@code value} if non-null, the result of {@code supplier}
106     * @throws E {@code Throwable} that may be thrown if the {@code supplier} is invoked
107     */
108    public static <T, E extends Throwable> T orElseGet(T value, ThrowingSupplier<T, E> supplier) throws E {
109        return nonNull(value) ? value : supplier.get();
110    }
111
112}