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