/* * Copyright (C) 2015 The Android Open Source Project * * 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 com.android.tv.common; import android.content.Context; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import com.android.tv.common.feature.Feature; import com.android.tv.common.util.CommonUtils; /** * Simple static methods to be called at the start of your own methods to verify correct arguments * and state. * *

{@code checkXXX} methods throw exceptions when {@link BuildConfig#ENG} is true, and logs a * warning when it is false. * *

This is based on com.android.internal.util.Preconditions. */ public final class SoftPreconditions { private static final String TAG = "SoftPreconditions"; /** * Throws or logs if an expression involving the parameter of the calling method is not true. * * @param expression a boolean expression * @param tag Used to identify the source of a log message. It usually identifies the class or * activity where the log call occurs. * @param errorMessageTemplate a template for the exception message should the check fail. 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)}. * @return the evaluation result of the boolean expression * @throws IllegalArgumentException if {@code expression} is true */ public static boolean checkArgument( final boolean expression, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (!expression) { String msg = format(errorMessageTemplate, errorMessageArgs); warn(tag, "Illegal argument", new IllegalArgumentException(msg), msg); } return expression; } /** * Throws or logs if an expression involving the parameter of the calling method is not true. * * @param expression a boolean expression * @return the evaluation result of the boolean expression * @throws IllegalArgumentException if {@code expression} is true */ public static boolean checkArgument(final boolean expression) { checkArgument(expression, null, null); return expression; } /** * Throws or logs if an and object is null. * * @param reference an object reference * @param tag Used to identify the source of a log message. It usually identifies the class or * activity where the log call occurs. * @param errorMessageTemplate a template for the exception message should the check fail. 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)}. * @return true if the object is null * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull( final T reference, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (reference == null) { String msg = format(errorMessageTemplate, errorMessageArgs); warn(tag, "Null Pointer", new NullPointerException(msg), msg); } return reference; } /** * Throws or logs if an and object is null. * * @param reference an object reference * @return true if the object is null * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(final T reference) { return checkNotNull(reference, null, null); } /** * Throws or logs if an expression involving the state of the calling instance, but not * involving any parameters to the calling method is not true. * * @param expression a boolean expression * @param tag Used to identify the source of a log message. It usually identifies the class or * activity where the log call occurs. * @param errorMessageTemplate a template for the exception message should the check fail. 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)}. * @return the evaluation result of the boolean expression * @throws IllegalStateException if {@code expression} is true */ public static boolean checkState( final boolean expression, String tag, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { if (!expression) { String msg = format(errorMessageTemplate, errorMessageArgs); warn(tag, "Illegal State", new IllegalStateException(msg), msg); } return expression; } /** * Throws or logs if an expression involving the state of the calling instance, but not * involving any parameters to the calling method is not true. * * @param expression a boolean expression * @return the evaluation result of the boolean expression * @throws IllegalStateException if {@code expression} is true */ public static boolean checkState(final boolean expression) { checkState(expression, null, null); return expression; } /** * Throws or logs if the Feature is not enabled * * @param context an android context * @param feature the required feature * @param tag used to identify the source of a log message. It usually identifies the class or * activity where the log call occurs * @throws IllegalStateException if {@code feature} is not enabled */ public static void checkFeatureEnabled(Context context, Feature feature, String tag) { checkState(feature.isEnabled(context), tag, feature.toString()); } /** * Throws a {@link RuntimeException} if {@link BuildConfig#ENG} is true and not running in a * test, else log a warning. * * @param tag Used to identify the source of a log message. It usually identifies the class or * activity where the log call occurs. * @param e The exception to wrap with a RuntimeException when thrown. * @param msg The message to be logged */ public static void warn(String tag, String prefix, Exception e, String msg) throws RuntimeException { if (TextUtils.isEmpty(tag)) { tag = TAG; } String logMessage; if (TextUtils.isEmpty(msg)) { logMessage = prefix; } else if (TextUtils.isEmpty(prefix)) { logMessage = msg; } else { logMessage = prefix + ": " + msg; } if (BuildConfig.ENG && !CommonUtils.isRunningInTest()) { throw new RuntimeException(msg, e); } else { Log.w(tag, logMessage, e); } } /** * 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. * * @param template a string containing 0 or more {@code %s} placeholders. null is treated as * "null". * @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. */ static String format(@Nullable String template, @Nullable Object... args) { template = String.valueOf(template); // null -> "null" args = args == null ? new Object[] {"(Object[])null"} : args; // 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, templateStart, placeholderStart); builder.append(args[i++]); templateStart = placeholderStart + 2; } builder.append(template, templateStart, template.length()); // 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(); } private SoftPreconditions() {} }