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