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 &le; 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}