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.juneau.commons.utils;
018
019import java.util.Collection;
020
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023
024/**
025 * Utility methods for argument validation and assertion.
026 *
027 * <p>
028 * This class provides static utility methods for validating method arguments and throwing
029 * {@link IllegalArgumentException} when validation fails. These methods are designed to
030 * simplify common parameter validation patterns and provide consistent error messages.
031 *
032 * <h5 class='section'>Features:</h5>
033 * <ul>
034 *   <li><b>Null checking:</b> Validate single or multiple arguments are not null
035 *   <li><b>Boolean expressions:</b> Assert arbitrary boolean conditions with custom messages
036 *   <li><b>String validation:</b> Check for null or blank strings
037 *   <li><b>Type checking:</b> Validate class array types
038 * </ul>
039 *
040 * <h5 class='section'>Usage:</h5>
041 * <p class='bjava'>
042 *   <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
043 *
044 *   <jk>public</jk> <jk>void</jk> setFooBar(String <jv>foo</jv>, String <jv>bar</jv>) {
045 *       <jc>// Validate multiple arguments at once</jc>
046 *       <jsm>assertArgsNotNull</jsm>(<js>"foo"</js>, <jv>foo</jv>, <js>"bar"</js>, <jv>bar</jv>);
047 *
048 *       <jc>// Validate custom condition</jc>
049 *       <jsm>assertArg</jsm>(<jv>foo</jv>.length() &gt; 0, <js>"Foo cannot be empty"</js>);
050 *   }
051 * </p>
052 *
053 * <h5 class='section'>See Also:</h5>
054 * <ul>
055 *   <li class='link'><a class="doclink" href='../../../../../index.html#juneau-commons.utils'>Overview &gt; juneau-commons.utils</a>
056 * </ul>
057 */
058public class AssertionUtils {
059
060   /**
061    * Throws an {@link IllegalArgumentException} if the specified expression is <jk>false</jk>.
062    *
063    * <h5 class='section'>Example:</h5>
064    * <p class='bjava'>
065    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
066    *
067    * <jk>public</jk> String setFoo(List&lt;String&gt; <jv>foo</jv>) {
068    *    <jsm>assertArg</jsm>(<jv>foo</jv> != <jk>null</jk> &amp;&amp; ! <jv>foo</jv>.isEmpty(), <js>"'foo' cannot be null or empty."</js>);
069    *    ...
070    * }
071    * </p>
072    *
073    * @param expression The boolean expression to check.
074    * @param msg The exception message.
075    * @param args The exception message args.
076    * @throws IllegalArgumentException Constructed exception.
077    */
078   public static final void assertArg(boolean expression, String msg, Object...args) throws IllegalArgumentException {
079      if (! expression)
080         throw illegalArg(msg, args);
081   }
082
083   /**
084    * Asserts that the given expression is <c>true</c>, throwing an {@link IllegalStateException} if it's not.
085    *
086    * <p>
087    * This method is similar to {@link #assertArg(boolean, String, Object...)} but throws an
088    * {@link IllegalStateException} instead of an {@link IllegalArgumentException}, making it suitable
089    * for state validation rather than argument validation.
090    *
091    * @param expression The expression to test.
092    * @param msg The error message format string.
093    * @param args The arguments for the error message format string.
094    * @throws IllegalStateException if the expression is <c>false</c>.
095    */
096   public static final void assertState(boolean expression, String msg, Object...args) throws IllegalStateException {
097      if (! expression)
098         throw illegalState(msg, args);
099   }
100
101   /**
102    * Throws an {@link IllegalArgumentException} if the specified argument is <jk>null</jk>.
103    *
104    * <h5 class='section'>Example:</h5>
105    * <p class='bjava'>
106    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
107    *
108    * <jk>public</jk> String setFoo(String <jv>foo</jv>) {
109    *    <jsm>assertArgNotNull</jsm>(<js>"foo"</js>, <jv>foo</jv>);
110    *    ...
111    * }
112    * </p>
113    *
114    * @param <T> The argument data type.
115    * @param name The argument name.
116    * @param o The object to check.
117    * @return The same argument.
118    * @throws IllegalArgumentException Constructed exception.
119    */
120   public static final <T> T assertArgNotNull(String name, T o) throws IllegalArgumentException {
121      assertArg(o != null, "Argument ''{0}'' cannot be null.", name);
122      return o;
123   }
124
125   /**
126    * Asserts that the specified object is not <jk>null</jk>, throwing an {@link IllegalStateException} if it is.
127    *
128    * @param <T> The type of the object.
129    * @param o The object to check. Can be <jk>null</jk>.
130    * @param msg The error message format string. Must not be <jk>null</jk>.
131    * @param args The arguments for the message format string.
132    * @return The non-null object.
133    * @throws IllegalStateException If the object is <jk>null</jk>.
134    */
135   public static final <T> T assertNotNull(T o, String msg, Object...args) throws IllegalStateException {
136      if (o == null)
137         throw illegalState(msg, args);
138      return o;
139   }
140
141   /**
142    * Throws an {@link IllegalArgumentException} if the specified string is <jk>null</jk> or blank.
143    *
144    * @param name The argument name.
145    * @param o The object to check.
146    * @return The same object.
147    * @throws IllegalArgumentException Thrown if the specified string is <jk>null</jk> or blank.
148    */
149   public static final String assertArgNotNullOrBlank(String name, String o) throws IllegalArgumentException {
150      assertArgNotNull(name, o);
151      assertArg(! o.isBlank(), "Argument ''{0}'' cannot be blank.", name);
152      return o;
153   }
154
155   /**
156    * Throws an {@link IllegalArgumentException} if any of the specified arguments are <jk>null</jk>.
157    *
158    * <h5 class='section'>Example:</h5>
159    * <p class='bjava'>
160    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
161    *
162    * <jk>public</jk> String setFooBar(String <jv>foo</jv>, String <jv>bar</jv>) {
163    *    <jsm>assertArgsNotNull</jsm>(<js>"foo"</js>, <jv>foo</jv>, <js>"bar"</js>, <jv>bar</jv>);
164    *    ...
165    * }
166    * </p>
167    *
168    * @param name1 The first argument name.
169    * @param o1 The first object to check.
170    * @param name2 The second argument name.
171    * @param o2 The second object to check.
172    * @throws IllegalArgumentException Constructed exception.
173    */
174   public static final void assertArgsNotNull(String name1, Object o1, String name2, Object o2) throws IllegalArgumentException {
175      assertArgNotNull(name1, o1);
176      assertArgNotNull(name2, o2);
177   }
178
179   /**
180    * Throws an {@link IllegalArgumentException} if any of the specified arguments are <jk>null</jk>.
181    *
182    * <h5 class='section'>Example:</h5>
183    * <p class='bjava'>
184    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
185    *
186    * <jk>public</jk> String setFooBarBaz(String <jv>foo</jv>, String <jv>bar</jv>, String <jv>baz</jv>) {
187    *    <jsm>assertArgsNotNull</jsm>(<js>"foo"</js>, <jv>foo</jv>, <js>"bar"</js>, <jv>bar</jv>, <js>"baz"</js>, <jv>baz</jv>);
188    *    ...
189    * }
190    * </p>
191    *
192    * @param name1 The first argument name.
193    * @param o1 The first object to check.
194    * @param name2 The second argument name.
195    * @param o2 The second object to check.
196    * @param name3 The third argument name.
197    * @param o3 The third object to check.
198    * @throws IllegalArgumentException Constructed exception.
199    */
200   public static final void assertArgsNotNull(String name1, Object o1, String name2, Object o2, String name3, Object o3) throws IllegalArgumentException {
201      assertArgNotNull(name1, o1);
202      assertArgNotNull(name2, o2);
203      assertArgNotNull(name3, o3);
204   }
205
206   /**
207    * Throws an {@link IllegalArgumentException} if any of the specified arguments are <jk>null</jk>.
208    *
209    * @param name1 The first argument name.
210    * @param o1 The first object to check.
211    * @param name2 The second argument name.
212    * @param o2 The second object to check.
213    * @param name3 The third argument name.
214    * @param o3 The third object to check.
215    * @param name4 The fourth argument name.
216    * @param o4 The fourth object to check.
217    * @throws IllegalArgumentException Constructed exception.
218    */
219   public static final void assertArgsNotNull(String name1, Object o1, String name2, Object o2, String name3, Object o3, String name4, Object o4) throws IllegalArgumentException {
220      assertArgNotNull(name1, o1);
221      assertArgNotNull(name2, o2);
222      assertArgNotNull(name3, o3);
223      assertArgNotNull(name4, o4);
224   }
225
226   /**
227    * Throws an {@link IllegalArgumentException} if any of the specified arguments are <jk>null</jk>.
228    *
229    * @param name1 The first argument name.
230    * @param o1 The first object to check.
231    * @param name2 The second argument name.
232    * @param o2 The second object to check.
233    * @param name3 The third argument name.
234    * @param o3 The third object to check.
235    * @param name4 The fourth argument name.
236    * @param o4 The fourth object to check.
237    * @param name5 The fifth argument name.
238    * @param o5 The fifth object to check.
239    * @throws IllegalArgumentException Constructed exception.
240    */
241   public static final void assertArgsNotNull(String name1, Object o1, String name2, Object o2, String name3, Object o3, String name4, Object o4, String name5, Object o5)
242      throws IllegalArgumentException {
243      assertArgNotNull(name1, o1);
244      assertArgNotNull(name2, o2);
245      assertArgNotNull(name3, o3);
246      assertArgNotNull(name4, o4);
247      assertArgNotNull(name5, o5);
248   }
249
250   /**
251    * Throws an {@link IllegalArgumentException} if the specified object is not an instance of the specified type.
252    *
253    * <h5 class='section'>Example:</h5>
254    * <p class='bjava'>
255    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
256    *
257    * <jk>public</jk> <jk>void</jk> setValue(Object <jv>value</jv>) {
258    *    <jc>// Validate that value is a String</jc>
259    *    <jsm>assertType</jsm>(String.<jk>class</jk>, <jv>value</jv>);
260    *    ...
261    * }
262    * </p>
263    *
264    * @param <T> The expected type.
265    * @param type The expected class type.
266    * @param o The object to check.
267    * @return The object cast to the specified type.
268    * @throws IllegalArgumentException Thrown if the object is not an instance of the specified type.
269    */
270   @SuppressWarnings({ "unchecked" })
271   public static final <T> T assertType(Class<T> type, Object o) throws IllegalArgumentException {
272      assertArgNotNull("type", type);
273      assertArgNotNull("o", o);
274      if (! type.isInstance(o))
275         throw illegalArg("Object is not an instance of {0}: {1}", cn(type), cn(o));
276      return (T)o;
277   }
278
279   /**
280    * Throws the exception provided by the supplier if the specified object is not an instance of the specified type.
281    *
282    * <h5 class='section'>Example:</h5>
283    * <p class='bjava'>
284    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
285    *
286    * <jk>public</jk> <jk>void</jk> setValue(Object <jv>value</jv>) {
287    *    <jc>// Validate that value is a String, throw custom exception</jc>
288    *    <jsm>assertType</jsm>(String.<jk>class</jk>, <jv>value</jv>, () -&gt; <jk>new</jk> IllegalStateException(<js>"Invalid value type"</js>));
289    *    ...
290    * }
291    * </p>
292    *
293    * @param <T> The expected type.
294    * @param type The expected class type.
295    * @param o The object to check.
296    * @param exceptionSupplier The supplier that provides the exception to throw if validation fails.
297    * @return The object cast to the specified type.
298    * @throws RuntimeException Thrown if the object is not an instance of the specified type (the exception is provided by the supplier).
299    */
300   @SuppressWarnings({ "unchecked" })
301   public static final <T> T assertType(Class<T> type, Object o, java.util.function.Supplier<? extends RuntimeException> exceptionSupplier) throws RuntimeException {
302      assertArgNotNull("type", type);
303      assertArgNotNull("o", o);
304      if (! type.isInstance(o))
305         throw exceptionSupplier.get();
306      return (T)o;
307   }
308
309   /**
310    * Throws an {@link IllegalArgumentException} if the specified value doesn't have all subclasses of the specified type.
311    *
312    * @param <E> The element type.
313    * @param name The argument name.
314    * @param type The expected parent class.
315    * @param value The array value being checked.
316    * @return The value cast to the specified array type.
317    * @throws IllegalArgumentException Constructed exception.
318    */
319   @SuppressWarnings("unchecked")
320   public static final <E> Class<E>[] assertClassArrayArgIsType(String name, Class<E> type, Class<?>[] value) throws IllegalArgumentException {
321      for (var i = 0; i < value.length; i++)
322         if (! type.isAssignableFrom(value[i]))
323            throw illegalArg("Arg {0} did not have arg of type {1} at index {2}: {3}", name, cn(type), i, cn(value[i]));
324      return (Class<E>[])value;
325   }
326
327   /**
328    * Throws an {@link AssertionError} if the specified actual value is not one of the expected values.
329    *
330    * @param <T> The value type.
331    * @param actual The actual value.
332    * @param expected The expected values.
333    * @return The actual value if it matches one of the expected values.
334    * @throws AssertionError if the value is not one of the expected values.
335    */
336   @SafeVarargs
337   public static final <T> T assertOneOf(T actual, T...expected) {
338      for (var e : expected) {
339         if (eq(actual, e))
340            return actual;
341      }
342      throw new AssertionError("Invalid value specified: " + actual);
343   }
344
345   /**
346    * Throws an {@link IllegalArgumentException} if the specified varargs array or any of its elements are <jk>null</jk>.
347    *
348    * @param <T> The element type.
349    * @param name The argument name.
350    * @param o The object to check.
351    * @return The same object.
352    * @throws IllegalArgumentException Thrown if the specified varargs array or any of its elements are <jk>null</jk>.
353    */
354   public static final <T> T[] assertArgNoNulls(String name, T[] o) throws IllegalArgumentException {
355      assertArgNotNull(name, o);
356      for (var i = 0; i < o.length; i++)
357         assertArg(nn(o[i]), "Argument ''{0}'' parameter {1} cannot be null.", name, i);
358      return o;
359   }
360
361   /**
362    * Throws an {@link IllegalArgumentException} if the specified collection or any of its elements are <jk>null</jk>.
363    *
364    * <h5 class='section'>Example:</h5>
365    * <p class='bjava'>
366    *    <jk>import static</jk> org.apache.juneau.commons.utils.AssertionUtils.*;
367    *
368    * <jk>public</jk> <jk>void</jk> setValues(List&lt;String&gt; <jv>values</jv>) {
369    *    <jsm>assertCollectionArgNotNull</jsm>(<js>"values"</js>, <jv>values</jv>);
370    *    ...
371    * }
372    * </p>
373    *
374    * @param <T> The element type.
375    * @param <C> The collection type.
376    * @param name The argument name.
377    * @param collection The collection to check.
378    * @return The same collection.
379    * @throws IllegalArgumentException Thrown if the specified collection or any of its elements are <jk>null</jk>.
380    */
381   public static final <T, C extends Collection<T>> C assertArgNoNulls(String name, C collection) throws IllegalArgumentException {
382      assertArgNotNull(name, collection);
383      var i = 0;
384      for (var element : collection)
385         assertArg(nn(element), "Argument ''{0}'' element at index {1} cannot be null.", name, i++);
386      return collection;
387   }
388
389}