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