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