/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.wave.model.util; import com.google.common.annotations.VisibleForTesting; import javax.annotation.Nullable; /** * Utility functions for checking preconditions and throwing appropriate * Exceptions. * * This class mostly mimics com.google.common.base.Preconditions but only * implement the parts we need. * (we avoid external dependencies from model/) * TODO(tobiast): Add tests for this class */ public final class Preconditions { private Preconditions() {} public static void checkElementIndex(int index, int size) { assert size >= 0; if (index < 0) { throw new IndexOutOfBoundsException("index (" + index + ") must not be negative"); } if (index >= size) { throw new IndexOutOfBoundsException( "index (" + index + ") must be less than size (" + size + ")"); } } public static void checkPositionIndex(int index, int size) { assert size >= 0; if (index < 0) { throw new IndexOutOfBoundsException("index (" + index + ") must not be negative"); } if (index > size) { throw new IndexOutOfBoundsException( "index (" + index + ") must not be greater than size (" + size + ")"); } } public static void checkPositionIndexesInRange(int rangeStart, int start, int end, int rangeEnd) { assert rangeStart >= 0; assert rangeEnd >= rangeStart; if (start < rangeStart) { throw new IndexOutOfBoundsException("start index (" + start + ") " + "must not be less than start of range (" + rangeStart + ")"); } if (end < start) { throw new IndexOutOfBoundsException( "end index (" + end + ") must not be less than start index (" + start + ")"); } if (end > rangeEnd) { throw new IndexOutOfBoundsException( "end index (" + end + ") must not be greater than end of range (" + rangeEnd + ")"); } } public static void checkPositionIndexes(int start, int end, int size) { checkPositionIndexesInRange(0, start, end, size); } /** * Note a failed null check. * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessage error message * @throws NullPointerException */ public static void nullPointer(Object errorMessage) { throw new NullPointerException("" + errorMessage); } /** * Note a failed null check. * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. */ public static void nullPointer(String errorMessageTemplate, @Nullable Object... errorMessageArgs) { throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); } /** * Ensures that the specified object is not null. * * @param x the object to check * @param errorMessage error message * @return the object if it is not null * @throws NullPointerException if {@code x} is null */ public static <T> T checkNotNull(T x, Object errorMessage) { if (x == null) { throw new NullPointerException("" + errorMessage); } return x; } /** * Ensures that the specified object is not null. * * @param x the object to check * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @return the object if it is not null * @throws NullPointerException if {@code x} is null */ public static <T> T checkNotNull(T x, String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (x == null) { throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); } return x; } /** * Note an illegal argument * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessage error message * @throws IllegalArgumentException */ public static void illegalArgument(Object errorMessage) { throw new IllegalArgumentException("" + errorMessage); } /** * Note an illegal argument * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @throws IllegalArgumentException */ public static void illegalArgument(String errorMessageTemplate, @Nullable Object... errorMessageArgs) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); } /** * Ensures the truth of an condition involving the a parameter to the * calling method, but not involving the state of the calling instance. * * @param condition a boolean expression * @param errorMessage error message * @throws IllegalArgumentException if {@code condition} is false */ public static void checkArgument(boolean condition, Object errorMessage) { if (!condition) { throw new IllegalArgumentException("" + errorMessage); } } /** * Ensures the truth of an condition involving the a parameter to the * calling method, but not involving the state of the calling instance. * * @param condition a boolean expression * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @throws IllegalArgumentException if {@code condition} is false */ public static void checkArgument(boolean condition, String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (!condition) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); } } /** * Note an illegal state. * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessage error message * @throws IllegalStateException */ public static void illegalState(Object errorMessage) { throw new IllegalStateException("" + errorMessage); } /** * Note an illegal state. * * Useful when an explicit check was done separately, to avoid building up * the error message object or string unecessarily (if profiling reveals a * hotspot). * * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @throws IllegalStateException */ public static void illegalState(String errorMessageTemplate, @Nullable Object... errorMessageArgs) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); } /** * Ensures the truth of an condition involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param condition a boolean expression * @param errorMessage error message * @throws IllegalStateException if {@code condition} is false */ public static void checkState(boolean condition, Object errorMessage) { if (!condition) { throw new IllegalStateException("" + errorMessage); } } /** * Ensures the truth of an condition involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param condition a boolean expression * @param errorMessageTemplate a template for the exception message. * The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @throws IllegalStateException if {@code condition} is false */ public static void checkState(boolean condition, String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (!condition) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); } } /** * Substitutes each {@code %s} in {@code template} with an argument. These * are matched by position - the first {@code %s} gets {@code args[0]}, etc. * If there are more arguments than placeholders, the unmatched arguments will * be appended to the end of the formatted message in square braces. * * This method is copied verbatim from com.google.common.base.Preconditions. * * @param template a non-null string containing 0 or more {@code %s} * placeholders. * @param args the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. */ @VisibleForTesting static String format(String template, @Nullable Object... args) { template = String.valueOf(template); // null -> "null" // start substituting the arguments into the '%s' placeholders StringBuilder builder = new StringBuilder( template.length() + 16 * args.length); int templateStart = 0; int i = 0; while (i < args.length) { int placeholderStart = template.indexOf("%s", templateStart); if (placeholderStart == -1) { break; } builder.append(template.substring(templateStart, placeholderStart)); builder.append(args[i++]); templateStart = placeholderStart + 2; } builder.append(template.substring(templateStart)); // if we run out of placeholders, append the extra args in square braces if (i < args.length) { builder.append(" ["); builder.append(args[i++]); while (i < args.length) { builder.append(", "); builder.append(args[i++]); } builder.append("]"); } return builder.toString(); } }