001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.tools.lang;
017
018import static de.cuioss.tools.base.Preconditions.checkArgument;
019import static de.cuioss.tools.collect.MoreCollections.isEmpty;
020
021import java.util.function.Supplier;
022
023import lombok.experimental.UtilityClass;
024
025/**
026 * Provides some utilities in the context of {@link Object}
027 *
028 * @author Oliver Wolff
029 *
030 */
031@UtilityClass
032public class MoreObjects {
033
034    /**
035     * Checks and returns the given Object if it is assignable to the given
036     * targetType. Otherwise, it throws an {@link IllegalArgumentException}. This
037     * will be thrown also if one of the parameters is {@code null}.
038     *
039     * @param <T>          defining the type to be returned.
040     *
041     * @param underCheck   KeyStoreType to be checked / cast. If it is null or is
042     *                     not assignable to expectedType an
043     *                     {@link IllegalArgumentException} will be thrown.
044     * @param expectedType checks the type . If it is null an
045     *                     {@link IllegalArgumentException} will be thrown
046     * @return the cast Objected of type T if applicable.
047     * @throws IllegalArgumentException if the given type is either null or not the
048     *                                  expected type
049     */
050    @SuppressWarnings("unchecked") // owolff: It is actually checked before.
051    public static <T> T requireType(final Object underCheck, Class<T> expectedType) {
052        checkArgument(null != underCheck, "Object to be checked must not be null");
053        checkArgument(null != expectedType, "expectedType must not be null");
054        checkArgument(expectedType.isAssignableFrom(underCheck.getClass()),
055                "KeyStoreType to be checked '%s' is not assignable to '%s'", underCheck.getClass(),
056                expectedType.getName());
057        return (T) underCheck;
058    }
059
060    /**
061     * Simple helper checking whether a number of given Objects are not {@code null}
062     *
063     * @param objects
064     * @return {@code true} if there is no {@code null} value given, {@code false}
065     *         if at least one null value is given. An empty varags parameter
066     *         therefore results in {@code true} (no null object found)
067     */
068    public static boolean allNonNull(Object... objects) {
069        if (isEmpty(objects)) {
070            return true;
071        }
072        for (Object object : objects) {
073            if (null == object) {
074                return false;
075            }
076        }
077        return true;
078    }
079
080    /**
081     * Simple helper checking whether a number of given Objects are {@code null}
082     *
083     * @param objects
084     * @return {@code true} if there is no non-{@code null} value given,
085     *         {@code false} if at least one non-null value is given. An empty
086     *         varags parameter therefore results in {@code true} (no non-null
087     *         object found)
088     */
089    public static boolean allNull(Object... objects) {
090        if (isEmpty(objects)) {
091            return true;
092        }
093        for (Object object : objects) {
094            if (null != object) {
095                return false;
096            }
097        }
098        return true;
099    }
100
101    /**
102     * <p>
103     * Returns the first value in the array which is not {@code null}. If all the
104     * values are {@code null} or the array is {@code null} or empty then
105     * {@code null} is returned.
106     * </p>
107     *
108     * <pre>
109     * MoreObjects.firstNonNull(null, null)      = null
110     * MoreObjects.firstNonNull(null, "")        = ""
111     * MoreObjects.firstNonNull(null, null, "")  = ""
112     * MoreObjects.firstNonNull(null, "zz")      = "zz"
113     * MoreObjects.firstNonNull("abc", *)        = "abc"
114     * MoreObjects.firstNonNull(null, "xyz", *)  = "xyz"
115     * MoreObjects.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
116     * MoreObjects.firstNonNull()                = null
117     * </pre>
118     *
119     * @param <T>    the component type of the array
120     * @param values the values to test, may be {@code null} or empty
121     *
122     * @return the first value from {@code values} which is not {@code null}, or
123     *         {@code null} if there are no non-null values
124     */
125    @SafeVarargs
126    public static <T> T firstNonNull(final T... values) {
127        if (values != null) {
128            for (final T val : values) {
129                if (val != null) {
130                    return val;
131                }
132            }
133        }
134        return null;
135    }
136
137    /**
138     * <p>
139     * Executes the given suppliers in order and returns the first return value
140     * where a value other than {@code null} is returned. Once a non-{@code null}
141     * value is obtained, all following suppliers are not executed anymore. If all
142     * the return values are {@code null} or no suppliers are provided then
143     * {@code null} is returned.
144     * </p>
145     *
146     * <pre>
147     * MoreObjects.getFirstNonNull(null, () -&gt; null) = null
148     * MoreObjects.getFirstNonNull(() -&gt; null, () -&gt; "") = ""
149     * MoreObjects.getFirstNonNull(() -&gt; "", () -&gt; throw new IllegalStateException()) = ""
150     * MoreObjects.getFirstNonNull(() -&gt; null, () -&gt; "zz) = "zz"
151     * MoreObjects.getFirstNonNull() = null
152     * </pre>
153     *
154     * @param <T>       the type of the return values
155     * @param suppliers the suppliers returning the values to test. {@code null}
156     *                  values are ignored. Suppliers may return {@code null} or a
157     *                  value of type @{code T}
158     *
159     * @return the first return value from {@code suppliers} which is not
160     *         {@code null}, or {@code null} if there are no non-null values
161     */
162    @SafeVarargs
163    public static <T> T getFirstNonNull(final Supplier<T>... suppliers) {
164        if (suppliers != null) {
165            for (final Supplier<T> supplier : suppliers) {
166                if (supplier != null) {
167                    final var value = supplier.get();
168                    if (value != null) {
169                        return value;
170                    }
171                }
172            }
173        }
174        return null;
175    }
176}