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 
17 package com.android.internal.util;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.text.TextUtils;
23 
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Objects;
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 public class Preconditions {
33 
34     @UnsupportedAppUsage
checkArgument(boolean expression)35     public static void checkArgument(boolean expression) {
36         if (!expression) {
37             throw new IllegalArgumentException();
38         }
39     }
40 
41     /**
42      * Ensures that an expression checking an argument is true.
43      *
44      * @param expression the expression to check
45      * @param errorMessage the exception message to use if the check fails; will
46      *     be converted to a string using {@link String#valueOf(Object)}
47      * @throws IllegalArgumentException if {@code expression} is false
48      */
49     @UnsupportedAppUsage
checkArgument(boolean expression, final Object errorMessage)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 expression checking an argument is true.
58      *
59      * @param expression the expression to check
60      * @param messageTemplate a printf-style message template to use if the check fails; will
61      *     be converted to a string using {@link String#format(String, Object...)}
62      * @param messageArgs arguments for {@code messageTemplate}
63      * @throws IllegalArgumentException if {@code expression} is false
64      */
checkArgument(boolean expression, final String messageTemplate, final Object... messageArgs)65     public static void checkArgument(boolean expression,
66             final String messageTemplate,
67             final Object... messageArgs) {
68         if (!expression) {
69             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
70         }
71     }
72 
73     /**
74      * Ensures that an string reference passed as a parameter to the calling
75      * method is not empty.
76      *
77      * @param string an string reference
78      * @return the string reference that was validated
79      * @throws IllegalArgumentException if {@code string} is empty
80      */
checkStringNotEmpty(final T string)81     public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) {
82         if (TextUtils.isEmpty(string)) {
83             throw new IllegalArgumentException();
84         }
85         return string;
86     }
87 
88     /**
89      * Ensures that an string reference passed as a parameter to the calling
90      * method is not empty.
91      *
92      * @param string an string reference
93      * @param errorMessage the exception message to use if the check fails; will
94      *     be converted to a string using {@link String#valueOf(Object)}
95      * @return the string reference that was validated
96      * @throws IllegalArgumentException if {@code string} is empty
97      */
checkStringNotEmpty(final T string, final Object errorMessage)98     public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string,
99             final Object errorMessage) {
100         if (TextUtils.isEmpty(string)) {
101             throw new IllegalArgumentException(String.valueOf(errorMessage));
102         }
103         return string;
104     }
105 
106     /**
107      * Ensures that an string reference passed as a parameter to the calling method is not empty.
108      *
109      * @param string an string reference
110      * @param messageTemplate a printf-style message template to use if the check fails; will be
111      *     converted to a string using {@link String#format(String, Object...)}
112      * @param messageArgs arguments for {@code messageTemplate}
113      * @return the string reference that was validated
114      * @throws IllegalArgumentException if {@code string} is empty
115      */
checkStringNotEmpty( final T string, final String messageTemplate, final Object... messageArgs)116     public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
117             final T string, final String messageTemplate, final Object... messageArgs) {
118         if (TextUtils.isEmpty(string)) {
119             throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
120         }
121         return string;
122     }
123 
124     /**
125      * Ensures that an object reference passed as a parameter to the calling
126      * method is not null.
127      *
128      * @param reference an object reference
129      * @return the non-null reference that was validated
130      * @throws NullPointerException if {@code reference} is null
131      * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
132      */
133     @Deprecated
134     @UnsupportedAppUsage
checkNotNull(final T reference)135     public static @NonNull <T> T checkNotNull(final T reference) {
136         if (reference == null) {
137             throw new NullPointerException();
138         }
139         return reference;
140     }
141 
142     /**
143      * Ensures that an object reference passed as a parameter to the calling
144      * method is not null.
145      *
146      * @param reference an object reference
147      * @param errorMessage the exception message to use if the check fails; will
148      *     be converted to a string using {@link String#valueOf(Object)}
149      * @return the non-null reference that was validated
150      * @throws NullPointerException if {@code reference} is null
151      * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
152      */
153     @Deprecated
154     @UnsupportedAppUsage
checkNotNull(final T reference, final Object errorMessage)155     public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
156         if (reference == null) {
157             throw new NullPointerException(String.valueOf(errorMessage));
158         }
159         return reference;
160     }
161 
162     /**
163      * Ensures the truth of an expression involving the state of the calling
164      * instance, but not involving any parameters to the calling method.
165      *
166      * @param expression a boolean expression
167      * @param message exception message
168      * @throws IllegalStateException if {@code expression} is false
169      */
170     @UnsupportedAppUsage
checkState(final boolean expression, String message)171     public static void checkState(final boolean expression, String message) {
172         if (!expression) {
173             throw new IllegalStateException(message);
174         }
175     }
176 
177     /**
178      * Ensures the truth of an expression involving the state of the calling
179      * instance, but not involving any parameters to the calling method.
180      *
181      * @param expression a boolean expression
182      * @throws IllegalStateException if {@code expression} is false
183      */
184     @UnsupportedAppUsage
checkState(final boolean expression)185     public static void checkState(final boolean expression) {
186         checkState(expression, null);
187     }
188 
189     /**
190      * Check the requested flags, throwing if any requested flags are outside
191      * the allowed set.
192      *
193      * @return the validated requested flags.
194      */
checkFlagsArgument(final int requestedFlags, final int allowedFlags)195     public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
196         if ((requestedFlags & allowedFlags) != requestedFlags) {
197             throw new IllegalArgumentException("Requested flags 0x"
198                     + Integer.toHexString(requestedFlags) + ", but only 0x"
199                     + Integer.toHexString(allowedFlags) + " are allowed");
200         }
201 
202         return requestedFlags;
203     }
204 
205     /**
206      * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
207      *
208      * @param value a numeric int 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      */
checkArgumentNonnegative(final int value, final String errorMessage)213     public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
214             final String errorMessage) {
215         if (value < 0) {
216             throw new IllegalArgumentException(errorMessage);
217         }
218 
219         return value;
220     }
221 
222     /**
223      * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
224      *
225      * @param value a numeric int value
226      *
227      * @return the validated numeric value
228      * @throws IllegalArgumentException if {@code value} was negative
229      */
checkArgumentNonnegative(final int value)230     public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) {
231         if (value < 0) {
232             throw new IllegalArgumentException();
233         }
234 
235         return value;
236     }
237 
238     /**
239      * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
240      *
241      * @param value a numeric long value
242      * @return the validated numeric value
243      * @throws IllegalArgumentException if {@code value} was negative
244      */
checkArgumentNonnegative(final long value)245     public static long checkArgumentNonnegative(final long value) {
246         if (value < 0) {
247             throw new IllegalArgumentException();
248         }
249 
250         return value;
251     }
252 
253     /**
254      * Ensures that that the argument numeric value is non-negative (greater than or equal to 0).
255      *
256      * @param value a numeric long value
257      * @param errorMessage the exception message to use if the check fails
258      * @return the validated numeric value
259      * @throws IllegalArgumentException if {@code value} was negative
260      */
checkArgumentNonnegative(final long value, final String errorMessage)261     public static long checkArgumentNonnegative(final long value, final String errorMessage) {
262         if (value < 0) {
263             throw new IllegalArgumentException(errorMessage);
264         }
265 
266         return value;
267     }
268 
269     /**
270      * Ensures that that the argument numeric value is positive (greater than 0).
271      *
272      * @param value a numeric int value
273      * @param errorMessage the exception message to use if the check fails
274      * @return the validated numeric value
275      * @throws IllegalArgumentException if {@code value} was not positive
276      */
checkArgumentPositive(final int value, final String errorMessage)277     public static int checkArgumentPositive(final int value, final String errorMessage) {
278         if (value <= 0) {
279             throw new IllegalArgumentException(errorMessage);
280         }
281 
282         return value;
283     }
284 
285     /**
286      * Ensures that the argument floating point value is non-negative (greater than or equal to 0).
287      * @param value a floating point value
288      * @param errorMessage the exteption message to use if the check fails
289      * @return the validated numeric value
290      * @throws IllegalArgumentException if {@code value} was negative
291      */
checkArgumentNonNegative(final float value, final String errorMessage)292     public static float checkArgumentNonNegative(final float value, final String errorMessage) {
293         if (value < 0) {
294             throw new IllegalArgumentException(errorMessage);
295         }
296 
297         return value;
298     }
299 
300     /**
301      * Ensures that the argument floating point value is positive (greater than 0).
302      * @param value a floating point value
303      * @param errorMessage the exteption message to use if the check fails
304      * @return the validated numeric value
305      * @throws IllegalArgumentException if {@code value} was not positive
306      */
checkArgumentPositive(final float value, final String errorMessage)307     public static float checkArgumentPositive(final float value, final String errorMessage) {
308         if (value <= 0) {
309             throw new IllegalArgumentException(errorMessage);
310         }
311 
312         return value;
313     }
314 
315     /**
316      * Ensures that the argument floating point value is a finite number.
317      *
318      * <p>A finite number is defined to be both representable (that is, not NaN) and
319      * not infinite (that is neither positive or negative infinity).</p>
320      *
321      * @param value a floating point value
322      * @param valueName the name of the argument to use if the check fails
323      *
324      * @return the validated floating point value
325      *
326      * @throws IllegalArgumentException if {@code value} was not finite
327      */
checkArgumentFinite(final float value, final String valueName)328     public static float checkArgumentFinite(final float value, final String valueName) {
329         if (Float.isNaN(value)) {
330             throw new IllegalArgumentException(valueName + " must not be NaN");
331         } else if (Float.isInfinite(value)) {
332             throw new IllegalArgumentException(valueName + " must not be infinite");
333         }
334 
335         return value;
336     }
337 
338     /**
339      * Ensures that the argument floating point value is within the inclusive range.
340      *
341      * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
342      * will always be out of range.</p>
343      *
344      * @param value a floating point value
345      * @param lower the lower endpoint of the inclusive range
346      * @param upper the upper endpoint of the inclusive range
347      * @param valueName the name of the argument to use if the check fails
348      *
349      * @return the validated floating point value
350      *
351      * @throws IllegalArgumentException if {@code value} was not within the range
352      */
checkArgumentInRange(float value, float lower, float upper, String valueName)353     public static float checkArgumentInRange(float value, float lower, float upper,
354             String valueName) {
355         if (Float.isNaN(value)) {
356             throw new IllegalArgumentException(valueName + " must not be NaN");
357         } else if (value < lower) {
358             throw new IllegalArgumentException(
359                     String.format(
360                             "%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
361         } else if (value > upper) {
362             throw new IllegalArgumentException(
363                     String.format(
364                             "%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
365         }
366 
367         return value;
368     }
369 
370     /**
371      * Ensures that the argument int value is within the inclusive range.
372      *
373      * @param value a int value
374      * @param lower the lower endpoint of the inclusive range
375      * @param upper the upper endpoint of the inclusive range
376      * @param valueName the name of the argument to use if the check fails
377      *
378      * @return the validated int value
379      *
380      * @throws IllegalArgumentException if {@code value} was not within the range
381      */
382     @UnsupportedAppUsage
checkArgumentInRange(int value, int lower, int upper, String valueName)383     public static int checkArgumentInRange(int value, int lower, int upper,
384             String valueName) {
385         if (value < lower) {
386             throw new IllegalArgumentException(
387                     String.format(
388                             "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
389         } else if (value > upper) {
390             throw new IllegalArgumentException(
391                     String.format(
392                             "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
393         }
394 
395         return value;
396     }
397 
398     /**
399      * Ensures that the argument long value is within the inclusive range.
400      *
401      * @param value a long value
402      * @param lower the lower endpoint of the inclusive range
403      * @param upper the upper endpoint of the inclusive range
404      * @param valueName the name of the argument to use if the check fails
405      *
406      * @return the validated long value
407      *
408      * @throws IllegalArgumentException if {@code value} was not within the range
409      */
checkArgumentInRange(long value, long lower, long upper, String valueName)410     public static long checkArgumentInRange(long value, long lower, long upper,
411             String valueName) {
412         if (value < lower) {
413             throw new IllegalArgumentException(
414                     String.format(
415                             "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
416         } else if (value > upper) {
417             throw new IllegalArgumentException(
418                     String.format(
419                             "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
420         }
421 
422         return value;
423     }
424 
425     /**
426      * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
427      *
428      * @param value an array of boxed objects
429      * @param valueName the name of the argument to use if the check fails
430      *
431      * @return the validated array
432      *
433      * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
434      */
checkArrayElementsNotNull(final T[] value, final String valueName)435     public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
436         if (value == null) {
437             throw new NullPointerException(valueName + " must not be null");
438         }
439 
440         for (int i = 0; i < value.length; ++i) {
441             if (value[i] == null) {
442                 throw new NullPointerException(
443                         String.format("%s[%d] must not be null", valueName, i));
444             }
445         }
446 
447         return value;
448     }
449 
450     /**
451      * Ensures that the {@link Collection} is not {@code null}, and none of its elements are
452      * {@code null}.
453      *
454      * @param value a {@link Collection} of boxed objects
455      * @param valueName the name of the argument to use if the check fails
456      *
457      * @return the validated {@link Collection}
458      *
459      * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
460      */
checkCollectionElementsNotNull( final C value, final String valueName)461     public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
462             final C value, final String valueName) {
463         if (value == null) {
464             throw new NullPointerException(valueName + " must not be null");
465         }
466 
467         long ctr = 0;
468         for (T elem : value) {
469             if (elem == null) {
470                 throw new NullPointerException(
471                         String.format("%s[%d] must not be null", valueName, ctr));
472             }
473             ++ctr;
474         }
475 
476         return value;
477     }
478 
479     /**
480      * Ensures that the {@link Collection} is not {@code null}, and contains at least one element.
481      *
482      * @param value a {@link Collection} of boxed elements.
483      * @param valueName the name of the argument to use if the check fails.
484 
485      * @return the validated {@link Collection}
486      *
487      * @throws NullPointerException if the {@code value} was {@code null}
488      * @throws IllegalArgumentException if the {@code value} was empty
489      */
checkCollectionNotEmpty(final Collection<T> value, final String valueName)490     public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value,
491             final String valueName) {
492         if (value == null) {
493             throw new NullPointerException(valueName + " must not be null");
494         }
495         if (value.isEmpty()) {
496             throw new IllegalArgumentException(valueName + " is empty");
497         }
498         return value;
499     }
500 
501     /**
502      * Ensures that the given byte array is not {@code null}, and contains at least one element.
503      *
504      * @param value an array of elements.
505      * @param valueName the name of the argument to use if the check fails.
506 
507      * @return the validated array
508      *
509      * @throws NullPointerException if the {@code value} was {@code null}
510      * @throws IllegalArgumentException if the {@code value} was empty
511      */
512     @NonNull
checkByteArrayNotEmpty(final byte[] value, final String valueName)513     public static byte[] checkByteArrayNotEmpty(final byte[] value, final String valueName) {
514         if (value == null) {
515             throw new NullPointerException(valueName + " must not be null");
516         }
517         if (value.length == 0) {
518             throw new IllegalArgumentException(valueName + " is empty");
519         }
520         return value;
521     }
522 
523     /**
524      * Ensures that argument {@code value} is one of {@code supportedValues}.
525      *
526      * @param supportedValues an array of string values
527      * @param value a string value
528      *
529      * @return the validated value
530      *
531      * @throws NullPointerException if either {@code value} or {@code supportedValues} is null
532      * @throws IllegalArgumentException if the {@code value} is not in {@code supportedValues}
533      */
534     @NonNull
checkArgumentIsSupported(final String[] supportedValues, final String value)535     public static String checkArgumentIsSupported(final String[] supportedValues,
536             final String value) {
537         checkNotNull(value);
538         checkNotNull(supportedValues);
539 
540         if (!contains(supportedValues, value)) {
541             throw new IllegalArgumentException(value + "is not supported "
542                     + Arrays.toString(supportedValues));
543         }
544         return value;
545     }
546 
contains(String[] values, String value)547     private static boolean contains(String[] values, String value) {
548         if (values == null) {
549             return false;
550         }
551         for (int i = 0; i < values.length; ++i) {
552             if (Objects.equals(value, values[i])) {
553                 return true;
554             }
555         }
556         return false;
557     }
558 
559     /**
560      * Ensures that all elements in the argument floating point array are within the inclusive range
561      *
562      * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
563      * will always be out of range.</p>
564      *
565      * @param value a floating point array of values
566      * @param lower the lower endpoint of the inclusive range
567      * @param upper the upper endpoint of the inclusive range
568      * @param valueName the name of the argument to use if the check fails
569      *
570      * @return the validated floating point value
571      *
572      * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
573      * @throws NullPointerException if the {@code value} was {@code null}
574      */
checkArrayElementsInRange(float[] value, float lower, float upper, String valueName)575     public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
576             String valueName) {
577         checkNotNull(value, valueName + " must not be null");
578 
579         for (int i = 0; i < value.length; ++i) {
580             float v = value[i];
581 
582             if (Float.isNaN(v)) {
583                 throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
584             } else if (v < lower) {
585                 throw new IllegalArgumentException(
586                         String.format("%s[%d] is out of range of [%f, %f] (too low)",
587                                 valueName, i, lower, upper));
588             } else if (v > upper) {
589                 throw new IllegalArgumentException(
590                         String.format("%s[%d] is out of range of [%f, %f] (too high)",
591                                 valueName, i, lower, upper));
592             }
593         }
594 
595         return value;
596     }
597 
598     /**
599      * Ensures that all elements in the argument integer array are within the inclusive range
600      *
601      * @param value an integer array of values
602      * @param lower the lower endpoint of the inclusive range
603      * @param upper the upper endpoint of the inclusive range
604      * @param valueName the name of the argument to use if the check fails
605      *
606      * @return the validated integer array
607      *
608      * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
609      * @throws NullPointerException if the {@code value} was {@code null}
610      */
checkArrayElementsInRange(int[] value, int lower, int upper, String valueName)611     public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
612             String valueName) {
613         checkNotNull(value, valueName + " must not be null");
614 
615         for (int i = 0; i < value.length; ++i) {
616             int v = value[i];
617 
618             if (v < lower) {
619                 throw new IllegalArgumentException(
620                         String.format("%s[%d] is out of range of [%d, %d] (too low)",
621                                 valueName, i, lower, upper));
622             } else if (v > upper) {
623                 throw new IllegalArgumentException(
624                         String.format("%s[%d] is out of range of [%d, %d] (too high)",
625                                 valueName, i, lower, upper));
626             }
627         }
628 
629         return value;
630     }
631 }
632