001/* 002 * Copyright (C) 2007 Google Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled.common; 018 019import java.util.NoSuchElementException; 020 021/** 022 * Simple static methods to be called at the start of your own methods to verify 023 * correct arguments and state. This allows constructs such as 024 * <pre> 025 * if (count ≤ 0) { 026 * throw new IllegalArgumentException("must be positive: " + count); 027 * } 028 * </pre> 029 * to be replaced with the more compact 030 * <pre> 031 * checkArgument(count > 0, "must be positive: %s", count); 032 * </pre> 033 * Note that the sense of the expression is inverted; with {@code Preconditions} 034 * you declare what you expect to be <i>true</i>, just as you do with an 035 * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/assert.html"> 036 * {@code assert}</a> or a JUnit {@code assertTrue} call. 037 * 038 * <p><b>Warning:</b> only the {@code "%s"} specifier is recognized as a 039 * placeholder in these messages, not the full range of String.format(String, Object[]) specifiers. 040 * </p> 041 * <p>Take care not to confuse precondition checking with other similar types 042 * of checks! Precondition exceptions -- including those provided here, but also 043 * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link 044 * UnsupportedOperationException} and others -- are used to signal that the 045 * <i>calling method</i> has made an error. This tells the caller that it should 046 * not have invoked the method when it did, with the arguments it did, or 047 * perhaps ever. Postcondition or other invariant failures should not throw 048 * these types of exceptions.</p> 049 * 050 * @author Kevin Bourrillion 051 */ 052public final class Preconditions { 053 private Preconditions() {} 054 055 /** 056 * Ensures the truth of an expression involving one or more parameters to the 057 * calling method. 058 * 059 * @param expression a boolean expression 060 * @throws IllegalArgumentException if {@code expression} is false 061 */ 062 public static void checkArgument(boolean expression) { 063 if (!expression) { 064 throw new IllegalArgumentException(); 065 } 066 } 067 068 /** 069 * Ensures the truth of an expression involving one or more parameters to the 070 * calling method. 071 * 072 * @param expression a boolean expression 073 * @param errorMessage the exception message to use if the check fails; will 074 * be converted to a string using {@link String#valueOf(Object)} 075 * @throws IllegalArgumentException if {@code expression} is false 076 */ 077 public static void checkArgument(boolean expression, Object errorMessage) { 078 if (!expression) { 079 throw new IllegalArgumentException(String.valueOf(errorMessage)); 080 } 081 } 082 083 /** 084 * Ensures the truth of an expression involving one or more parameters to the 085 * calling method. 086 * 087 * @param expression a boolean expression 088 * @param errorMessageTemplate a template for the exception message should the 089 * check fail. The message is formed by replacing each {@code %s} 090 * placeholder in the template with an argument. These are matched by 091 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 092 * Unmatched arguments will be appended to the formatted message in square 093 * braces. Unmatched placeholders will be left as-is. 094 * @param errorMessageArgs the arguments to be substituted into the message 095 * template. Arguments are converted to strings using 096 * {@link String#valueOf(Object)}. 097 * @throws IllegalArgumentException if {@code expression} is false 098 * @throws NullPointerException if the check fails and either {@code 099 * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let 100 * this happen) 101 */ 102 public static void checkArgument(boolean expression, 103 String errorMessageTemplate, Object... errorMessageArgs) { 104 if (!expression) { 105 throw new IllegalArgumentException( 106 format(errorMessageTemplate, errorMessageArgs)); 107 } 108 } 109 110 /** 111 * Ensures the truth of an expression involving the state of the calling 112 * instance, but not involving any parameters to the calling method. 113 * 114 * @param expression a boolean expression 115 * @throws IllegalStateException if {@code expression} is false 116 */ 117 public static void checkState(boolean expression) { 118 if (!expression) { 119 throw new IllegalStateException(); 120 } 121 } 122 123 /** 124 * Ensures the truth of an expression involving the state of the calling 125 * instance, but not involving any parameters to the calling method. 126 * 127 * @param expression a boolean expression 128 * @param errorMessage the exception message to use if the check fails; will 129 * be converted to a string using {@link String#valueOf(Object)} 130 * @throws IllegalStateException if {@code expression} is false 131 */ 132 public static void checkState(boolean expression, Object errorMessage) { 133 if (!expression) { 134 throw new IllegalStateException(String.valueOf(errorMessage)); 135 } 136 } 137 138 /** 139 * Ensures the truth of an expression involving the state of the calling 140 * instance, but not involving any parameters to the calling method. 141 * 142 * @param expression a boolean expression 143 * @param errorMessageTemplate a template for the exception message should the 144 * check fail. The message is formed by replacing each {@code %s} 145 * placeholder in the template with an argument. These are matched by 146 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 147 * Unmatched arguments will be appended to the formatted message in square 148 * braces. Unmatched placeholders will be left as-is. 149 * @param errorMessageArgs the arguments to be substituted into the message 150 * template. Arguments are converted to strings using 151 * {@link String#valueOf(Object)}. 152 * @throws IllegalStateException if {@code expression} is false 153 * @throws NullPointerException if the check fails and either {@code 154 * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let 155 * this happen) 156 */ 157 public static void checkState(boolean expression, 158 String errorMessageTemplate, Object... errorMessageArgs) { 159 if (!expression) { 160 throw new IllegalStateException( 161 format(errorMessageTemplate, errorMessageArgs)); 162 } 163 } 164 165 /** 166 * Ensures that an object reference passed as a parameter to the calling 167 * method is not null. 168 * 169 * @param reference an object reference 170 * @return the non-null reference that was validated 171 * @throws NullPointerException if {@code reference} is null 172 */ 173 public static <T> T checkNotNull(T reference) { 174 if (reference == null) { 175 throw new NullPointerException(); 176 } 177 return reference; 178 } 179 180 /** 181 * Ensures that an object reference passed as a parameter to the calling 182 * method is not null. 183 * 184 * @param reference an object reference 185 * @param errorMessage the exception message to use if the check fails; will 186 * be converted to a string using {@link String#valueOf(Object)} 187 * @return the non-null reference that was validated 188 * @throws NullPointerException if {@code reference} is null 189 */ 190 public static <T> T checkNotNull(T reference, Object errorMessage) { 191 if (reference == null) { 192 throw new NullPointerException(String.valueOf(errorMessage)); 193 } 194 return reference; 195 } 196 197 /** 198 * Ensures that an object reference passed as a parameter to the calling 199 * method is not null. 200 * 201 * @param reference an object reference 202 * @param parameterName the parameter name 203 * @return the non-null reference that was validated 204 * @throws NullPointerException if {@code reference} is null 205 */ 206 public static <T> T checkArgNotNull(T reference, String parameterName) { 207 if (reference == null) { 208 throw new NullPointerException(format("Argument '%s' must not be null", parameterName)); 209 } 210 return reference; 211 } 212 213 /** 214 * Ensures that an object reference passed as a parameter to the calling 215 * method is not null. 216 * 217 * @param reference an object reference 218 * @param errorMessageTemplate a template for the exception message should the 219 * check fail. The message is formed by replacing each {@code %s} 220 * placeholder in the template with an argument. These are matched by 221 * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. 222 * Unmatched arguments will be appended to the formatted message in square 223 * braces. Unmatched placeholders will be left as-is. 224 * @param errorMessageArgs the arguments to be substituted into the message 225 * template. Arguments are converted to strings using 226 * {@link String#valueOf(Object)}. 227 * @return the non-null reference that was validated 228 * @throws NullPointerException if {@code reference} is null 229 */ 230 public static <T> T checkNotNull(T reference, String errorMessageTemplate, 231 Object... errorMessageArgs) { 232 if (reference == null) { 233 // If either of these parameters is null, the right thing happens anyway 234 throw new NullPointerException( 235 format(errorMessageTemplate, errorMessageArgs)); 236 } 237 return reference; 238 } 239 240 /** 241 * Ensures that {@code index} specifies a valid <i>element</i> in an array, 242 * list or string of size {@code size}. An element index may range from zero, 243 * inclusive, to {@code size}, exclusive. 244 * 245 * @param index a user-supplied index identifying an element of an array, list 246 * or string 247 * @param size the size of that array, list or string 248 * @return the value of {@code index} 249 * @throws IndexOutOfBoundsException if {@code index} is negative or is not 250 * less than {@code size} 251 * @throws IllegalArgumentException if {@code size} is negative 252 */ 253 public static int checkElementIndex(int index, int size) { 254 return checkElementIndex(index, size, "index"); 255 } 256 257 /** 258 * Ensures that {@code index} specifies a valid <i>element</i> in an array, 259 * list or string of size {@code size}. An element index may range from zero, 260 * inclusive, to {@code size}, exclusive. 261 * 262 * @param index a user-supplied index identifying an element of an array, list 263 * or string 264 * @param size the size of that array, list or string 265 * @param desc the text to use to describe this index in an error message 266 * @return the value of {@code index} 267 * @throws IndexOutOfBoundsException if {@code index} is negative or is not 268 * less than {@code size} 269 * @throws IllegalArgumentException if {@code size} is negative 270 */ 271 public static int checkElementIndex(int index, int size, String desc) { 272 // Carefully optimized for execution by hotspot (explanatory comment above) 273 if (index < 0 || index >= size) { 274 throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); 275 } 276 return index; 277 } 278 279 private static String badElementIndex(int index, int size, String desc) { 280 if (index < 0) { 281 return format("%s (%s) must not be negative", desc, index); 282 } else if (size < 0) { 283 throw new IllegalArgumentException("negative size: " + size); 284 } else { // index >= size 285 return format("%s (%s) must be less than size (%s)", desc, index, size); 286 } 287 } 288 289 /** 290 * Ensures that {@code index} specifies a valid <i>position</i> in an array, 291 * list or string of size {@code size}. A position index may range from zero 292 * to {@code size}, inclusive. 293 * 294 * @param index a user-supplied index identifying a position in an array, list 295 * or string 296 * @param size the size of that array, list or string 297 * @return the value of {@code index} 298 * @throws IndexOutOfBoundsException if {@code index} is negative or is 299 * greater than {@code size} 300 * @throws IllegalArgumentException if {@code size} is negative 301 */ 302 public static int checkPositionIndex(int index, int size) { 303 return checkPositionIndex(index, size, "index"); 304 } 305 306 /** 307 * Ensures that {@code index} specifies a valid <i>position</i> in an array, 308 * list or string of size {@code size}. A position index may range from zero 309 * to {@code size}, inclusive. 310 * 311 * @param index a user-supplied index identifying a position in an array, list 312 * or string 313 * @param size the size of that array, list or string 314 * @param desc the text to use to describe this index in an error message 315 * @return the value of {@code index} 316 * @throws IndexOutOfBoundsException if {@code index} is negative or is 317 * greater than {@code size} 318 * @throws IllegalArgumentException if {@code size} is negative 319 */ 320 public static int checkPositionIndex(int index, int size, String desc) { 321 // Carefully optimized for execution by hotspot (explanatory comment above) 322 if (index < 0 || index > size) { 323 throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); 324 } 325 return index; 326 } 327 328 private static String badPositionIndex(int index, int size, String desc) { 329 if (index < 0) { 330 return format("%s (%s) must not be negative", desc, index); 331 } else if (size < 0) { 332 throw new IllegalArgumentException("negative size: " + size); 333 } else { // index > size 334 return format("%s (%s) must not be greater than size (%s)", 335 desc, index, size); 336 } 337 } 338 339 /** 340 * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> 341 * in an array, list or string of size {@code size}, and are in order. A 342 * position index may range from zero to {@code size}, inclusive. 343 * 344 * @param start a user-supplied index identifying a starting position in an 345 * array, list or string 346 * @param end a user-supplied index identifying a ending position in an array, 347 * list or string 348 * @param size the size of that array, list or string 349 * @throws IndexOutOfBoundsException if either index is negative or is 350 * greater than {@code size}, or if {@code end} is less than {@code start} 351 * @throws IllegalArgumentException if {@code size} is negative 352 */ 353 public static void checkPositionIndexes(int start, int end, int size) { 354 // Carefully optimized for execution by hotspot (explanatory comment above) 355 if (start < 0 || end < start || end > size) { 356 throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); 357 } 358 } 359 360 private static String badPositionIndexes(int start, int end, int size) { 361 if (start < 0 || start > size) { 362 return badPositionIndex(start, size, "start index"); 363 } 364 if (end < 0 || end > size) { 365 return badPositionIndex(end, size, "end index"); 366 } 367 // end < start 368 return format("end index (%s) must not be less than start index (%s)", 369 end, start); 370 } 371 372 /** 373 * Substitutes each {@code %s} in {@code template} with an argument. These 374 * are matched by position - the first {@code %s} gets {@code args[0]}, etc. 375 * If there are more arguments than placeholders, the unmatched arguments will 376 * be appended to the end of the formatted message in square braces. 377 * 378 * @param template a non-null string containing 0 or more {@code %s} 379 * placeholders. 380 * @param args the arguments to be substituted into the message 381 * template. Arguments are converted to strings using 382 * {@link String#valueOf(Object)}. Arguments can be null. 383 * @return String 384 */ 385 static String format(String template, Object... args) { 386 // start substituting the arguments into the '%s' placeholders 387 StringBuilder builder = new StringBuilder( 388 template.length() + 16 * args.length); 389 int templateStart = 0; 390 int i = 0; 391 while (i < args.length) { 392 int placeholderStart = template.indexOf("%s", templateStart); 393 if (placeholderStart == -1) { 394 break; 395 } 396 builder.append(template.substring(templateStart, placeholderStart)); 397 builder.append(args[i++]); 398 templateStart = placeholderStart + 2; 399 } 400 builder.append(template.substring(templateStart)); 401 402 // if we run out of placeholders, append the extra args in square braces 403 if (i < args.length) { 404 builder.append(" ["); 405 builder.append(args[i++]); 406 while (i < args.length) { 407 builder.append(", "); 408 builder.append(args[i++]); 409 } 410 builder.append("]"); 411 } 412 413 return builder.toString(); 414 } 415}