1 /* 2 * Copyright (C) 2007 Google Inc. 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.google.android.mail.common.base; 18 19 import java.util.NoSuchElementException; 20 21 /** 22 * Simple static methods to be called at the start of your own methods to verify 23 * correct arguments and state. This allows constructs such as 24 * <pre> 25 * if (count <= 0) { 26 * throw new IllegalArgumentException("must be positive: " + count); 27 * }</pre> 28 * 29 * to be replaced with the more compact 30 * <pre> 31 * checkArgument(count > 0, "must be positive: %s", count);</pre> 32 * 33 * Note that the sense of the expression is inverted; with {@code Preconditions} 34 * you declare what you expect to be <i>true</i>, just as you do with an 35 * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/assert.html"> 36 * {@code assert}</a> or a JUnit {@code assertTrue} call. 37 * 38 * <p><b>Warning:</b> only the {@code "%s"} specifier is recognized as a 39 * placeholder in these messages, not the full range of {@link 40 * String#format(String, Object[])} specifiers. 41 * 42 * <p>Take care not to confuse precondition checking with other similar types 43 * of checks! Precondition exceptions -- including those provided here, but also 44 * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link 45 * UnsupportedOperationException} and others -- are used to signal that the 46 * <i>calling method</i> has made an error. This tells the caller that it should 47 * not have invoked the method when it did, with the arguments it did, or 48 * perhaps ever. Postcondition or other invariant failures should not throw 49 * these types of exceptions. 50 * 51 * @author Kevin Bourrillion 52 * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library) 53 */ 54 public class Preconditions { Preconditions()55 private Preconditions() {} 56 57 /** 58 * Ensures the truth of an expression involving one or more parameters to the 59 * calling method. 60 * 61 * @param expression a boolean expression 62 * @throws IllegalArgumentException if {@code expression} is false 63 */ checkArgument(boolean expression)64 public static void checkArgument(boolean expression) { 65 if (!expression) { 66 throw new IllegalArgumentException(); 67 } 68 } 69 70 /** 71 * Ensures the truth of an expression involving one or more parameters to the 72 * calling method. 73 * 74 * @param expression a boolean expression 75 * @param errorMessage the exception message to use if the check fails; will 76 * be converted to a string using {@link String#valueOf(Object)} 77 * @throws IllegalArgumentException if {@code expression} is false 78 */ checkArgument(boolean expression, Object errorMessage)79 public static void checkArgument(boolean expression, Object errorMessage) { 80 if (!expression) { 81 throw new IllegalArgumentException(String.valueOf(errorMessage)); 82 } 83 } 84 85 /** 86 * Ensures the truth of an expression involving one or more parameters to the 87 * calling method. 88 * 89 * @param expression a boolean expression 90 * @param errorMessageTemplate a template for the exception message should the 91 * check fail. The message is formed by replacing each {@code %s} 92 * placeholder in the template with an argument. These are matched by 93 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 94 * Unmatched arguments will be appended to the formatted message in square 95 * braces. Unmatched placeholders will be left as-is. 96 * @param errorMessageArgs the arguments to be substituted into the message 97 * template. Arguments are converted to strings using 98 * {@link String#valueOf(Object)}. 99 * @throws IllegalArgumentException if {@code expression} is false 100 * @throws NullPointerException if the check fails and either {@code 101 * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let 102 * this happen) 103 */ checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs)104 public static void checkArgument(boolean expression, 105 String errorMessageTemplate, Object... errorMessageArgs) { 106 if (!expression) { 107 throw new IllegalArgumentException( 108 format(errorMessageTemplate, errorMessageArgs)); 109 } 110 } 111 112 /** 113 * Ensures the truth of an expression involving the state of the calling 114 * instance, but not involving any parameters to the calling method. 115 * 116 * @param expression a boolean expression 117 * @throws IllegalStateException if {@code expression} is false 118 */ checkState(boolean expression)119 public static void checkState(boolean expression) { 120 if (!expression) { 121 throw new IllegalStateException(); 122 } 123 } 124 125 /** 126 * Ensures the truth of an expression involving the state of the calling 127 * instance, but not involving any parameters to the calling method. 128 * 129 * @param expression a boolean expression 130 * @param errorMessage the exception message to use if the check fails; will 131 * be converted to a string using {@link String#valueOf(Object)} 132 * @throws IllegalStateException if {@code expression} is false 133 */ checkState(boolean expression, Object errorMessage)134 public static void checkState(boolean expression, Object errorMessage) { 135 if (!expression) { 136 throw new IllegalStateException(String.valueOf(errorMessage)); 137 } 138 } 139 140 /** 141 * Ensures the truth of an expression involving the state of the calling 142 * instance, but not involving any parameters to the calling method. 143 * 144 * @param expression a boolean expression 145 * @param errorMessageTemplate a template for the exception message should the 146 * check fail. The message is formed by replacing each {@code %s} 147 * placeholder in the template with an argument. These are matched by 148 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 149 * Unmatched arguments will be appended to the formatted message in square 150 * braces. Unmatched placeholders will be left as-is. 151 * @param errorMessageArgs the arguments to be substituted into the message 152 * template. Arguments are converted to strings using 153 * {@link String#valueOf(Object)}. 154 * @throws IllegalStateException if {@code expression} is false 155 * @throws NullPointerException if the check fails and either {@code 156 * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let 157 * this happen) 158 */ checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs)159 public static void checkState(boolean expression, 160 String errorMessageTemplate, Object... errorMessageArgs) { 161 if (!expression) { 162 throw new IllegalStateException( 163 format(errorMessageTemplate, errorMessageArgs)); 164 } 165 } 166 167 /** 168 * Ensures that an object reference passed as a parameter to the calling 169 * method is not null. 170 * 171 * @param reference an object reference 172 * @return the non-null reference that was validated 173 * @throws NullPointerException if {@code reference} is null 174 */ checkNotNull(T reference)175 public static <T> T checkNotNull(T reference) { 176 if (reference == null) { 177 throw new NullPointerException(); 178 } 179 return reference; 180 } 181 182 /** 183 * Ensures that an object reference passed as a parameter to the calling 184 * method is not null. 185 * 186 * @param reference an object reference 187 * @param errorMessage the exception message to use if the check fails; will 188 * be converted to a string using {@link String#valueOf(Object)} 189 * @return the non-null reference that was validated 190 * @throws NullPointerException if {@code reference} is null 191 */ checkNotNull(T reference, Object errorMessage)192 public static <T> T checkNotNull(T reference, Object errorMessage) { 193 if (reference == null) { 194 throw new NullPointerException(String.valueOf(errorMessage)); 195 } 196 return reference; 197 } 198 199 /** 200 * Ensures that an object reference passed as a parameter to the calling 201 * method is not null. 202 * 203 * @param reference an object reference 204 * @param errorMessageTemplate a template for the exception message should the 205 * check fail. The message is formed by replacing each {@code %s} 206 * placeholder in the template with an argument. These are matched by 207 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 208 * Unmatched arguments will be appended to the formatted message in square 209 * braces. Unmatched placeholders will be left as-is. 210 * @param errorMessageArgs the arguments to be substituted into the message 211 * template. Arguments are converted to strings using 212 * {@link String#valueOf(Object)}. 213 * @return the non-null reference that was validated 214 * @throws NullPointerException if {@code reference} is null 215 */ checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs)216 public static <T> T checkNotNull(T reference, String errorMessageTemplate, 217 Object... errorMessageArgs) { 218 if (reference == null) { 219 // If either of these parameters is null, the right thing happens anyway 220 throw new NullPointerException( 221 format(errorMessageTemplate, errorMessageArgs)); 222 } 223 return reference; 224 } 225 226 /* 227 * All recent hotspots (as of 2009) *really* like to have the natural code 228 * 229 * if (guardExpression) { 230 * throw new BadException(messageExpression); 231 * } 232 * 233 * refactored so that messageExpression is moved to a separate 234 * String-returning method. 235 * 236 * if (guardExpression) { 237 * throw new BadException(badMsg(...)); 238 * } 239 * 240 * The alternative natural refactorings into void or Exception-returning 241 * methods are much slower. This is a big deal - we're talking factors of 242 * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer 243 * bug, which should be fixed, but that's a separate, big project). 244 * 245 * The coding pattern above is heavily used in java.util, e.g. in ArrayList. 246 * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. 247 * 248 * But the methods in this class want to throw different exceptions, 249 * depending on the args, so it appears that this pattern is not directly 250 * applicable. But we can use the ridiculous, devious trick of throwing an 251 * exception in the middle of the construction of another exception. 252 * Hotspot is fine with that. 253 */ 254 255 /** 256 * Ensures that {@code index} specifies a valid <i>element</i> in an array, 257 * list or string of size {@code size}. An element index may range from zero, 258 * inclusive, to {@code size}, exclusive. 259 * 260 * @param index a user-supplied index identifying an element of an array, list 261 * or string 262 * @param size the size of that array, list or string 263 * @return the value of {@code index} 264 * @throws IndexOutOfBoundsException if {@code index} is negative or is not 265 * less than {@code size} 266 * @throws IllegalArgumentException if {@code size} is negative 267 */ checkElementIndex(int index, int size)268 public static int checkElementIndex(int index, int size) { 269 return checkElementIndex(index, size, "index"); 270 } 271 272 /** 273 * Ensures that {@code index} specifies a valid <i>element</i> in an array, 274 * list or string of size {@code size}. An element index may range from zero, 275 * inclusive, to {@code size}, exclusive. 276 * 277 * @param index a user-supplied index identifying an element of an array, list 278 * or string 279 * @param size the size of that array, list or string 280 * @param desc the text to use to describe this index in an error message 281 * @return the value of {@code index} 282 * @throws IndexOutOfBoundsException if {@code index} is negative or is not 283 * less than {@code size} 284 * @throws IllegalArgumentException if {@code size} is negative 285 */ checkElementIndex(int index, int size, String desc)286 public static int checkElementIndex(int index, int size, String desc) { 287 // Carefully optimized for execution by hotspot (explanatory comment above) 288 if (index < 0 || index >= size) { 289 throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); 290 } 291 return index; 292 } 293 badElementIndex(int index, int size, String desc)294 private static String badElementIndex(int index, int size, String desc) { 295 if (index < 0) { 296 return format("%s (%s) must not be negative", desc, index); 297 } else if (size < 0) { 298 throw new IllegalArgumentException("negative size: " + size); 299 } else { // index >= size 300 return format("%s (%s) must be less than size (%s)", desc, index, size); 301 } 302 } 303 304 /** 305 * Ensures that {@code index} specifies a valid <i>position</i> in an array, 306 * list or string of size {@code size}. A position index may range from zero 307 * to {@code size}, inclusive. 308 * 309 * @param index a user-supplied index identifying a position in an array, list 310 * or string 311 * @param size the size of that array, list or string 312 * @return the value of {@code index} 313 * @throws IndexOutOfBoundsException if {@code index} is negative or is 314 * greater than {@code size} 315 * @throws IllegalArgumentException if {@code size} is negative 316 */ checkPositionIndex(int index, int size)317 public static int checkPositionIndex(int index, int size) { 318 return checkPositionIndex(index, size, "index"); 319 } 320 321 /** 322 * Ensures that {@code index} specifies a valid <i>position</i> in an array, 323 * list or string of size {@code size}. A position index may range from zero 324 * to {@code size}, inclusive. 325 * 326 * @param index a user-supplied index identifying a position in an array, list 327 * or string 328 * @param size the size of that array, list or string 329 * @param desc the text to use to describe this index in an error message 330 * @return the value of {@code index} 331 * @throws IndexOutOfBoundsException if {@code index} is negative or is 332 * greater than {@code size} 333 * @throws IllegalArgumentException if {@code size} is negative 334 */ checkPositionIndex(int index, int size, String desc)335 public static int checkPositionIndex(int index, int size, String desc) { 336 // Carefully optimized for execution by hotspot (explanatory comment above) 337 if (index < 0 || index > size) { 338 throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); 339 } 340 return index; 341 } 342 badPositionIndex(int index, int size, String desc)343 private static String badPositionIndex(int index, int size, String desc) { 344 if (index < 0) { 345 return format("%s (%s) must not be negative", desc, index); 346 } else if (size < 0) { 347 throw new IllegalArgumentException("negative size: " + size); 348 } else { // index > size 349 return format("%s (%s) must not be greater than size (%s)", 350 desc, index, size); 351 } 352 } 353 354 /** 355 * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> 356 * in an array, list or string of size {@code size}, and are in order. A 357 * position index may range from zero to {@code size}, inclusive. 358 * 359 * @param start a user-supplied index identifying a starting position in an 360 * array, list or string 361 * @param end a user-supplied index identifying a ending position in an array, 362 * list or string 363 * @param size the size of that array, list or string 364 * @throws IndexOutOfBoundsException if either index is negative or is 365 * greater than {@code size}, or if {@code end} is less than {@code start} 366 * @throws IllegalArgumentException if {@code size} is negative 367 */ checkPositionIndexes(int start, int end, int size)368 public static void checkPositionIndexes(int start, int end, int size) { 369 // Carefully optimized for execution by hotspot (explanatory comment above) 370 if (start < 0 || end < start || end > size) { 371 throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); 372 } 373 } 374 badPositionIndexes(int start, int end, int size)375 private static String badPositionIndexes(int start, int end, int size) { 376 if (start < 0 || start > size) { 377 return badPositionIndex(start, size, "start index"); 378 } 379 if (end < 0 || end > size) { 380 return badPositionIndex(end, size, "end index"); 381 } 382 // end < start 383 return format("end index (%s) must not be less than start index (%s)", 384 end, start); 385 } 386 387 /** 388 * Substitutes each {@code %s} in {@code template} with an argument. These 389 * are matched by position - the first {@code %s} gets {@code args[0]}, etc. 390 * If there are more arguments than placeholders, the unmatched arguments will 391 * be appended to the end of the formatted message in square braces. 392 * 393 * @param template a non-null string containing 0 or more {@code %s} 394 * placeholders. 395 * @param args the arguments to be substituted into the message 396 * template. Arguments are converted to strings using 397 * {@link String#valueOf(Object)}. Arguments can be null. 398 */ format(String template, Object... args)399 static String format(String template, Object... args) { 400 // start substituting the arguments into the '%s' placeholders 401 StringBuilder builder = new StringBuilder( 402 template.length() + 16 * args.length); 403 int templateStart = 0; 404 int i = 0; 405 while (i < args.length) { 406 int placeholderStart = template.indexOf("%s", templateStart); 407 if (placeholderStart == -1) { 408 break; 409 } 410 builder.append(template.substring(templateStart, placeholderStart)); 411 builder.append(args[i++]); 412 templateStart = placeholderStart + 2; 413 } 414 builder.append(template.substring(templateStart)); 415 416 // if we run out of placeholders, append the extra args in square braces 417 if (i < args.length) { 418 builder.append(" ["); 419 builder.append(args[i++]); 420 while (i < args.length) { 421 builder.append(", "); 422 builder.append(args[i++]); 423 } 424 builder.append("]"); 425 } 426 427 return builder.toString(); 428 } 429 } 430