1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.core.util;
18
19import android.text.TextUtils;
20
21import androidx.annotation.IntRange;
22import androidx.annotation.NonNull;
23import androidx.annotation.RestrictTo;
24
25import java.util.Collection;
26import java.util.Locale;
27
28/**
29 * Simple static methods to be called at the start of your own methods to verify
30 * correct arguments and state.
31 *
32 * @hide
33 */
34@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
35public class Preconditions {
36    public static void checkArgument(boolean expression) {
37        if (!expression) {
38            throw new IllegalArgumentException();
39        }
40    }
41
42    /**
43     * Ensures that an expression checking an argument is true.
44     *
45     * @param expression the expression to check
46     * @param errorMessage the exception message to use if the check fails; will
47     *     be converted to a string using {@link String#valueOf(Object)}
48     * @throws IllegalArgumentException if {@code expression} is false
49     */
50    public static void checkArgument(boolean expression, final Object errorMessage) {
51        if (!expression) {
52            throw new IllegalArgumentException(String.valueOf(errorMessage));
53        }
54    }
55
56    /**
57     * Ensures that an string reference passed as a parameter to the calling
58     * method is not empty.
59     *
60     * @param string an string reference
61     * @return the string reference that was validated
62     * @throws IllegalArgumentException if {@code string} is empty
63     */
64    public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) {
65        if (TextUtils.isEmpty(string)) {
66            throw new IllegalArgumentException();
67        }
68        return string;
69    }
70
71    /**
72     * Ensures that an string reference passed as a parameter to the calling
73     * method is not empty.
74     *
75     * @param string an string reference
76     * @param errorMessage the exception message to use if the check fails; will
77     *     be converted to a string using {@link String#valueOf(Object)}
78     * @return the string reference that was validated
79     * @throws IllegalArgumentException if {@code string} is empty
80     */
81    public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string,
82            final Object errorMessage) {
83        if (TextUtils.isEmpty(string)) {
84            throw new IllegalArgumentException(String.valueOf(errorMessage));
85        }
86        return string;
87    }
88
89    /**
90     * Ensures that an object reference passed as a parameter to the calling
91     * method is not null.
92     *
93     * @param reference an object reference
94     * @return the non-null reference that was validated
95     * @throws NullPointerException if {@code reference} is null
96     */
97    public static @NonNull <T> T checkNotNull(final T reference) {
98        if (reference == null) {
99            throw new NullPointerException();
100        }
101        return reference;
102    }
103
104    /**
105     * Ensures that an object reference passed as a parameter to the calling
106     * method is not null.
107     *
108     * @param reference an object reference
109     * @param errorMessage the exception message to use if the check fails; will
110     *     be converted to a string using {@link String#valueOf(Object)}
111     * @return the non-null reference that was validated
112     * @throws NullPointerException if {@code reference} is null
113     */
114    public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
115        if (reference == null) {
116            throw new NullPointerException(String.valueOf(errorMessage));
117        }
118        return reference;
119    }
120
121    /**
122     * Ensures the truth of an expression involving the state of the calling
123     * instance, but not involving any parameters to the calling method.
124     *
125     * @param expression a boolean expression
126     * @param message exception message
127     * @throws IllegalStateException if {@code expression} is false
128     */
129    public static void checkState(final boolean expression, String message) {
130        if (!expression) {
131            throw new IllegalStateException(message);
132        }
133    }
134
135    /**
136     * Ensures the truth of an expression involving the state of the calling
137     * instance, but not involving any parameters to the calling method.
138     *
139     * @param expression a boolean expression
140     * @throws IllegalStateException if {@code expression} is false
141     */
142    public static void checkState(final boolean expression) {
143        checkState(expression, null);
144    }
145
146    /**
147     * Check the requested flags, throwing if any requested flags are outside
148     * the allowed set.
149     *
150     * @return the validated requested flags.
151     */
152    public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
153        if ((requestedFlags & allowedFlags) != requestedFlags) {
154            throw new IllegalArgumentException("Requested flags 0x"
155                    + Integer.toHexString(requestedFlags) + ", but only 0x"
156                    + Integer.toHexString(allowedFlags) + " are allowed");
157        }
158
159        return requestedFlags;
160    }
161
162    /**
163     * Ensures that that the argument numeric value is non-negative.
164     *
165     * @param value a numeric int value
166     * @param errorMessage the exception message to use if the check fails
167     * @return the validated numeric value
168     * @throws IllegalArgumentException if {@code value} was negative
169     */
170    public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
171            final String errorMessage) {
172        if (value < 0) {
173            throw new IllegalArgumentException(errorMessage);
174        }
175
176        return value;
177    }
178
179    /**
180     * Ensures that that the argument numeric value is non-negative.
181     *
182     * @param value a numeric int value
183     *
184     * @return the validated numeric value
185     * @throws IllegalArgumentException if {@code value} was negative
186     */
187    public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) {
188        if (value < 0) {
189            throw new IllegalArgumentException();
190        }
191
192        return value;
193    }
194
195    /**
196     * Ensures that that the argument numeric value is non-negative.
197     *
198     * @param value a numeric long value
199     * @return the validated numeric value
200     * @throws IllegalArgumentException if {@code value} was negative
201     */
202    public static long checkArgumentNonnegative(final long value) {
203        if (value < 0) {
204            throw new IllegalArgumentException();
205        }
206
207        return value;
208    }
209
210    /**
211     * Ensures that that the argument numeric value is non-negative.
212     *
213     * @param value a numeric long value
214     * @param errorMessage the exception message to use if the check fails
215     * @return the validated numeric value
216     * @throws IllegalArgumentException if {@code value} was negative
217     */
218    public static long checkArgumentNonnegative(final long value, final String errorMessage) {
219        if (value < 0) {
220            throw new IllegalArgumentException(errorMessage);
221        }
222
223        return value;
224    }
225
226    /**
227     * Ensures that that the argument numeric value is positive.
228     *
229     * @param value a numeric int value
230     * @param errorMessage the exception message to use if the check fails
231     * @return the validated numeric value
232     * @throws IllegalArgumentException if {@code value} was not positive
233     */
234    public static int checkArgumentPositive(final int value, final String errorMessage) {
235        if (value <= 0) {
236            throw new IllegalArgumentException(errorMessage);
237        }
238
239        return value;
240    }
241
242    /**
243     * Ensures that the argument floating point value is a finite number.
244     *
245     * <p>A finite number is defined to be both representable (that is, not NaN) and
246     * not infinite (that is neither positive or negative infinity).</p>
247     *
248     * @param value a floating point value
249     * @param valueName the name of the argument to use if the check fails
250     *
251     * @return the validated floating point value
252     *
253     * @throws IllegalArgumentException if {@code value} was not finite
254     */
255    public static float checkArgumentFinite(final float value, final String valueName) {
256        if (Float.isNaN(value)) {
257            throw new IllegalArgumentException(valueName + " must not be NaN");
258        } else if (Float.isInfinite(value)) {
259            throw new IllegalArgumentException(valueName + " must not be infinite");
260        }
261
262        return value;
263    }
264
265    /**
266     * Ensures that the argument floating point value is within the inclusive range.
267     *
268     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
269     * will always be out of range.</p>
270     *
271     * @param value a floating point value
272     * @param lower the lower endpoint of the inclusive range
273     * @param upper the upper endpoint of the inclusive range
274     * @param valueName the name of the argument to use if the check fails
275     *
276     * @return the validated floating point value
277     *
278     * @throws IllegalArgumentException if {@code value} was not within the range
279     */
280    public static float checkArgumentInRange(float value, float lower, float upper,
281            String valueName) {
282        if (Float.isNaN(value)) {
283            throw new IllegalArgumentException(valueName + " must not be NaN");
284        } else if (value < lower) {
285            throw new IllegalArgumentException(
286                    String.format(Locale.US,
287                            "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
288        } else if (value > upper) {
289            throw new IllegalArgumentException(
290                    String.format(Locale.US,
291                            "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
292        }
293
294        return value;
295    }
296
297    /**
298     * Ensures that the argument int value is within the inclusive range.
299     *
300     * @param value a int value
301     * @param lower the lower endpoint of the inclusive range
302     * @param upper the upper endpoint of the inclusive range
303     * @param valueName the name of the argument to use if the check fails
304     *
305     * @return the validated int value
306     *
307     * @throws IllegalArgumentException if {@code value} was not within the range
308     */
309    public static int checkArgumentInRange(int value, int lower, int upper,
310            String valueName) {
311        if (value < lower) {
312            throw new IllegalArgumentException(
313                    String.format(Locale.US,
314                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
315        } else if (value > upper) {
316            throw new IllegalArgumentException(
317                    String.format(Locale.US,
318                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
319        }
320
321        return value;
322    }
323
324    /**
325     * Ensures that the argument long value is within the inclusive range.
326     *
327     * @param value a long value
328     * @param lower the lower endpoint of the inclusive range
329     * @param upper the upper endpoint of the inclusive range
330     * @param valueName the name of the argument to use if the check fails
331     *
332     * @return the validated long value
333     *
334     * @throws IllegalArgumentException if {@code value} was not within the range
335     */
336    public static long checkArgumentInRange(long value, long lower, long upper,
337            String valueName) {
338        if (value < lower) {
339            throw new IllegalArgumentException(
340                    String.format(Locale.US,
341                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
342        } else if (value > upper) {
343            throw new IllegalArgumentException(
344                    String.format(Locale.US,
345                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
346        }
347
348        return value;
349    }
350
351    /**
352     * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
353     *
354     * @param value an array of boxed objects
355     * @param valueName the name of the argument to use if the check fails
356     *
357     * @return the validated array
358     *
359     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
360     */
361    public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
362        if (value == null) {
363            throw new NullPointerException(valueName + " must not be null");
364        }
365
366        for (int i = 0; i < value.length; ++i) {
367            if (value[i] == null) {
368                throw new NullPointerException(
369                        String.format(Locale.US, "%s[%d] must not be null", valueName, i));
370            }
371        }
372
373        return value;
374    }
375
376    /**
377     * Ensures that the {@link Collection} is not {@code null}, and none of its elements are
378     * {@code null}.
379     *
380     * @param value a {@link Collection} of boxed objects
381     * @param valueName the name of the argument to use if the check fails
382     *
383     * @return the validated {@link Collection}
384     *
385     * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
386     */
387    public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
388            final C value, final String valueName) {
389        if (value == null) {
390            throw new NullPointerException(valueName + " must not be null");
391        }
392
393        long ctr = 0;
394        for (T elem : value) {
395            if (elem == null) {
396                throw new NullPointerException(
397                        String.format(Locale.US, "%s[%d] must not be null", valueName, ctr));
398            }
399            ++ctr;
400        }
401
402        return value;
403    }
404
405    /**
406     * Ensures that the {@link Collection} is not {@code null}, and contains at least one element.
407     *
408     * @param value a {@link Collection} of boxed elements.
409     * @param valueName the name of the argument to use if the check fails.
410
411     * @return the validated {@link Collection}
412     *
413     * @throws NullPointerException if the {@code value} was {@code null}
414     * @throws IllegalArgumentException if the {@code value} was empty
415     */
416    public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value,
417            final String valueName) {
418        if (value == null) {
419            throw new NullPointerException(valueName + " must not be null");
420        }
421        if (value.isEmpty()) {
422            throw new IllegalArgumentException(valueName + " is empty");
423        }
424        return value;
425    }
426
427    /**
428     * Ensures that all elements in the argument floating point array are within the inclusive range
429     *
430     * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
431     * will always be out of range.</p>
432     *
433     * @param value a floating point array of values
434     * @param lower the lower endpoint of the inclusive range
435     * @param upper the upper endpoint of the inclusive range
436     * @param valueName the name of the argument to use if the check fails
437     *
438     * @return the validated floating point value
439     *
440     * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
441     * @throws NullPointerException if the {@code value} was {@code null}
442     */
443    public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
444            String valueName) {
445        checkNotNull(value, valueName + " must not be null");
446
447        for (int i = 0; i < value.length; ++i) {
448            float v = value[i];
449
450            if (Float.isNaN(v)) {
451                throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
452            } else if (v < lower) {
453                throw new IllegalArgumentException(
454                        String.format(Locale.US, "%s[%d] is out of range of [%f, %f] (too low)",
455                                valueName, i, lower, upper));
456            } else if (v > upper) {
457                throw new IllegalArgumentException(
458                        String.format(Locale.US, "%s[%d] is out of range of [%f, %f] (too high)",
459                                valueName, i, lower, upper));
460            }
461        }
462
463        return value;
464    }
465
466    private Preconditions() {
467    }
468}
469