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