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