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