Preconditions.java revision 1bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5
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
17package com.google.android.mail.common.base;
18
19import 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 */
54public class 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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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
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   */
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   */
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
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   */
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
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   */
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